There are two ways to make a COM object as safe for scripting and initializing--either through a component category entry in the registry, or by supporting the IObjectSafety interface.
A control can be marked as safe for scripting or initializing by adding it to the CATID_SafeForScripting and CATID_SafeForInitializing component categories in the registry, respectively. This should be done using the component category manager, which already a standard part of IE3.
Use the following code to mark your control as SafeForScripting:
hr = CreateComponentCategory(CATID_SafeForScripting, L"Controls that are safely scriptable"); hr = RegisterCLSIDInCategory(CLSID_SafeItem, CATID_SafeForScripting);
or this code to mark your control as SafeForInitializing:
hr = CreateComponentCategory(CATID_SafeForInitializing, L"Controls safely initializable from persistent data"); hr = RegisterCLSIDInCategory(CLSID_SafeItem, CATID_SafeForInitializing);
Both of these snippets use the helper functions below, and assume that CLSID_SafeItem contains the CLSID of the control, while CATID_SafeForInitializing and CATID_SafeForScripting are defined in objsafe.h.
This function can be used to create a component category and associated description.
#include "comcat.h" HRESULT CreateComponentCategory(CATID catid, WCHAR* catDescription) { ICatRegister* pcr = NULL ; HRESULT hr = S_OK ; hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr, NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr); if (FAILED(hr)) return hr; // Make sure the HKCR\Component Categories\{..catid...} // key is registered CATEGORYINFO catinfo; catinfo.catid = catid; catinfo.lcid = 0x0409 ; // english // Make sure the provided description is not too long. // Only copy the first 127 characters if it is int len = wcslen(catDescription); if (len>127) len = 127; wcsncpy(catinfo.szDescription, catDescription, len); // Make sure the description is null terminated catinfo.szDescription[len] = '\0'; hr = pcr->RegisterCategories(1, &catinfo); pcr->Release(); return hr; }
This function can be used to register a CLSID as belonging to a component category.
#include "comcat.h" HRESULT RegisterCLSIDInCategory(REFCLSID clsid, CATID catid) { // Register your component categories information. ICatRegister* pcr = NULL ; HRESULT hr = S_OK ; hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr, NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr); if (SUCCEEDED(hr)) { // Register this category as being "implemented" by // the class. CATID rgcatid[1] ; rgcatid[0] = catid; hr = pcr->RegisterClassImplCategories(clsid, 1, rgcatid); } if (pcr != NULL) pcr->Release(); return hr; }
This function can be used to unregister a CLSID as belonging to a component category.
#include "comcat.h" HRESULT UnRegisterCLSIDInCategory(REFCLSID clsid, CATID catid) { ICatRegister* pcr = NULL ; HRESULT hr = S_OK ; hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr, NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr); if (SUCCEEDED(hr)) { // Unregister this category as being "implemented" by // the class. CATID rgcatid[1] ; rgcatid[0] = catid; hr = pcr->UnRegisterClassImplCategories(clsid, 1, rgcatid); } if (pcr != NULL) pcr->Release(); return hr; }
Calls to these snippets should be inserted in your DLL registration and unregistration routines.
The IObjectSafety interface is an interface that allows an object user to ask the object to make itself safe, or to ask the current safety of the object, with regards to various capabilities on the object. The current defined capabilites are INTERFACESAFE_FOR_UNTRUSTED_CALLER and INTERFACESAFE_FOR_UNTRUSTED_DATA, corresponding the "Safe for Scripting" and "Safe for Initializing", but the design allows us to later define other capabilities.
The interface is as follows.
// Option bit definitions for IObjectSafety: #define INTERFACESAFE_FOR_UNTRUSTED_CALLER 0x00000001 // Caller of interface may be untrusted #define INTERFACESAFE_FOR_UNTRUSTED_DATA 0x00000002 // Data passed into interface may be untrusted // {CB5BDC81-93C1-11cf-8F20-00805F2CD064} DEFINE_GUID(IID_IObjectSafety, 0xcb5bdc81, 0x93c1, 0x11cf, 0x8f, 0x20, 0x0, 0x80, 0x5f, 0x2c, 0xd0, 0x64); interface IObjectSafety : public IUnknown { public: virtual HRESULT __stdcall GetInterfaceSafetyOptions( /* [in] */ REFIID riid, /* [out] */ DWORD __RPC_FAR *pdwSupportedOptions, /* [out] */ DWORD __RPC_FAR *pdwEnabledOptions) = 0; virtual HRESULT __stdcall SetInterfaceSafetyOptions( /* [in] */ REFIID riid, /* [in] */ DWORD dwOptionSetMask, /* [in] */ DWORD dwEnabledOptions) = 0; };
HRESULT SetInterfaceSafetyOptions( /* [in] */ REFIID riid, /* [in] */ DWORD dwOptionSetMask, /* [in] */ DWORD dwEnabledOptions);
The SetInterfaceSafetyOptions function makes an object safe for initialization or scripting.
Value | Meaning |
INTERFACESAFE_FOR_UNTRUSTED_CALLER | Specifies that the interface identified by riid should be made safe for scripting. |
INTERFACESAFE_FOR_UNTRUSTED_DATA | Specifies that the interface identified by riid should be made safe for untrusted data during initialization. |
Typically, a control client would call SetInterfaceSafetyOptions before attempting to script or initialize the object, to attempt to require that the object is safe for the desired action. If SetInterfaceSafetyOptions returns failure, it client will display UI to the user to confirm whether the action should be allowed.
SetInterfaceOptions takes an interface (either IDispatch for scripting or IPersist* for initialization), a bitmask of options to change (dwOptionSetMask) and the new settings for those options (dwEnabledOptions). The currently supported capabilities are, as described above, INTERFACESAFE_FOR_UNTRUSTED_CALLER and INTERFACESAFE_FOR_UNTRUSTED_DATA.
HRESULT GetInterfaceSafetyOptions( /* [in] */ REFIID riid, /* [out] */ DWORD __RPC_FAR *pdwSupportedOptions, /* [out] */ DWORD __RPC_FAR *pdwEnabledOptions);
The GetInterfaceSafetyOptions function retrieves the safety options supported by an object as well as the safety options that are currently set for that object.
GetInterfaceSafetyOptions returns a set of bits in pdwSupportedOptions for each capability that the control knows about, and a set of bits in pdwEnabledOptions for each capability for which the control is currently safe.
For example, my control might say that it knows about INTERFACESAFE_FOR_UNTRUSTED_DATA and INTERFACESAFE_FOR_UNTRUSTED_CALLER, and is currently safe for just INTERFACESAFE_FOR_UNTRUSTED_DATA.