Search code examples
comregistryuacatl

Why does ATL COM registration defaults to HKCR


When creating an COM object through ATL, the default .rgs file always registers the object to HKCR:

HKCR
{
...
}

If this object is being written to the registry for the first time, writing to HKCR is equivalent to writing to HKLM, which would make the object globally visible to all users.

  1. This seems like a rather poor idea from the stand point of registry maintenance since the HKCR/HKLM will quickly blow up with any COM objects installed by any user on the machine and thus slowing down access for all the users. In addition, any deployment module including COM objects will have to request for admin elevation on Windows with UAC, which would adversely affect deployability. So why is ATL/COM designed this way?

  2. This article suggests that in order to register a COM object under HKCU (http://blogs.msdn.com/b/jaredpar/archive/2005/05/29/423000.aspx), DllRegisterServer must be updated. Why wouldn't it work if we simply changed HKCR to HKCU in the .rgs file? Would ATL simply ignore it? If that's the case, why does it insist on using HKCR?


Solution

  • Registration through DllRegisterServer is expected to provide system wide registration and COM class availability by design.

    To supersede this registration behavior and register per-user, at some point another registration method was introduced: DllInstall function. Current ATL implements this out of the box offering per-user registration with "user" command line switch provided to the DllInstall function.

    Below is code you have on new project generated from template (using wizard):

    // DllInstall - Adds/Removes entries to the system registry per user per machine.
    STDAPI DllInstall(BOOL bInstall, _In_opt_  LPCWSTR pszCmdLine)
    {
        HRESULT hr = E_FAIL;
        static const wchar_t szUserSwitch[] = L"user";
        if (pszCmdLine != NULL)
        {
            if (_wcsnicmp(pszCmdLine, szUserSwitch, _countof(szUserSwitch)) == 0)
            {
                ATL::AtlSetPerUserRegistration(true);
            }
        }
        if (bInstall)
        {   
            hr = DllRegisterServer();
            if (FAILED(hr))
            {
                DllUnregisterServer();
            }
        }
        else
        {
            hr = DllUnregisterServer();
        }
        return hr;
    }
    

    To register per-user you can use "regsvr32 /i:user MyAtlProject.dll". You are free to choose the registration you want, there are no "poor" and "good" methods - you just have options to choose from.