You may receive failure messages while unregistering a DLL that you were able to correctly register before (832926)



The information in this article applies to:

  • Microsoft Visual C++ .NET (2003)
  • Microsoft Visual C++ .NET (2002)
  • Microsoft .NET Framework 1.1
  • Microsoft .NET Framework 1.0

SUMMARY

When you try to unregister a DLL by using the Regsvr32 /u command, you may receive failure messages with return code values that indicate the Access is denied error message or the Error accessing the OLE registry error message.

This problem occurs when you try to implement scripting categories that require registry modification.

This article describes how to resolve this problem by adding scripting categories in the category map macro. This article also describes how you can keep both the DLL registration procedure and the DLL unregistration procedure simpler.

SYMPTOMS

You may receive failure messages with either the return code value of 0x80070005 or the return code value of 0x8002801c. These failure messages occur when you try to unregister a DLL that you were able to correctly register before. The return code values that are mentioned previously indicate the Access is denied error message and the Error accessing the OLE registry error message, respectively. You notice this problem when you try to explicitly register and then unregister some scripting categories. For example, this problem may occur when you try to explicitly register and to unregister object safety scripting categories while implementing the object safety category helper functions that are available in the Microsoft ActiveX Software Development Kit (SDK). This problem may also occur when you try to delete some sub keys in the DllUnregisterServer method.

CAUSE

This problem occurs because there are two implemented category keys that are not being unregistered in the registry. Typically, the keys are added, and the keys are deleted by using the CLSID_StdComponentCategoriesMgr COM object. If you do not create the CLSID_StdComponentCategoriesMgr COM object during unregistration, you cannot delete the keys that you added. You may receive the Access is denied error message when you try to delete the sub keys.

RESOLUTION

To resolve this problem, you must add a category map with all the categories that you want to implement. You put the category map with all the categories that you want to implement in your object header file after all the other maps in your object header file. The Active Template Library (ATL) macros then handle the registering and the unregistering of the categories that you want to implement, and no additional code is required.

