BBSˮľÇ廪վ¡Ã¾«»ªÇø

·¢ÐÅÈË: afei (·ÉÉ«¾«Áé), ÐÅÇø: Java 
±ê  Ìâ: CAB&SIGN¼¼Êõ(3)---ActiveX control 
·¢ÐÅÕ¾: BBS Ë®Ä¾Ç廪վ (Tue Dec 16 16:08:34 1997) 
 
 
                                                           Start Page | Site Map 
                                                               MSDN Online 
  
Signing and Marking ActiveX Controls with ATL  
 
 
 
This article is based on the article Signing and Marking ActiveX Controls, but has been updated to demonstrate 
those techniques with an ATL (Active Template Library) control.  
 
Introduction: I don’t want all those security dialog boxes!  
 
If you've used unsigned or unmarked ActiveX controls with Microsoft Internet Explorer 3.0, you may have gotten 
dialog boxes telling you that the control is not signed, the control is not safe for initializing, or the control is not safe 
for scripting. Or, if you set your security level to high rather than medium, the control did not load or display at all. 
(Thrill-seekers who set security at none won't get any of these behaviors. But they won't have any security, 
either!)  
 
Well, you can have your security and use your controls, too! This article tells you how to get rid of those dialog 
boxes without setting your security level to none. The issue is especially important because Microsoft Internet 
Explorer defaults to the high security setting.  
 
These behaviors happen because of Internet Explorer's security mechanisms for ActiveX controls. The ActiveX 
controls you can automatically download over the Internet can do anything—including things that would damage 
your system. Java attempts to solve this problem by severely limiting what a Java applet can do. It can't, for 
instance, access the client computer's file system. ActiveX controls take a different approach: they demand 
positive identification of the author of the control, verify that the control hasn't been modified since it was signed, 
and identify safe controls—kind of like shrink-wrapping a control for download over the Internet. Because of this 
approach, ActiveX controls can use the full power of the client's system safely.  
 
What's in this article?  
 
• What users experience if you don't sign your control  
 
• What users experience if you don't mark your control  
 
• How to sign your control  
 
• How to mark your control as safe  
 
• How to mark your control by tweaking the registry  
 
• How to mark your control by modifying your self-registration code  
 
• How to modify your control by implementing the IObjectSafety OLE interface 
 
What happens if you don't sign your control?  
 
If a user attempts to load a Web page that uses a control not already registered on the user's system, Internet 
Explorer will download the control. But before it does, the browser checks to see if the control has been digitally 
signed. If not, and security is set at high, the following dialog box appears:  
 
                                            
 
And the following appears in the Web page instead of the control:  
 
                
 
Note that at high security, there is no option to use the control.  
 
If security is set to medium, the following dialog box appears:  
 
                                                  
 
If the user clicks Yes, the control installs itself normally. If the user clicks No, an error placeholder appears 
instead of the control, the same as appears with the high security setting.  
 
If security is set at none, the control works and no dialog box appears.  
 
Once the control is registered on the user’s system, the control no longer invokes code-signing dialog boxes. 
After a control is installed, it is considered safe even if it was not signed originally .  
 
If a control is signed, a certificate dialog box appears, displaying information intended to help users decide 
whether to trust the author of the control.  
 
                                            
 
Note the check boxes at the bottom. When the user checks either box, controls signed by those sources will 
download with no further interruption. (For software published by individual developers, only the first check box is 
available with Internet Explorer version 3.01.)  
 
What happens if you don't mark your control?  
 
Once the control is installed, Internet Explorer 3.0 won't check it again to see if it's signed. However, before your 
Web page initializes the control, Internet Explorer 3.0 will verify that the control is marked as safe for initializing 
and, if your Web page scripts the control, whether that control is marked as safe for scripting.  
 
If the control passes both checks, it loads and runs. If it fails either check, one of three things will happen:  
 
If security is set on high, you'll get the same "Potential safety violation avoided" dialog box as if the control were 
being installed and hadn't been signed. The control will display and will run, but without initialization and without 
scripting. Since the control won't be initialized, it will use default values for its persistent properties. Also, if any 
script refers to the control, the script will fail, displaying a dialog box similar to the following:  
 
                                                 
 
If security is set to medium, the user gets one dialog box for each security check. First, Internet 
Explorer 3.0 will check to see if the control is safe for initializing. If it's not, the browser 
will display the following dialog box:  
 
                                           
 
