我正在尝试从头开始创建 COM InprocServer,并通过显示一个简单的
MessageBox
来使示例 ContextMenuHandler for Explorer 工作。问题是,在查询 IShellExtInit
后(我实际上正确接收了所选的文件名,没有问题),资源管理器永远不会查询 IContextMenu
界面!我知道这一点,因为我在代码中使用了充足的调试MessageBox
,以查看发生了什么。
我确信
IContextMenu
永远不会被查询。相反,资源管理器会在 IShellExtInit
之后查询这 2 个随机 GUID,每个查询 2 次:
{79EAC9EE-BAF9-11CE-8C82-0AA04BA9B}
{FC4801A3-2BA9-11CF-A229-0AA03D7352}
{79EAC9EE-BAF9-11CE-8C82-0AA04BA9B}
{FC4801A3-2BA9-11CF-A229-0AA03D7352}
我在任何地方都找不到这个 GUID。不在互联网上,不在注册表中,当然也不在
ShlGuid.h
中。这里的问题可能是什么原因造成的?
完整实现代码:
#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;
}
更新0:我刚刚设法在注册表中找到了神秘的GUID。
{79EAC9EE-BAF9-11CE-8C82-0AA04BA9B}
是 IInternetSecurityManager
{FC4801A3-2BA9-11CF-A229-0AA03D7352}
是 IObjectWithSite
但这根本没有任何意义。为什么文件管理器在插件中需要这个接口?
更新1:我检查了我的类实际查询了哪些接口,实际上,Explorer确实连续查询了
IContextMenu
接口2次,但是,奇怪的是,before调用IShellExtInit
,这是一种反逻辑的。尽管如此,它永远不会调用 IContextMenu
中定义的实际方法,因此永远不会添加菜单项......这种情况变得越来越梦幻......
哦,我还“实现”了
IInternetSecurityManager
和IObjectWithSite
,只需返回E_INVALIDARG
并显示MessageBox
,但资源管理器实际上从未从这个接口调用任何方法,所以它似乎不是IContextMenu
问题。而且,在得到 IObjectWithSite
后,Explorer 再也没有要求过 IInternetSecurityManager
。