Search code examples
windowsshelldatahandler

COM: DllGetClassObject not called for DataHandler shell extension


I am trying to write a data handler shell extension. On Windows Dev Center it says "Data handler is called when a drag-and-drop operation is performed on dragShell objects. It enables you to provide additional clipboard formats to the drop target." And I am trying to get the selected file directory right after it has been copied or dragged.

I can see the GUID has been registered in registry, and DllMain and DllRegisterServer have been invoked while registry. But the DllGetClassObject has never been called by any process. I am expecting that explorer.exe will call it when I copy or drag a file.

I pretty sure I compile the dll under the x64 configuration since my OS is windows 7 64-bit. The CLSID is created using "create GUID" tool in VS 2010. After register, I can see the CLSID is under the subkey of DataHandler that is under shellex. Also the dll's full directory is in that CLSID registry entry-point. So I can not think of any reason why it does not load my dll.

Even though I can find some articles about shell extension but most of them are about other handlers. The only relevant one is on the link below:

http://msdn.microsoft.com/en-us/library/windows/desktop/cc144163(v=vs.85).aspx

Where am I wrong? Any help will be appreciated!!

Here is the code in DllMain

// {A097432A-44BE-44FC-AF1D-A012B65392F5}
static const GUID CLSID_DragObjectInfoExt = 
{ 0xA097432A, 0x44BE, 0x44FC, { 0xAF, 0x1D, 0xA0, 0x12, 0xB6, 0x53, 0x92, 0xF5 } };

HINSTANCE   g_hInst     = NULL;
long        g_cDllRef   = 0;


BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved)
{
    MessageBox(NULL, L"DllMain", L"QueryDragObject", MB_OK);
    switch (dwReason)
    {
    case DLL_PROCESS_ATTACH:
        g_hInst = hModule;
        DisableThreadLibraryCalls(hModule);
        break;
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppv)
{
    MessageBox(NULL, L"DllGetClassObject", L"QueryDragObject", MB_OK);
    HRESULT hr = CLASS_E_CLASSNOTAVAILABLE;

    if (IsEqualCLSID(CLSID_DragObjectInfoExt, rclsid))
    {
        hr = E_OUTOFMEMORY;

        ClassFactory *pClassFactory = new ClassFactory();
        if (pClassFactory)
        {
            hr = pClassFactory->QueryInterface(riid, ppv);
            pClassFactory->Release();
        }
    }

    return hr;
}

STDAPI DllCanUnloadNow(void)
{
    MessageBox(NULL, L"DllCanUnloadNow", L"QueryDragObject", MB_OK);
    return g_cDllRef > 0 ? S_FALSE : S_OK;
}

STDAPI DllRegisterServer(void)
{
/*  while(true) {
        Sleep(100);
    }*/
    MessageBox(NULL, L"DllRegisterServer", L"QueryDragObject", MB_OK);

    HRESULT hr;

    wchar_t szModule[MAX_PATH];
    if (GetModuleFileName(g_hInst, szModule, ARRAYSIZE(szModule)) == 0)
    {
        hr = HRESULT_FROM_WIN32(GetLastError());
        return hr;
    }

    // Register the component.
    hr = RegisterInprocServer(szModule, CLSID_DragObjectInfoExt, 
        L"TxtShellExtDragObjectHandler.DragObjectInfoExt Class", // Friendly name
        L"Apartment"); // Threading model
    if (SUCCEEDED(hr))
    {
        hr = RegisterShellExtDragInfoHandler(L"*", 
            CLSID_DragObjectInfoExt, 
            L"TxtShellExtDragObjectHandler.DragObjectInfoExt");
    }
    else
    {
        MessageBox(NULL, L"DllRegisterServer failed", L"DragObjectInfoHandler", MB_OK);
    }

    return hr;
}

STDAPI DllUnregisterServer(void)
{
    MessageBox(NULL, L"DllUnregisterServer", L"QueryDragObject", MB_OK);
    HRESULT hr = S_OK;

    wchar_t szModule[MAX_PATH];
    if (GetModuleFileName(g_hInst, szModule, ARRAYSIZE(szModule)) == 0)
    {
        hr = HRESULT_FROM_WIN32(GetLastError());
        return hr;
    }

    // Unregister the component.
    hr = UnregisterInprocServer(CLSID_DragObjectInfoExt);
    if (SUCCEEDED(hr))
    {
        // Unregister the context menu handler.
        hr = UnregisterShellExtDragInfoHandler(L"*", 
            CLSID_DragObjectInfoExt);
    }
    else
    {
        MessageBox(NULL, L"DllUnregisterServer failed", L"DragObjectInfoHandler", MB_OK);
    }

    return hr;
}

Solution

  • Finally figure out where I was doing wrong.

    I used the reg.h/cpp files in ContextMenu example and only modified a few places. In fact the registry tree structures for ContextMenuHandlers and DataHandler are a little bit different. The correct structure should look like this:

    HKEY_CLASSES_ROOT
       .fileType
          (Default) = MyProgram.1
       CLSID
          {00000000-1111-2222-3333-444444444444}
             InProcServer32
                (Default) = C:\MyDir\MyCommand.dll
                ThreadingModel = Apartment
       MyProgram.1
          (Default) = MyProgram Application
          Shellex
             DataHandler
                (Default) = {00000000-1111-2222-3333-444444444444}
    

    And before it was like:

    HKEY_CLASSES_ROOT
       .fileType
          (Default) = MyProgram.1
       CLSID
          {00000000-1111-2222-3333-444444444444}
             InProcServer32
                (Default) = C:\MyDir\MyCommand.dll
                ThreadingModel = Apartment
       MyProgram.1
          (Default) = MyProgram Application
          Shellex
             DataHandler
                {00000000-1111-2222-3333-444444444444} = 
                'Friendly Name'
    

    Hopefully this post could prevent other people from making the same mistake.