If the user selects either of the No buttons, the affected control(s) will not be initialized; they will use default 
values for their persistent properties.  
 
Next, Internet Explorer 3.0 checks to see if the control is safe for scripting. If not, it displays a dialog box similar to 
the following:  
 
                                           
 
If the user selects either No button, any script that refers to an affected control will fail, displaying a dialog box 
similar to the one in the high security instance above.  
 
If security is set to none, everything just works.  
 
Making controls work with security  
 
Two fairly simple steps will eliminate these problems: Sign the control, and mark it as safe for scripting and safe 
for initializing.  
 
Sign that control  
 
To sign your control, you'll need to obtain a certificate from a Certificate Authority such as VeriSign™. Find 
directions from VeriSign at http://digitalid.verisign.com/codesign.htm. (This link points to a server not 
under the control of Microsoft Corporation. Please read our disclaimer before continuing.)  
 
There are two classes of digital IDs for Authenticode™ technology. Class 2 certificates, for individuals who 
publish software, cost US$20 per year and require that you provide your name, address, e-mail address, date of 
birth, and Social Security Number. After VeriSign verifies this information, you will be issued a certificate.  
 
Class 3 certificates, for commercial software publishers, cost US$400 per year and require a 
Dun-and-Bradstreet rating in addition to company name, location, and contacts.  
 
Once you obtain the certificate, run the SIGNCODE utility (available in the CAB&SIGN directory on the Visual 
C++ 5.0 CD) to launch the Code Signing Wizard. For details, see "Signing a CAB File" in the article Creating 
Signed CAB Files for MFC and ATL Controls. Note that you'll have to re-sign code if you modify it (such as to 
mark it safe for initializing and scripting). Note also that signatures are only checked when the control is first 
installed—the signature is not checked every time Internet Explorer uses the control.  
 
Once your code is signed, even users whose security setting is high will be able to download, install, and register 
your controls. But they will only be able to use pages that initialize and script these signed controls if you mark 
them as safe for initializing and safe for scripting.  
 
Most companies should have one certificate and one group responsible for signing code so that they have control 
over what's signed. If your company does, follow its procedure, rather than signing the control yourself.  
 
If it’s safe, mark it as safe!  
 
So you've got your control signed—but you still get the safety dialog boxes shown above in the section What 
happens if you don't mark your control? How do you get rid of these dialog boxes so that your users will 
have a seamless browsing experience?  
 
Simple. Mark your control as safe for initialization and safe for scripting. But only do this if you know the control is 
actually safe.  
 
How do you know the control is safe?  
 
Read this section carefully—this is a serious issue.  
 
Since the marking stays with the control rather than with the Web page, controls marked as safe have to be safe 
in all possible Web pages. So a control marked as safe has to be written to protect itself from any unpleasant thing 
a Web page author might do in either initializing or scripting the control. While it's fairly simple to verify that a 
particular control is safe when used with a particular Web page, it's far from trivial to verify that the control will 
always be safe with any Web page.  
 
If you mark your control as safe for initializing, you are asserting that no matter what values are used to initialize 
your control, it won't do anything that would damage a user's system or compromise the user’s security.  
 
If you mark your control as safe for scripting, you are asserting that your control won't do anything to damage a 
user's system or compromise the user’s security regardless of how your control's methods and properties are 
manipulated by the Web page's script. In other words, it has to accept any method calls (with any parameters) 
and/or property manipulations in any order without doing anything bad.  
 
Here are some things to consider before you mark your control as safe. You should verify that your control:  
 
• Does not manipulate the file system  
 
• Does not manipulate the registry (except to register and unregister itself)  
 
• Does not overindex arrays or otherwise manipulate memory incorrectly  
 
• Validates (and corrects) all input, including initialization, method parameters, and property set functions—both in 
DoPropExchange and in SetColor  
 
• Does not misuse any data about the user or provided by the user  
 
This list of do's and don'ts isn’t complete, but it's a good start.  
 
It is very important never to mark a control safe that isn't actually safe, as tempting as this might be (if, for 
instance, you don't have the source code for a control). Once marked safe, the control will be considered safe by 
all Web pages, regardless of what the page might actually do to the control. (As of this writing, there is no way to 
sign a Web page nor mark it as safe; nor is there any way to specify that a control is only marked as safe with 
certain Web pages.)  
 
A simple and safe alternative to marking unsafe controls as safe is to write a new safe control that subclasses the 
unsafe control. Just ensure that your new control's initialization, methods, and properties are safe.  
 
