Explorer is not querying IContextMenu interface from my ContextMenuHandler

45 views Asked by At

I'm trying to create COM InprocServer from scratch and make the sample ContextMenuHandler for Explorer work by showing a simple MessageBox. The problem is, that after querying for IShellExtInit (I actually correctly receive the selected filename, no problems) the IContextMenu interface is never queried by the Explorer! I know it, because I use ample debug MessageBoxes in the code, to see what is going on.

I know for sure that IContextMenu is never queried. Instead, Explorer queries for this 2 random GUIDs after the IShellExtInit, 2 times each:

{79EAC9EE-BAF9-11CE-8C82-0AA04BA9B}
{FC4801A3-2BA9-11CF-A229-0AA03D7352}
{79EAC9EE-BAF9-11CE-8C82-0AA04BA9B}
{FC4801A3-2BA9-11CF-A229-0AA03D7352}

I can't find this GUIDs anywhere. Not on the Internet, not in the Registry, and, of course, not in the ShlGuid.h. What can be the cause of the problem here?

Full code of the implementation:

#include "exports.h"

#include <shellapi.h>
#include <shobjidl.h>
#include <objbase.h>
#include <objidl.h>
#include <shlguid.h>
//#include <winnls.h>

#pragma warning( disable : 4100 )



wchar_t dbbuf[1024];




// {9E5FC5BF-6040-4F4E-B2A8-B1E9B9927651}
static const GUID CLSID_DragAndDropHlink =
    { 0x9E5FC5BF, 0x6040, 0x4F4E, { 0xB2, 0xA8, 0xB1, 0xE9, 0xB9, 0x92, 0x76, 0x51 }};

class DragAndDropHlink :
    public IShellExtInit,
    public IContextMenu
{
public:
    DragAndDropHlink();
    ~DragAndDropHlink();

    // IUknown Implementation
    HRESULT QueryInterface(REFIID riid, void **ppo);
    ULONG AddRef();
    ULONG Release();

    // IShellExtInit Implementation
    HRESULT Initialize(LPCITEMIDLIST pfid, IDataObject *pdo, HKEY hk);

    // IContextMenu Implementation
    HRESULT GetCommandString(UINT_PTR cmd_id, UINT type, UINT *reserved, CHAR *name, UINT buff_s);
    HRESULT InvokeCommand(CMINVOKECOMMANDINFO *pici);
    HRESULT QueryContextMenu(HMENU menu, UINT menu_idx, UINT fst_cmd_id, UINT lst_cmd_id, UINT flags);

public:
    static ui64 objs_alive;

private:
    ULONG ref_c;
    wchar_t file_n[MAX_PATH];
};

ui64 DragAndDropHlink::objs_alive = 0;

DragAndDropHlink::DragAndDropHlink()
{
    ref_c = 1;
    ++objs_alive;
}

DragAndDropHlink::~DragAndDropHlink()
{
    --objs_alive;
}

HRESULT DragAndDropHlink::QueryInterface(REFIID riid, void **ppo)
{
    if(ppo == NULL)
    {
        return E_INVALIDARG;
    }

    *ppo = NULL;

    if(riid == IID_IUnknown     ||
        riid == IID_IShellExtInit   ||
        riid == IID_IContextMenu)
    {
        *ppo = (void *)this;
        AddRef();

        MessageBox(NULL, L"DragAndDropHlink", L"QueryInterfaceOK!!!", MB_OK);

        return S_OK;
    }



    wsprintf(dbbuf, L"{%X-%X-%X-%X%X-%X%X%X%X%X%X}", riid.Data1, riid.Data2, riid.Data3, riid.Data4[0],
        riid.Data4[1], riid.Data4[2], riid.Data4[3], riid.Data4[4], riid.Data4[5], riid.Data4[6], riid.Data4[7]);

    ////////////////////////////////////// UNICODE
    //wchar_t buf[100];
    //wsprintf(buf, L"%d", id);

    SIZE_T l = wcslen(dbbuf) + 1;
    HGLOBAL hmem = GlobalAlloc(GMEM_MOVEABLE, l * sizeof(wchar_t));
    memcpy(GlobalLock(hmem), dbbuf, l * sizeof(wchar_t));
    GlobalUnlock(hmem);
    OpenClipboard(NULL);
    EmptyClipboard(); // Using this with OpenClipboard(NULL) should not work, but it does! Magick?
    SetClipboardData(CF_UNICODETEXT, hmem);
    CloseClipboard();

    //VirtualFree(big_buf, 0, MEM_RELEASE);
    /////////////////////////////////////////////////////////////////////////////////////////////////////////






    MessageBox(NULL, dbbuf, L"QueryInterfaceFAIL!!!", MB_OK);

    return E_NOINTERFACE;
}