To add a category map with all the categories that you want to implement, follow these steps:
  1. Start Microsoft Visual Studio .NET 2002 or Visual Studio .NET 2003.
  2. On the File menu, point to New, and then click Project.

    The New Project dialog box appears.
  3. Under Project Types, click Visual C++ Projects.
  4. Under Templates, click ATL Project.
  5. In the Name box, type Q832926, and then click OK.
  6. In the ATL Project Wizard - Q832926 dialog box, click Application Settings.
  7. Clear the Attributed option, and then click Finish.
  8. In Solution Explorer, right-click the Q832926 project node, point to Add, and then click Add Class.
  9. In the Add Class - Q832926 dialog box, click Generic C++ Class under Templates, and then click Open.
  10. In the Generic C++ Class Wizard - Q832926 dialog box, type helpers in the Class name box, and then click Finish.
  11. In Solution Explorer, double-click the Helpers.h file in the Header Files folder.
  12. Replace the code in the Helpers.h file with the following code:
    // Helpers.h: Declarations of object safety category helper functions.
    
    #include "comcat.h"
    
    HRESULT CreateComponentCategory(CATID catid, WCHAR* catDescription);
    HRESULT RegisterCLSIDInCategory(REFCLSID clsid, CATID catid);
  13. In Solution Explorer, double-click the Helpers.cpp file in the Source Files folder.
  14. Replace the code in the Helpers.cpp file with the following code:
    // Helpers.cpp: Implementation of object safety category helper functions.
    // (Copied from the ActiveX SDK docs.)
    
    #include "stdafx.h"
    #include "helpers.h"
    
    /////////////////////////////////////////////////////////////////////////////
    // CreateComponentCategory - This makes sure that the component category exists in the registry.
    // (Copied from the ActiveX SDK docs.)
    
    HRESULT CreateComponentCategory(CATID catid, WCHAR* catDescription)
    {
    
        ICatRegister* pcr = NULL ;	// interface pointer
        HRESULT hr = S_OK ;
    
        hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr, 
    			NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr);
    	if (FAILED(hr))
    		return hr;
    
        // This makes sure that the HKCR\Component Categories\{..catid...}
        // key is registered.
        CATEGORYINFO catinfo;
        catinfo.catid = catid;
        catinfo.lcid = 0x0409 ; // english
    
    	// This makes sure that the provided description is not too long.
    	// Only copy the first 127 characters if it is:
    	UINT len = (UINT)wcslen(catDescription);
    	if (len > 127)
    		len = 127;
        wcsncpy(catinfo.szDescription, catDescription, len);
    	// This makes sure that the description is null terminated.
    	catinfo.szDescription[len] = 0;
    
        hr = pcr->RegisterCategories(1, &catinfo);
    	if ( FAILED(hr) )
    		return hr;
    	hr = pcr->Release();
    	if ( FAILED(hr) )
    		return hr;
    
    	return hr;
    }
    
    /////////////////////////////////////////////////////////////////////////////
    // RegisterCLSIDInCategory - This registers the class clsid as implementing the category catid.
    // (Copied from the ActiveX SDK docs.)
    
    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)
    	{
            hr = pcr->Release();
    	}
      
    	return hr;
    }
    
  15. In Solution Explorer, double-click the Q832926.cpp file in the Source Files folder.
  16. In the Code window, add the following code after the #include statements:
    #include "helpers.h"
    #include <objsafe.h>
  17. In the Code window, add the following code before the DllRegisterServer function:
    GUID clsid = 
    { 0x84C0B6F1, 0x8036, 0x4D7C, { 0xB0,  0x2F,  0x4B,  0x53,  0x7B,  0x79,  0x72,  0x05 } };
  18. Replace the code in the DllRegisterServer function with the following code:
    // This registers the object, the typelib, and all the interfaces in the typelib.
    HRESULT hr = _AtlModule.DllRegisterServer();
    if ( FAILED(hr) )
    	return hr;
    
    //Uncomment the following block of code to reproduce the problem:
    /*
    // Mark as safe for scripting-failure OK.
    hr = CreateComponentCategory(CATID_SafeForScripting, 
    	L"Controls that are safely scriptable");
    
    if (SUCCEEDED(hr))
    	// Only register if the category exists.
    	RegisterCLSIDInCategory(clsid, CATID_SafeForScripting);
    else
    {
    	TCHAR szMsg[256];
    	_stprintf(szMsg, _T("Register safe-for-scripting failed: hr=%08X"), hr);
    	::MessageBox(NULL, szMsg, _T("ContactUpload"), MB_OK);
    	return E_FAIL;
    }
    
    // Mark as safe for data initialization.
    hr = CreateComponentCategory(CATID_SafeForInitializing, 
    		L"Controls safely initializable from persistent data");
    
    if (SUCCEEDED(hr))
    		// Only register if the category exists.
    	RegisterCLSIDInCategory(clsid, CATID_SafeForInitializing);
    else
    {
    	TCHAR szMsg[256];
    	_stprintf(szMsg, _T("Register safe-for-initializing failed: hr=%08X"), hr);
    	::MessageBox(NULL, szMsg, _T("ContactUpload"), MB_OK);
    	return E_FAIL;
    }*/
    
    return S_OK;
  19. Replace the code in the DllUnregisterServer function with the following code:
    HRESULT hr = _AtlModule.DllUnregisterServer();
    	if ( FAILED(hr) )
    		return hr;
    
    //Uncomment the following block of code to reproduce the problem:
    /*#if _WIN32_WINNT >= 0x0400
         hr = UnRegisterTypeLib(LIBID_Q832926Lib, 1, 0,
          LOCALE_NEUTRAL, SYS_WIN32);
    #endif*/
    
    	return hr;
  20. In Solution Explorer, right-click the Q832926 project node, point to Add, and then click Add Class.
  21. In the Add Class - Q832926 dialog box, click ATL Control under Templates, and then click Open.
  22. In the ATL Control Wizard - Q832926 dialog box, type MyATLCtrl in the Short name box.
  23. Click Options, and then click Connection points.
  24. Click Finish.
  25. In Solution Explorer, double-click the MyATLCtrl.h file in the Header Files folder.
  26. Add the following code after the CONNECTION_POINT_MAP map declaration and the MSG_MAP map declaration.
    //Comment the following category map to reproduce the problem:
    ///**** Add this to fix the unregister problem. *******/////
    BEGIN_CATEGORY_MAP(CMyATLCtrl)
    
          IMPLEMENTED_CATEGORY(CATID_SafeForScripting)
    
          IMPLEMENTED_CATEGORY(CATID_SafeForInitializing)
    
    END_CATEGORY_MAP()
    ///************         END OF FIX      *******/////
  27. Press CTRL+SHIFT+S to save the project.
  28. Press CTRL+SHIFT+B to build the solution.

    The solution builds successfully, and the Q832926.dll file is registered automatically.
  29. Click Start, and then click Run.
  30. In the Open box, type regsvr32 /u PathToDll\Q832926.dll, and then click OK.

    You receive the following message that the DLL is successfully unregistered:

    DllUnregisterServer in PathToDll\Q832926.dll succeeded.

    Note PathToDll is a placeholder for the directory path of the Q832926.dll file.

MORE INFORMATION

Steps to reproduce the problem

  1. In Solution Explorer, double-click the Q832926.cpp file in the Source Files folder.
  2. Locate all instances of the following line of code. Uncomment the block of code that is commented after the following line of code:
    //Uncomment the following block of code to reproduce the problem:
  3. In Solution Explorer, double-click the MyATLCtrl.h file in the Header Files folder.
  4. Locate the following line of code. Comment the CATEGORY_MAP code that you previously added.
    //Comment the following category map to reproduce the problem:
  5. Press CTRL+SHIFT+S to save the project.
  6. Press CTRL+SHIFT+B to build the solution.

    The solution builds successfully, and the Q832926.dll file is automatically registered.
  7. Try to unregister the Q832926.dll file as instructed in step 30 of the "Resolution" section.

    You may receive the following failure message that is mentioned in the "Symptoms" section:

    DllUnregisterServer in PathToDll\Q832926.dll failed. Return code was: 0x8002801c.
    Note PathToDll is a placeholder for the directory path of the Q832926.dll file.

REFERENCES

For additional information about category macros, visit the following Microsoft Developer Network (MSDN) Web site:

Modification Type:MinorLast Reviewed:1/6/2006
Keywords:kbcode kberrmsg kbRegistry kbDLL kbATLServer kbActiveXScript kbprb KB832926 kbAudDeveloper