Ways to mark a control as safe  
 
You can mark a control as safe in two basic ways:  
 
• Add the necessary entries to the registry key for the control to mark it as safe. You can do this either by 
manipulating the registry yourself, perhaps when you install the control, or, better yet, by calling certain functions 
when the control registers itself.  
 
• Implement the IObjectSafety OLE interface, which allows the container to query the control's current safety 
state and to request that it change to safe mode.  
 
Each method has advantages and disadvantages.  
 
Implementing IObjectSafety allows your control to have a safe mode and an unsafe mode—a spreadsheet control, 
for instance, could both read and write files when it's in unsafe mode, but perhaps only read from files when it's in 
safe mode. This would allow the control to be used in very powerful ways when safety wasn't important but still 
be safe for use in a Web page. Using IObjectSafety also allows a container to determine whether an 
already-created control is safe without accessing the registry, which can make your control come up faster. 
Finally, controls that implement IObjectSafety can be safe on some interfaces and unsafe on others, since safety 
is on an interface-by-interface basis.  
 
Adding the entries to the registry has the theoretical advantage that a container could check the registry to see if a 
control is safe before instantiating it. (Checking the registry is expensive, but instantiating controls is even more 
expensive.) For instance, when an authoring environment such as the ActiveX Control Pad or Visual Basic 
showed you a list of controls you could insert, it could also indicate which controls were safe and which weren't. 
(This is a theoretical advantage because neither environment currently does this.) If the entries weren't in the 
registry, the authoring environment would have to instantiate the control in order to use IObjectSafety.  
 
Because ActiveX controls must be able to register and unregister themselves using DllRegisterServer and 
DllUnregisterServer, you'll want to do the registry manipulation in your control's code rather than in a setup 
program.  
 
There is one situation in which you might want to add the entries to the registry using a setup program or .REG file 
—when you know the control is safe, but you can't modify the control itself. As mentioned above, we do not 
recommend this unless you're sure the control is actually safe. (But you can always create a new control that 
safely subclasses the unsafe control.) If you use this method to mark controls that are automatically installed by 
Internet Explorer 3.0, you'll need to install your control using an .INF file rather than just supplying the control's 
executable file.  
 
The added code necessary to implement IObjectSafety is about the same size as the code to implement 
self-registration—less than 1K. So from a size perspective, it doesn't matter which you use.  
 
If your control is always safe, it is possible to use both methods. Using both gives you the best performance, both 
in secure containers and in authoring environments that check for safety (although no such authoring 
environments exist as of this writing) but makes your control grow roughly an additional 1K.  
 
If your control has both safe and unsafe modes, or is only safe on some of its interfaces, you should only 
implement IObjectSafety—do not add the registry keys, because they imply that your control is always safe on all 
interfaces. Also, remember that implementing IObjectSafety will give better run-time performance. 
 
Marking in the registry yourself  
 
You can add the necessary keys to mark the control in the registry either when you install the control or later.  
 
The two keys you need are both of the following form:  
 
\HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\<GUID of control class>\Implemented 
Categories\<GUID of category>  
 
The ActiveX SDK header file ObjSafe.H defines the GUID values for CATID_SafeForInitializing and 
CATID_SafeForScripting.  
 
For example in the Polygon control from the ATL tutorial (available in the Visual C++ 5.0 online documentation), 
the class GUID is {4CBBC676-507F-11D0-B98B-000000000000}, so the two keys to add are:  
 
   REGEDIT4 
  
   [HKEY_CLASSES_ROOT\CLSID\{4CBBC676-507F-11D0-B98B-000000000000}\Implemented Categories] 
   [HKEY_CLASSES_ROOT\CLSID\{4CBBC676-507F-11D0-B98B-000000000000}\Implemented Categories\{7DD95801-9882-11CF-9FA9-00AA006C42C4}] 
   [HKEY_CLASSES_ROOT\CLSID\{4CBBC676-507F-11D0-B98B-000000000000}\Implemented Categories\{7DD95802-9882-11CF-9FA9-00AA006C42C4}] 
 
To have the component categories properly described in the registry, you should also add the following keys:  
 
   REGEDIT4 
          
   [HKEY_CLASSES_ROOT\Component Categories] 
   [HKEY_CLASSES_ROOT\Component Categories\{7DD95801-9882-11CF-9FA9-00AA006C42C4}] 
   "409"="Controls that are safely scriptable" 
   [HKEY_CLASSES_ROOT\Component Categories\{7DD95802-9882-11CF-9FA9-00AA006C42C4}] 
   "409"="Controls safely initializable from persistent data" 
 
