Click here to Skip to main content
15,860,859 members
Articles / Programming Languages / C++
Article

DLL Registration / Unregistration using a Shell Extension

Rate me:
Please Sign up or sign in to vote.
4.50/5 (2 votes)
30 Nov 19992 min read 152.9K   2.7K   53   18
Why are we still using command prompt to register a DLL and not using the Windows Explorer?
  • Download demo project - 22 Kb
  • Download source files - 13 Kb

    Sample Image

    This time is about shell extensions. Because I am tired to open a console every time when I want to register the Debug dll as shell extension, I was thinking that's a good idea to right-click the dll to register/unregister.

    So here we are! (as stupid Dexters's sister, DeeDee, said once). You will find here few things you cannot find in MSDN or in other articles.

    1. GUID. You can obtain this sinister number running one of the uuidgen or guidgen tools. This is CLSID_DLLRegisterer.

    2. The main file, where the DLL's entry point DllMain resides, is dllregshex.cpp. I prefer to load resources at start and free them at end, especially when are in little number. So, the routines _LoadResources and _UnloadResources do this. The other two interesting routines are DllRegisterServer and DllUnregisterServer. If you use one, be polite to the user and use the twin (I have seen may Reg, but few Unreg). That's when you did not choose another way (as a .reg file, for example, or separate install/uninstall programs).

    So, what's happening registering a dll as shell context extension? You have to create the entry in CLSID key and to create under this the 'samename' key and the thread model key (I read somewhere that only 'Apartment' is accepted for context extensions - is really true?).

    STDAPI 
    DllRegisterServer(void)
    {
      HINSTANCE hInst = g_hmodThisDll;
    
      int      i;
      HKEY     hKey;
      LRESULT  lResult;
      DWORD    dwDisp;
      TCHAR    szSubKey[MAX_PATH];
      TCHAR    szCLSID[MAX_PATH];
      TCHAR    szModule[MAX_PATH];
      LPWSTR   pwszShellExt;
    
      StringFromIID(CLSID_DLLRegisterer, &pwszShellExt);
    
      if (pwszShellExt) 
      {
        WideCharToLocal(szCLSID, pwszShellExt, ARRAYSIZE(szCLSID));
    
        LPMALLOC pMalloc;
        CoGetMalloc(1, &pMalloc);
        if(pMalloc)
        {
          pMalloc->Free(pwszShellExt);
          pMalloc->Release();
        }
      }                                                
    
      GetModuleFileName(hInst, szModule, ARRAYSIZE(szModule));
      REGSTRUCT ShExClsidEntries[] = 
      {
        HKEY_CLASSES_ROOT, TEXT("CLSID\\%s"), NULL, TEXT(DLLREGUNREGNAME),
        HKEY_CLASSES_ROOT, TEXT("CLSID\\%s\\InProcServer32"), NULL, TEXT("%s"),
        HKEY_CLASSES_ROOT, TEXT("CLSID\\%s\\InProcServer32"),
    	  TEXT("ThreadingModel"), TEXT("Apartment"), NULL, NULL, NULL, NULL
      };
    
      for(i = 0; ShExClsidEntries[i].hRootKey; i++) 
      {
        wsprintf(szSubKey, ShExClsidEntries[i].lpszSubKey, szCLSID);
        lResult = RegCreateKeyEx(ShExClsidEntries[i].hRootKey, szSubKey, 0, NULL,
          REG_OPTION_NON_VOLATILE,  KEY_WRITE, NULL,
          &hKey, &dwDisp);
    
        if(NOERROR == lResult) 
        {
          TCHAR szData[MAX_PATH];
          wsprintf(szData, ShExClsidEntries[i].lpszData, szModule);
          lResult = RegSetValueEx(hKey, ShExClsidEntries[i].lpszValueName, 0,
            REG_SZ, 
            (LPBYTE)szData, lstrlen(szData) + 1);
          RegCloseKey(hKey);
        } 
        else
          return SELFREG_E_CLASS;
        }
    The other entries bind the desired extension (in our case, the DLLs) to our CLSID:
    REGSTRUCT OtherShExEntries[] =
    {
      HKEY_LOCAL_MACHINE, TEXT("software\\classes\\clsid\\"DLLREGUNREGNAME),
      NULL,   TEXT("%s"),
      HKEY_CLASSES_ROOT,
      TEXT("dllfile\\shellex\\ContextMenuHandlers\\"DLLREGUNREGNAME),
      NULL, TEXT("%s"), NULL, NULL, NULL, NULL
    };
    
    for (i = 0; OtherShExEntries[i].hRootKey; i++)
    {
      wsprintf(szSubKey, OtherShExEntries[i].lpszSubKey, szCLSID);
      lResult = RegCreateKeyEx(OtherShExEntries[i].hRootKey, szSubKey, 0, NULL,
        REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, &dwDisp);
    
      if(NOERROR == lResult)
      {
        TCHAR szData[MAX_PATH];
        wsprintf(szData, OtherShExEntries[i].lpszData, szCLSID);
        lResult = RegSetValueEx(hKey, OtherShExEntries[i].lpszValueName, 0,
        REG_SZ,
          (LPBYTE)szData, lstrlen(szData) + 1);
        RegCloseKey(hKey);
      }
      else
        return SELFREG_E_CLASS;
    }
    
    A special mention when Windows is NT: register the extension as 'Approved':
      OSVERSIONINFO  osvi;
      osvi.dwOSVersionInfoSize = sizeof(osvi);
      GetVersionEx(&osvi);
      if (VER_PLATFORM_WIN32_NT == osvi.dwPlatformId)  
      {
        lstrcpy( szSubKey,
        TEXT(
          "Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved"
    	));
        lResult = RegCreateKeyEx(HKEY_LOCAL_MACHINE, szSubKey, 0, NULL,
          REG_OPTION_NON_VOLATILE, KEY_WRITE,
          NULL, &hKey, &dwDisp);
        if(NOERROR == lResult) 
        {
          TCHAR szData[MAX_PATH];
          lstrcpy(szData, DLLREGUNREGNAME);
          lResult = RegSetValueEx(hKey, szCLSID, 0, REG_SZ, (LPBYTE)szData,
    	  lstrlen(szData) + 1);
          RegCloseKey(hKey);
        } 
        else
          return SELFREG_E_CLASS;
      }
    
      return S_OK;
    }
    The unregister routine performs the inverse way. The rest is history: AddRef, Release, QueryInterface.
    3. The utils file (dllreg_util.cpp) contains conversion routines. This UNICODE job really kills me, I simply copied one of the 1000 other ways to deal with it.
    4. The menu file (dllreg_ctxm.cpp) contains 3 standard routines: QueryContextMenu, which builds the added popup menu and his options, InvokeCommand and GetCommandString - this will show you a simple text on Explorer' status bar. Nothing unusual.
    #include "dllreg_xhdr.h"
    #include "dllregshex.h"
    #include "dllreg_util.h"
    #include "resource.h"
    
    extern UINT      g_cRefThisDll;
    extern HINSTANCE   g_hmodThisDll;
    
    extern HBITMAP      hBmp_Install;
    extern HBITMAP      hBmp_Uninstall;
    extern HBITMAP      hBmp_About;
    extern HMENU      hSubMenu;
    
    // IContextMenu
    STDMETHODIMP 
    CShellExt::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst,
      UINT idCmdLast, UINT uFlags)
    {
      _UNUSED_PARAMETER(idCmdLast);
      UINT idCmd = idCmdFirst;
    
      char *szMenuText_Popup   = "Shell e&xtension";
      char *szMenuText_Install   = "&Install";
      char *szMenuText_Uninstall   = "&Uninstall";
      char *szMenuText_About   = "&About...";
    
      BOOL bAppendItems = TRUE;
    
      if((uFlags & 0x000F) == CMF_NORMAL)
        bAppendItems = TRUE;
      else if (uFlags & CMF_VERBSONLY)
        bAppendItems = TRUE;
      else if (uFlags & CMF_EXPLORE)
        bAppendItems = TRUE;
      else
        bAppendItems = FALSE;
    
      if(bAppendItems)
      {
        InsertMenu(hMenu, indexMenu++, MF_SEPARATOR | MF_BYPOSITION, 0, NULL);  
    
        HMENU hSubMenu = CreateMenu();
        if(hSubMenu)
        {
          InsertMenu(hSubMenu, 0, MF_STRING    | MF_BYPOSITION, idCmd++,
    	    szMenuText_Install);
          SetMenuItemBitmaps(hSubMenu, 0, MF_BYPOSITION, hBmp_Install,
    	    hBmp_Install);
          InsertMenu(hSubMenu, 1, MF_STRING | MF_BYPOSITION, idCmd++,
    	    szMenuText_Uninstall);
          SetMenuItemBitmaps(hSubMenu, 1, MF_BYPOSITION, hBmp_Uninstall,
    	    hBmp_Uninstall);
    
          InsertMenu(hSubMenu, 2, MF_SEPARATOR | MF_BYPOSITION, 0, NULL);
    
          InsertMenu(hSubMenu, 3, MF_STRING    | MF_BYPOSITION, idCmd++,
    	    szMenuText_About);
          SetMenuItemBitmaps(hSubMenu, 3, MF_BYPOSITION, hBmp_About, hBmp_About);
        }
         
        InsertMenu(hMenu, indexMenu++, MF_STRING | MF_POPUP | MF_BYPOSITION, 
          (UINT_PTR)hSubMenu, szMenuText_Popup);
        InsertMenu(hMenu, indexMenu++, MF_SEPARATOR | MF_BYPOSITION, 0, NULL);
    
        return ResultFromShort(idCmd - idCmdFirst);
      }
      
      return NOERROR;
    }
    
    STDMETHODIMP 
    CShellExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
    {
      HRESULT hr = E_INVALIDARG;
      if (!HIWORD(lpcmi->lpVerb))
      {
        UINT idCmd = LOWORD(lpcmi->lpVerb);
        switch (idCmd)
        {
          case 0:
            hr = DoInstall(lpcmi->hwnd, lpcmi->lpDirectory,
    		  lpcmi->lpVerb, lpcmi->lpParameters, lpcmi->nShow);
            break;
          case 1:
            hr = DoUninstall(lpcmi->hwnd, lpcmi->lpDirectory,
    		  lpcmi->lpVerb, lpcmi->lpParameters, lpcmi->nShow);
            break;
          case 2:
            hr = DoAbout(lpcmi->hwnd, lpcmi->lpDirectory,
    		  lpcmi->lpVerb, lpcmi->lpParameters, lpcmi->nShow);
            break;
          default:
            break;
        }
      }
      return hr;
    }
    
    STDMETHODIMP 
    CShellExt::GetCommandString(UINT idCmd, UINT uFlags, UINT FAR *reserved,
      LPSTR pszName, UINT cchMax)
    {
      _UNUSED_PARAMETER(reserved);
      _UNUSED_PARAMETER(uFlags);
    
      *pszName = 0;
      cchMax   = 40;
      char psz[40];
    
      switch (idCmd)
      {
        case 0:
          LoadString(g_hmodThisDll, IDCMD_INSTALL, psz, cchMax);
          break;
        case 1:
          LoadString(g_hmodThisDll, IDCMD_UNINSTALL, psz, cchMax);
          break;
        case 2:
          LoadString(g_hmodThisDll, IDCMD_ABOUT, psz, cchMax);
          break;
        default:
          break;
      }
      wcscpy((unsigned short *)pszName, _WCSTR(psz));
      return NOERROR;
    }
    5. The most interesting stuff is contained in file dllreg_job.cpp, and this is the _GetFullFileName function. The others are handled in one routine, which simply format a command line using the regsvr32.exe tool (_DoRegisterJob)
    STDMETHODIMP 
    CShellExt::_GetFullFileName()
    {
      HRESULT hr = S_FALSE;
    
      //
      // IEnumFORMATETC. Needed for format enumeration.
      //
      IEnumFORMATETC *pefEtc = 0;
      hr = m_pDataObj->EnumFormatEtc(DATADIR_GET, &pefEtc);
      if(SUCCEEDED(hr))
      {
        hr = pefEtc->Reset();   //   Reset enumeration.
    
        if(SUCCEEDED(hr))
        {
          //
          //   FORMATETC. Needed for get data about object.
          //
          FORMATETC fEtc;
          ULONG ulFetched = 0L;
    
          while(TRUE)
          {
            hr = pefEtc->Next(1, &fEtc, &ulFetched);
            if(FAILED(hr) || (ulFetched <= 0))
              break;
    
            //
            //   'Arm' format and 'launch' to obtain STGMEDIUM...
            //
            fEtc.cfFormat   = CF_HDROP;
            fEtc.dwAspect   = DVASPECT_CONTENT;
            fEtc.lindex      = -1;
            fEtc.ptd      = NULL;
            fEtc.tymed      = TYMED_HGLOBAL;
            //
            //   IDataObject : GetData. Returned as TYMED_HGLOBAL.
            //
            STGMEDIUM stgM;
            hr = m_pDataObj->GetData(&fEtc, &stgM);
            if(SUCCEEDED(hr))
            {
              if(stgM.tymed == TYMED_HGLOBAL)
              {
                m_ppszFileUserClickedOn[0] = '\0';
    
                UINT nFileCount = DragQueryFile((HDROP)stgM.hGlobal,
                  (UINT)INVALID_HANDLE_VALUE, NULL, 0);
                if(nFileCount >= 1)
                {
                  if(m_ppszFileUserClickedOn)
                  {
                    DeleteFileData();
                  }
    
                  m_ppszFileUserClickedOn = new LPTSTR[nFileCount];
    
                  for(register UINT x = 0; x < nFileCount; x++)
                  {
                    m_ppszFileUserClickedOn[x] = new TCHAR[_MAX_PATH + 1];
                    // one file only; eliminate this and improve code to allow
                    //  multiple-DLL file (un)registering
                    DragQueryFile((HDROP)stgM.hGlobal, x,
                      m_ppszFileUserClickedOn[x], _MAX_PATH + 1);
                  }
    
                  m_xFileCount = nFileCount;
                }
              }
            }
          }
        }
      }
    
      if(pefEtc)
        pefEtc->Release();
      return hr;
    }
  • License

    This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

    A list of licenses authors might use can be found here


    Written By
    Web Developer
    Romania Romania
    This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

    Comments and Discussions

     
    Generalmore simple... Pin
    mayti14-Sep-05 2:13
    mayti14-Sep-05 2:13 
    GeneralGood Job Pin
    Mark Philip Henri19-Jul-03 13:09
    Mark Philip Henri19-Jul-03 13:09 
    GeneralRe: Good Job Pin
    Sardaukar19-Jul-03 22:17
    Sardaukar19-Jul-03 22:17 
    GeneralRe: Good Job Pin
    Heath Stewart26-Jan-04 3:23
    protectorHeath Stewart26-Jan-04 3:23 
    GeneralOLE2! Command Failed Pin
    10-Mar-02 22:03
    suss10-Mar-02 22:03 
    GeneralEasier Method Pin
    Todd Smith4-Mar-02 16:45
    Todd Smith4-Mar-02 16:45 
    GeneralRe: Easier Method Pin
    Cristian Amarie4-Mar-02 20:22
    Cristian Amarie4-Mar-02 20:22 
    GeneralI don't understand Pin
    linghuchong15-Jan-01 2:10
    linghuchong15-Jan-01 2:10 
    QuestionCan you tell me? Pin
    linghuchong12-Jan-01 19:57
    linghuchong12-Jan-01 19:57 
    GeneralThis would be cool if it worked. Pin
    Jim A. Johnson9-Jan-01 7:57
    Jim A. Johnson9-Jan-01 7:57 
    GeneralRe: This would be cool if it worked. Pin
    2-Jul-01 2:06
    suss2-Jul-01 2:06 
    GeneralRe: This would be cool if it worked. Pin
    Volki12-Mar-02 14:39
    Volki12-Mar-02 14:39 
    QuestionHow to register my own DLL! Pin
    william21-Aug-00 11:44
    william21-Aug-00 11:44 
    Dear Sir.

    I have my own DLL(TSshellexD.dll), and after I compiled the Dll, it can run on my PC, but when I move the DLL to other PC that didn't have VC++ compiler, and the DLL couldn't work. I used "regsvr32.exe" to register the DLL and it responds a error message to me "DllRegisterServer in C:\TEMP\TSshellexD.dll failed. Return code was: 0x80040154".
    How should I do that my dll can install in non installed VC++'s PC and it can work as well?
    If someone has good idea, tell me please.

    William
    AnswerRe: How to register my own DLL! Pin
    Sardaukar21-Aug-00 20:02
    Sardaukar21-Aug-00 20:02 
    QuestionDoes this work for Solidworks DLL? Pin
    happy31-Jul-00 18:20
    happy31-Jul-00 18:20 
    AnswerRe: Does this work for Solidworks DLL? Pin
    Sardaukar6-Aug-00 0:27
    Sardaukar6-Aug-00 0:27 
    GeneralVery nice! Pin
    Jonathan Gilligan10-Mar-00 8:31
    Jonathan Gilligan10-Mar-00 8:31 
    GeneralRe: Very nice! Pin
    Sardaukar11-Mar-00 23:42
    Sardaukar11-Mar-00 23:42 

    General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

    Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.