inline ULONG DragAndDropHlink::AddRef()
{
    InterlockedIncrement(&ref_c);


    wsprintf(dbbuf, L"ref_c[%ld]", ref_c);
    MessageBox(NULL, dbbuf, L"DragAndDropHlink AddRef!!!", MB_OK);


    return ref_c;
}

inline ULONG DragAndDropHlink::Release()
{
    ULONG new_ref_c = InterlockedDecrement(&ref_c);

    wsprintf(dbbuf, L"ref_c[%ld]", ref_c);
    MessageBox(NULL, dbbuf, L"DragAndDropHlink Release!!!", MB_OK);

    if(ref_c == 0)
    {
        delete this;
    }

    return new_ref_c;
}

HRESULT DragAndDropHlink::Initialize(LPCITEMIDLIST pfid, IDataObject *pdo, HKEY hk)
{
    if(pdo == NULL)
    {
        return E_INVALIDARG;
    }

    MessageBox(NULL, L"DragAndDropHlink", L"Initialize!!!", MB_OK);

    STGMEDIUM med;
    FORMATETC fe = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
    UINT cnt = 0;

    if(pdo->GetData(&fe, &med) == S_OK)
    {
        cnt = DragQueryFile((HDROP)med.hGlobal, (UINT)-1, NULL, 0);
        if(cnt > 0)
        {
            DragQueryFile((HDROP)med.hGlobal, 0, file_n, MAX_PATH);
        }

        ReleaseStgMedium(&med);
    }

    MessageBox(NULL, file_n, L"Initialize END!!!", MB_OK);

    //RegOpenKeyEx(hk, nullptr, 0, MAXIMUM_ALLOWED, &some_hkey_var);

    return S_OK;
}

HRESULT DragAndDropHlink::GetCommandString(UINT_PTR cmd_id, UINT type, UINT *reserved, CHAR *name, UINT buff_s)
{
    if(cmd_id != 0)
    {
        return E_INVALIDARG;
    }

    MessageBox(NULL, L"DragAndDropHlink", L"GetCommandString!!!", MB_OK);
    
    // Ignore the help string pleads
    return E_INVALIDARG;
}

HRESULT DragAndDropHlink::InvokeCommand(CMINVOKECOMMANDINFO *pici)
{
    MessageBox(NULL, L"DragAndDropHlink", L"InvokeCommandBEG!!!", MB_OK);
    
    ui64 verb = (ui64)pici->lpVerb;
    if((verb & 0xFFFFFFFF00000000) != 0)
    {
        return E_INVALIDARG;
    }

    MessageBox(NULL, L"DragAndDropHlink", L"InvokeCommand 173 line!!!", MB_OK);

    switch(verb)
    {
    case 0:
        MessageBox(pici->hwnd, file_n, L"BOOOO!!!", MB_OK);
        return S_OK;
    default:
        return E_INVALIDARG;
    }
}

HRESULT DragAndDropHlink::QueryContextMenu(HMENU menu, UINT menu_idx, UINT fst_cmd_id, UINT lst_cmd_id, UINT flags)
{
    if(flags & CMF_DEFAULTONLY)
    {
        return 0;
    }

    MessageBox(NULL, L"NONSENSE!", L"QueryContextMenu!!!", MB_OK);

    InsertMenu(menu, menu_idx, MF_BYPOSITION, fst_cmd_id, L"MSGBOX SHOvER!");
    return 1;
}




class ComObjectFactory : public IClassFactory
{
public:
    ComObjectFactory();
    ~ComObjectFactory();

    // IUknown Implementation
    HRESULT QueryInterface(REFIID riid, void **ppo);
    ULONG AddRef();
    ULONG Release();

    // IClassFactory Implementation
    HRESULT CreateInstance(IUnknown *paggr, REFIID riid, void **ppo);
    HRESULT LockServer(BOOL lock);

public:
    static ui64 s_locks; // Server Locks count

private:
    ULONG ref_c;
};

ui64 ComObjectFactory::s_locks = 0;

ComObjectFactory::ComObjectFactory()
{
    ref_c = 1;
}

ComObjectFactory::~ComObjectFactory()
{
    // Do nothing
}

HRESULT ComObjectFactory::QueryInterface(REFIID riid, void **ppo)
{
    if(ppo == NULL)
    {
        return E_INVALIDARG;
    }

    MessageBox(NULL, L"NONSENSE!", L"ComObjectFactoryQueryInterface!!!", MB_OK);

    *ppo = NULL;

    if(riid == IID_IUnknown || riid == IID_IClassFactory)
    {
        *ppo = (void *)this;
        AddRef();
        return S_OK;
    }

    return E_NOINTERFACE;
}