Don't use this method unless you have no other choice, and never use this method to mark as safe controls that 
aren't really safe. 
 
Marking the control while self-registering  
 
If you're going to mark your control by changing the registry, it's far better to add the code to the control's 
self-registration routine rather than rely on setup routines. Note that the ActiveX control specification requires 
that ActiveX controls be self-registering.  
 
In the Polygon control from the ATL tutorial, you can simply add the highlighted lines shown below to the 
PolyCtl.rgs file.  
 
   HKCR 
   { 
        PolyCtl.PolyCtl.1 = s 'PolyCtl Class' 
        { 
                CLSID = s '{4CBBC676-507F-11D0-B98B-000000000000}' 
        } 
        PolyCtl.PolyCtl = s 'PolyCtl Class' 
        { 
                CurVer = s 'PolyCtl.PolyCtl.1' 
                CLSID = s '{4CBBC676-507F-11D0-B98B-000000000000}' 
        } 
        NoRemove CLSID 
        { 
                NoRemove 'Component Categories' 
                { 
                        '{7DD95801-9882-11CF-9FA9-00AA006C42C4}' = s '0' 
                        { 
                                '409' = s 'Controls that are safely scriptable' 
                        } 
                        '{7DD95802-9882-11CF-9FA9-00AA006C42C4}' = s '0' 
                        { 
                                '409' = s 'Controls safely initializable from persistent data' 
                        } 
                } 
                ForceRemove {4CBBC676-507F-11D0-B98B-000000000000} = s 'PolyCtl Class' 
                { 
                        'Implemented Categories' 
                        { 
                                '{7DD95801-9882-11CF-9FA9-00AA006C42C4}' 
                        } 
                        'Implemented Categories' 
                        { 
                                '{7DD95802-9882-11CF-9FA9-00AA006C42C4}' 
                        } 
                        ProgID = s 'PolyCtl.PolyCtl.1' 
                        VersionIndependentProgID = s 'PolyCtl.PolyCtl' 
                        InprocServer32 = s '%MODULE%' 
                        { 
                                val ThreadingModel = s 'Apartment' 
                        } 
                        ForceRemove 'Control' 
                        ForceRemove 'Programmable' 
                        ForceRemove 'Insertable' 
                        ForceRemove 'ToolboxBitmap32' = s '%MODULE%, 1' 
                        'MiscStatus' = s '0' 
                        { 
                         '1' = s '131473' 
                        } 
                        'TypeLib' = s '{4CBBC673-507F-11D0-B98B-000000000000}' 
                        'Version' = s '1.0' 
                } 
        } 
   } 
 
Implementing the IObjectSafety Interface  
 
The most flexible way to "mark" your control is to implement the IObjectSafety interface. This requires you to 
implement two functions: GetInterfaceSafetyOptions and SetInterfaceSafetyOptions.  
 
As you might imagine, GetInterfaceSafetyOptions allows your control's container to ask the control whether it's 
currently safe in each of the two currently supported safety classes (safe for initializing and safe for scripting).  
 
In addition, it allows the container to ask the control whether it can be safe—and allows the container to ask these 
questions on an interface-by-interface basis. Check the interface ID in the event that only some of your 
interfaces are safe.  
 
The method SetInterfaceSafetyOptions is called by the container to tell the control to turn safety on. If the 
control supports the passed interface and the required safety options, then it should turn on the required safety for 
that interface. If not, it should return an appropriate error code.  
 
ATL supplies an implementation of the IObjectSafety interface in the class IObjectSafetyImpl. Unfortunately, 
by default, it only marks the control as safe for scripting and thus isn’t very flexible. An improved version would 
allow you to specify the supported safety options of the control to the class to determine how it implements the 
two methods.  
 
