从Shell扩展DLL直接调用IContextMenu失败的解决方法求助
Shell扩展的COM对象不会直接暴露IContextMenu接口,这类对象需要先通过持久化接口(比如IPersistFile)完成与目标对象(文件/文件夹)的绑定初始化后,才会提供IContextMenu相关接口。你的代码跳过了初始化步骤,直接请求IContextMenu,所以返回E_NOINTERFACE。
正确的实现步骤:
- 创建COM实例后,先查询
IPersistFile接口 - 调用
IPersistFile::Load()传入目标文件路径,完成对象初始化 - 初始化完成后,再从实例查询
IContextMenu(或IContextMenu2/3)接口
修改后的代码如下:
#include <iostream> #include <combaseapi.h> #include <shobjidl_core.h> #include <shlwapi.h> #pragma comment(lib, "shlwapi.lib") typedef HRESULT(WINAPI* DllGetClassObjectType)(REFCLSID rclsid, REFIID riid, void** ppv); int main() { HRESULT hr = CoInitialize(NULL); if (FAILED(hr)) { std::cout << "CoInitialize ERR " << hr; return 0; } HMODULE hModule = LoadLibrary(L"C:\\Users\\UserName\\Desktop\\NppShell.dll"); if (!hModule) { std::cout << "LoadLibrary ERR " << GetLastError(); CoUninitialize(); return 0; } DllGetClassObjectType DllGetClassObject = (DllGetClassObjectType)GetProcAddress(hModule, "DllGetClassObject"); if (!DllGetClassObject) { std::cout << "DllGetClassObject ERR " << GetLastError(); FreeLibrary(hModule); CoUninitialize(); return 0; } CLSID clsid; hr = CLSIDFromString(L"{B298D29A-A6ED-11DE-BA8C-A68E55D89593}", &clsid); if (FAILED(hr)) { std::cout << "CLSIDFromString ERR " << hr; FreeLibrary(hModule); CoUninitialize(); return 0; } IClassFactory* iFactory = nullptr; hr = DllGetClassObject(clsid, IID_IClassFactory, (void**)&iFactory); if (FAILED(hr)) { std::cout << "DllGetClassObject ERR " << hr; FreeLibrary(hModule); CoUninitialize(); return 0; } // 先创建基础实例,不直接请求IContextMenu IUnknown* pUnk = nullptr; hr = iFactory->CreateInstance(NULL, IID_IUnknown, (void**)&pUnk); if (FAILED(hr)) { std::cout << "CreateInstance ERR " << hr; iFactory->Release(); FreeLibrary(hModule); CoUninitialize(); return 0; } iFactory->Release(); // 查询IPersistFile接口 IPersistFile* pPersistFile = nullptr; hr = pUnk->QueryInterface(IID_IPersistFile, (void**)&pPersistFile); if (FAILED(hr)) { std::cout << "Query IPersistFile ERR " << hr; pUnk->Release(); FreeLibrary(hModule); CoUninitialize(); return 0; } // 绑定到目标文件,完成初始化 LPCWSTR pszFilePath = L"C:\\test.txt"; // 替换为你要测试的文件路径 hr = pPersistFile->Load(pszFilePath, STGM_READ); if (FAILED(hr)) { std::cout << "IPersistFile::Load ERR " << hr; pPersistFile->Release(); pUnk->Release(); FreeLibrary(hModule); CoUninitialize(); return 0; } pPersistFile->Release(); // 现在可以查询IContextMenu接口了 IContextMenu* cm = nullptr; hr = pUnk->QueryInterface(IID_IContextMenu, (void**)&cm); if (SUCCEEDED(hr)) { // 成功获取,可以调用QueryContextMenu等方法 std::cout << "Success get IContextMenu" << std::endl; // 后续操作示例:调用QueryContextMenu HMENU hMenu = CreatePopupMenu(); if (hMenu) { hr = cm->QueryContextMenu(hMenu, 0, 1, 0x7FFF, CMF_NORMAL); if (SUCCEEDED(hr)) { // 可以显示菜单或处理其他逻辑 } DestroyMenu(hMenu); } cm->Release(); } else { std::cout << "Query IContextMenu ERR " << hr; } pUnk->Release(); FreeLibrary(hModule); CoUninitialize(); return 0; }
注意事项:
- 不同类型的Shell扩展可能需要不同的持久化接口:比如文件夹扩展用
IPersistFolder,而文件扩展用IPersistFile,NppShell属于文件右键扩展,所以用IPersistFile - 必须传入有效的目标文件路径,否则初始化失败,仍无法获取IContextMenu
- 记得正确释放COM对象和加载的DLL,避免内存泄漏
内容的提问来源于stack exchange,提问作者Joe J
相关产品推荐
相关产品推荐

