Search code examples
c++windowscom

How to register a COM (Component Object Model) in Windows? (for Icon Overlay Handler)


I am quite inexperienced with COM objects, following is what I did:

  • In C++, I created a class which extend IUnknow and implements QueryInterface, AddRef and Release.
  • Extend IShellIconOverlayIdentifier and implements the 3 methods inside.
  • Export the class with __declspec(dllexport)
  • The DLL is compiled with MSVC2015 64 bits. (working on a windows7 64bits).

Now comes the difficult part:

How to make windows to link/use the DLL:

How to register and how to check if registered a COM dll?

Is there compilation restrictions like compiler/architecture?

EDITED Simplified:

It seem that I had a very incomplete vision of COM implementation, and in order to register the COM object, 2 directions are possible:

  • Register manually the object in the register, which is a nightmare.
  • Implement DllRegisterServer and DllUnregisterServer: which is a nightmare also.

To simplify the problem, I removed everything else up to having only those 2 functions:

STDAPI DllRegisterServer(void)
{
    return NOERROR;
}

STDAPI DllUnregisterServer(void)
{
    return NOERROR;
}

Unfortunately, this is not exported to the DLL, (as no __declspec(dllexport) is specified). As soon as I try to add this declaration, the compiler complain and no DLL is generated.

SOLUTION STEP 1:

Olectl.h is very very bad and avoid DllRegisterServer to be exported.


Solution

  • In order to create a COM object manually (without wizard), following steps are required:

    • Implement DllGetClassObject & DllCanUnloadNow
    • Implement Factory extending IClassFactory
    • Implement the Target class, extending IUknown
    • Register the COM in the registry
    • [optional] implement DllRegisterServer & DllUnregisterServer to make registry easier.

    Implements DllGetClassObject & DllCanUnloadNow

    Those are the only exported to the DLL functions:

    STDAPI DllGetClassObject(_In_ REFCLSID rclsid, _In_ REFIID riid, _Outptr_ LPVOID * ppObject)
    STDAPI DllCanUnloadNow(void)
    

    DllGetClassObject, if the provided rclsid is the TargetClassCLSID and riid is the `FactoryCLSID, it creates and return a Factory object.

    DllCanUnloadNow return if the DLL is in use (S_OK) or not, in which case the user/client may unload the DLL (S_FALSE).

    The problem is, the definition do not allows to add __declspec(dllexport) due probably to some Microsoft monopol directive to make VS "mandatory".

    To export them, just add before

    #pragma comment( linker, "/export:DllGetClassObject,PRIVATE" )
    #pragma comment( linker, "/export:DllCanUnloadNow,PRIVATE" )
    //TODO: make this cross-compiler compatible?
    

    Implementing the Factory

    Implements AddRef and Release: they maintain how many client/users are currently using this Factory object. AddRef increment it by 1, Release decrements it by 1. As soon as the number is 0, Releasemay auto delete itself.

    Implements QueryInterface which return if riidis the FactoryCLSID.

    Implements CreateInstance, which return an instance of the TargetObject if riid match TargetClassCLSID

    Implementing the Target Class

    Again, we have to implements AddRef, Release and QueryInterface, but for the final class this time.

    In my case, this Target class extends IShellIconOverlayIdentifier.

    Register the COM object into the OS registry

    After generating the dll, several registry need to be set. Some of them are linked together, so adding one may automatically add another.

    HKEY_CLASS_ROOT::CLSID::<Add Here>
    HKEY_CLASS_ROOT::Wow6432Node::CLSID::<Add Here>
    HKEY_LOCAL_MACHINE::SOFTWARE::Classes::CLSID::<Add Here>
    HKEY_LOCAL_MACHINE::SOFTWARE::Classes::Wow6432Node::CLSID::<Add Here>
    HKEY_LOCAL_MACHINE::SOFTWARE::Wow6432Node::Classes::CLSID::<Add Here>
    

    Content to add is:

    • Create a key: name={TargetClassCLSID}, value=<Any app name>
    • Create a sub-key: name=InProcServer32, value=<link to your dll>
    • Create a sub-string of InProcServer32: name=ThreadingModel, value=Apartment

    //TODO: probably there are other alternatives, to better understand what that mean.

    In my case, I had to register some more registers for the IconHandler:

    HKEY_LOCAL_MACHINE::SOFTWARE::Microsoft::Windows::CurrentVersion::Explorer::ShellIconOverlayIdentifier::<Overlay here>
    HKEY_LOCAL_MACHINE::SOFTWARE::Microsoft::Windows::CurrentVersion::ShellExtensions::Aproved::<new entry here>
    

    Where the overlay entry is a key: name=<Any app name>, value=<TargetClassCLSID>

    The new entry is a string value which name=<TargetClassCLSID>and value=Any app name

    Those values need also to be in:

    HKEY_LOCAL_MACHINE::SOFTWARE::Wow6432Node::Microsoft::Windows....