You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

从Shell扩展DLL直接调用IContextMenu失败的解决方法求助

直接调用Shell扩展DLL的IContextMenu时E_NOINTERFACE错误的解决方法

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 03:55:56