Here is an improved implementation of the IObjectSafety interface that allows you to specify the supported safety 
options to the constructor of the class.  
 
   template <class T> 
   class ATL_NO_VTABLE CMyObjectSafety 
   { 
   public: 
        // Pass in supported safety options, e.g. 
        // INTERFACESAFE_FOR_UNTRUSTED_CALLER - safe for scripting 
        // INTERFACESAFE_FOR_UNTRUSTED_DATA - safe for initialization from data 
        CMyObjectSafety(DWORD dwSupportedSafety) 
        { 
                m_dwCurrentSafety = 0; 
                m_dwSupportedSafety = dwSupportedSafety; 
        } 
  
        // IUnknown 
        // 
        STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject) = 0; 
        _ATL_DEBUG_ADDREF_RELEASE_IMPL(CMyObjectSafety) 
  
        // IObjectSafety 
        // 
        STDMETHOD(GetInterfaceSafetyOptions) 
                (REFIID riid, DWORD *pdwSupportedOptions, DWORD *pdwEnabledOptions) 
        { 
                ATLTRACE(_T("CMyObjectSafety::GetInterfaceSafetyOptions\n")); 
                T* pT = (T*)this; 
                if (pdwSupportedOptions == NULL || pdwEnabledOptions == NULL) 
                        return E_POINTER; 
                 
                HRESULT hr; 
                IUnknown* pUnk; 
                // Check if we support this interface 
                hr = pT->GetUnknown()->QueryInterface(riid, (void**)&pUnk); 
                if (SUCCEEDED(hr)) 
                { 
                        // We support this interface so set the safety options accordingly 
                        pUnk->Release();        // Release the interface we just acquired 
                        *pdwSupportedOptions = m_dwSupportedSafety; 
                        *pdwEnabledOptions = m_dwCurrentSafety; 
                } 
                else 
                { 
                        // We don't support this interface 
                        *pdwSupportedOptions = 0; 
                        *pdwEnabledOptions = 0; 
                } 
                return hr; 
        } 
        STDMETHOD(SetInterfaceSafetyOptions) 
                (REFIID riid, DWORD dwOptionSetMask, DWORD dwEnabledOptions) 
        { 
                ATLTRACE(_T("CMyObjectSafety::SetInterfaceSafetyOptions\n")); 
                T* pT = (T*)this; 
                IUnknown* pUnk; 
                 
                // Check if we support the interface and return E_NOINTEFACE if we don't 
                if (FAILED(pT->GetUnknown()->QueryInterface(riid, (void**)&pUnk))) 
                        return E_NOINTERFACE; 
                pUnk->Release();        // Release the interface we just acquired 
                 
                // If we are asked to set options we don't support then fail 
                if (dwOptionSetMask & ~m_dwSupportedSafety) 
                        return E_FAIL; 
  
                // Set the safety options we have been asked to 
                m_dwCurrentSafety = m_dwCurrentSafety & ~dwEnabledOptions |  
                        dwOptionSetMask; 
                return S_OK; 
        } 
        DWORD m_dwCurrentSafety; 
        DWORD m_dwSupportedSafety; 
   }; 
 
To add this new improved implementation of IObjectSafety to the Polygon control requires several changes to the 
CPolyCtl class in addition to adding the above code.  
 
Change the inheritance list to derive from CMyObjectSafety instead of IObjectSafetyImpl.  
 
Change the constructor to initialize the CMyObjectSafety class with the supported safety. For example, if the 
control is safe for scripting and safe for initialization from data, then the constructor for the Polygon control would 
become:  
 
   CPolyCtl() : CMyObjectSafety<CPolyCtl> 
        (INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA) 
        { 
                m_nSides = 3; 
                m_clrFillColor = RGB(0, 0xFF, 0); 
        } 
 
Replace the following line from the interface map:  
 
        COM_INTERFACE_ENTRY_IMPL(IObjectSafety) 
 
with:  
 
        COM_INTERFACE_ENTRY_IID(IID_IObjectSafety, CMyObjectSafety<CPolyCtl>) 
 
to say that class CMyObjectSafety is now being used to implement the IObjectSafety interface.  
 
That’s all you need to do. The same technique can be used to add this implementation of IObjectSafety to any 
ATL control.  
 
Conclusion: Can you spare a moment for safety  
 
Signing and marking together require only about 4.5K. At 28.8K bps, that’s a second and a half additional 
download time for the user, hardly a significant burden when you consider the huge gain in safety. So take the 
time to sign all your code and mark those controls that are safe.  
 
© 1997 Microsoft Corporation. All 
rights reserved. Terms of use.  
 
-- 
¡ù À´Ô´:¡¤BBS Ë®Ä¾Ç廪վ bbs.net.tsinghua.edu.cn¡¤[FROM: curie.eps.jhu.e] 

BBSˮľÇ廪վ¡Ã¾«»ªÇø