Recently I had the need to create a component in .NET that would be callable as a COM component (not unlike many other such objects in the past). This one, however, was peculiar in that I also needed to be able to call it from a web page running in IE (due to constraints already on the application, cross-browser support is not a concern in this case). I didn't want the users to be burdened with dialog boxes or even require that they change their security settings. I wanted to create the object as 'safe for scripting'.
There are two approaches that I know of that address the issue: implementing IObjectSafety (a COM interface) or directly managing the object via the Component Category Manager (ICatRegister COM interface) and registering the CAT_SafeForScripting GUID.
The first presumes that I have access to and can compile the code, because the IObjectSafety interface must be implemented directly on the object in question. The second solution can be done after the fact (e.g., within an installer).
Due to the nature of the project, I opted for the first option because, frankly, it was the easier of the two to implement given my time constraints.
The IObjectSafety interface is not native to the .NET world. As such, in order to implement a COM interface on a .NET object the interface must be defined within the managed code and then decorated with the appropriate attributes.
Here's my implementation of the IObjectSafety interface in managed code:
[ComImport()]
[Guid("CB5BDC81-93C1-11CF-8F20-00805F2CD064")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IObjectSafety {
[PreserveSig()]
int GetInterfaceSafetyOptions(ref Guid riid, out int pdwSupportedOptions, out int pdwEnabledOptions);
[PreserveSig()]
int SetInterfaceSafetyOptions(ref Guid riid, int dwOptionSetMask, int dwEnabledOptions);
}
The ComImportAttribute identifies that the interface was previously defined in COM with the IID specified by the GuidAttribute. The PreserveSigAttribute is important on each of the methods to tell the .NET runtime to not suppress the HRESULT when the method is called.
Once defined, it's then a simple matter to implement the interface on my class, and indicate that it's safe for scripting:
[ClassInterface(ClassInterfaceType.AutoDispatch)]
[Guid("43B6ADAA-6DE7-43c2-9206-3389C94B9531")]
[ProgId("DemoLib.DemoObject")]
public sealed class DemoObject : IObjectSafety {
private const int INTERFACESAFE_FOR_UNTRUSTED_CALLER = 0x00000001;
private const int INTERFACESAFE_FOR_UNTRUSTED_DATA = 0x00000002;
private const int S_OK = 0;
public int GetInterfaceSafetyOptions(ref Guid riid, out int pdwSupportedOptions, out int pdwEnabledOptions) {
pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA;
pdwEnabledOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA;
return S_OK;
}
public int SetInterfaceSafetyOptions(ref Guid riid, int dwOptionSetMask, int dwEnabledOptions) {
return S_OK;
}
}
Once registered (via Regasm.exe), you can invoke methods on an instance of the object via your script:
var obj = new ActiveXObject("DemoLib.DemoObject");
obj.CallSomeMethod();