inline ULONG ComObjectFactory::AddRef()
{
    InterlockedIncrement(&ref_c);
    MessageBox(NULL, L"NONSENSE!", L"ComObjectFactoryAddRef!!!", MB_OK);
    return ref_c;
}

inline ULONG ComObjectFactory::Release()
{
    ULONG new_ref_c = InterlockedDecrement(&ref_c);

    wsprintf(dbbuf, L"ref_c[%ld]", ref_c);
    MessageBox(NULL, dbbuf, L"ComObjectFactoryRelease!!!", MB_OK);
    if(ref_c == 0)
    {
        delete this;
    }

    return new_ref_c;
}

HRESULT ComObjectFactory::CreateInstance(IUnknown *paggr, REFIID riid, void **ppo)
{
    MessageBox(NULL, L"before CLASS_E_NOAGGREGATION!", L"CreateInstance!!!", MB_OK);
    
    if(paggr != NULL) // Ignore aggregates
    {
        return CLASS_E_NOAGGREGATION;
    }

    MessageBox(NULL, L"NONSENSE!", L"ComObjectFactoryCreateInstance!!!", MB_OK);

    DragAndDropHlink *dndhl = new DragAndDropHlink();
    if(dndhl == NULL)
    {
        return E_OUTOFMEMORY;
    }

    HRESULT res = dndhl->QueryInterface(riid, ppo);
    dndhl->Release();

    return res;
}

HRESULT ComObjectFactory::LockServer(BOOL lock)
{
    if(lock)
    {
        ++s_locks;
    }
    else
    {
        --s_locks;
    }

    MessageBox(NULL, L"NONSENSE!", L"ComObjectFactoryLockServer!!!", MB_OK);

    CoLockObjectExternal(this, lock, FALSE); // May be wrong =\

    return S_OK;
}




BOOL APIENTRY DllMain(
    _In_    HMODULE hm,         // "Handle" to "Module" in fact its base adress of DLL
    _In_    DWORD reason,       // Reason for calling this function by the OS
    _In_    LPVOID reserved)    // Dynamic/Statc link flag or FreeLibrary/Process term.
{
    switch(reason)
    {
    case DLL_PROCESS_ATTACH:
        //MessageBox(NULL, L"NONSENSE!", L"DLL_PROCESS_ATTACH!!!", MB_OK);
        break;
    case DLL_THREAD_ATTACH:
        break;
    case DLL_THREAD_DETACH:
        break;
    case DLL_PROCESS_DETACH:
        break;
    default:
        break;
    }
    return TRUE;
}

HRESULT DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppo)
{
    wsprintf(dbbuf, L"riid[%ld]", riid);
    MessageBox(NULL, dbbuf, L"DllGetClassObject!!!", MB_OK);
    
    
    if(rclsid != CLSID_DragAndDropHlink)
    {
        return CLASS_E_CLASSNOTAVAILABLE;
    }

    ComObjectFactory *f = new ComObjectFactory();
    if(f == NULL)
    {
        return E_OUTOFMEMORY;
    }

    HRESULT res = f->QueryInterface(riid, ppo);
    f->Release();

    wsprintf(dbbuf, L"res[%ld] ppo[%lX]", res, ppo);
    MessageBox(NULL, dbbuf, L"f->QueryInterface!!!", MB_OK);

    return res;
}

HRESULT DllCanUnloadNow()
{
    
    wsprintf(dbbuf, L"objs_alive[%ld]", DragAndDropHlink::objs_alive);
    MessageBox(NULL, dbbuf, L"DllCanUnloadNow!!!", MB_OK);
    
    return DragAndDropHlink::objs_alive == 0 ? S_OK : S_FALSE;
}

Update 0: I just managed to actually find the mysterious GUIDs in the registry.

{79EAC9EE-BAF9-11CE-8C82-0AA04BA9B} is IInternetSecurityManager

{FC4801A3-2BA9-11CF-A229-0AA03D7352} is IObjectWithSite

Still this does not make any sense at all. Why would file manager need this interfaces in the add-on?

Update 1: I checked what interfaces are actually queried for my class, and, actually, Explorer DOES query IContextMenu interface 2 times in a row, but, strangely, before calling IShellExtInit, which is kind of counter-logical. Still, it never calls the actual methods defined in IContextMenu, therefore menu item is never added... This case becoming more and more phantasmagorical...

Oh, and I also "implemented" IInternetSecurityManager and IObjectWithSite, just by returning E_INVALIDARG and displaying MessageBox, but Explorer actually never called any methods from this interfaces anyway, so it's not seem to be the source of the IContextMenu problem. Also, after getting the IObjectWithSite, Explorer never asked for IInternetSecurityManager.

Update 2: I tested it on different computer, did explorer reloads, reboots. Same results.

0

There are 0 answers