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

如何无管理员权限移除IContextMenu中的Include in library菜单项

无管理员权限移除文件夹右键“Include in library”菜单项的方案

针对你遇到的问题——无法通过verb定位“Include in library”菜单项,且不想用管理员权限修改系统注册表,这里提供一个用户态的解决方案:枚举右键菜单的所有项,通过匹配菜单项文本直接移除,完全不需要系统级权限。


实现思路

Windows右键菜单由IContextMenu::QueryContextMenu生成,尽管该菜单项没有注册verb,但它会被添加到最终的菜单句柄(hMenu)中。我们只需要在菜单生成完成、弹出之前,执行以下步骤:

  1. 枚举目标菜单的所有菜单项
  2. 获取每个项的文本,匹配“Include in library”(中文系统对应“包含到库中”)
  3. 调用RemoveMenu移除匹配项

可以通过两种方式实现:一是开发自定义Shell扩展附加到文件夹右键菜单,二是使用全局窗口钩子拦截所有右键菜单事件。下面提供Shell扩展的测试代码,更易于验证。


测试代码(C++)

以下是一个最小化的Shell扩展DLL实现,专门用于移除文件夹右键菜单中的目标项:

#include <windows.h>
#include <shobjidl.h>
#include <shlwapi.h>
#include <strsafe.h>

#pragma comment(lib, "shlwapi.lib")
#pragma comment(lib, "ole32.lib")

// 自定义CLSID,替换为你自己生成的GUID
DEFINE_GUID(CLSID_FolderMenuRemover, 
    0x12345678, 0x1234, 0x1234, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef);

class CFolderMenuRemover : public IContextMenu, IShellExtInit {
private:
    LONG m_cRefCount;
    PIDLIST_ABSOLUTE m_pidlTargetFolder;

public:
    CFolderMenuRemover() : m_cRefCount(1), m_pidlTargetFolder(nullptr) {}
    ~CFolderMenuRemover() { if (m_pidlTargetFolder) ILFree(m_pidlTargetFolder); }

    // IUnknown 接口实现
    STDMETHODIMP QueryInterface(REFIID riid, void** ppvObject) {
        static const QITAB qit[] = {
            QITABENT(CFolderMenuRemover, IContextMenu),
            QITABENT(CFolderMenuRemover, IShellExtInit),
            { nullptr }
        };
        return QISearch(this, qit, riid, ppvObject);
    }

    STDMETHODIMP_(ULONG) AddRef() {
        return InterlockedIncrement(&m_cRefCount);
    }

    STDMETHODIMP_(ULONG) Release() {
        ULONG refCount = InterlockedDecrement(&m_cRefCount);
        if (refCount == 0) delete this;
        return refCount;
    }

    // IShellExtInit 接口实现:初始化时记录目标文件夹
    STDMETHODIMP Initialize(LPCITEMIDLIST pidlFolder, LPDATAOBJECT pDataObj, HKEY hKeyProgID) {
        if (pidlFolder) {
            m_pidlTargetFolder = ILClone(pidlFolder);
        }
        return S_OK;
    }

    // IContextMenu 接口实现:处理菜单生成
    STDMETHODIMP QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) {
        // 跳过默认菜单请求
        if (uFlags & CMF_DEFAULTONLY) return S_OK;

        // 枚举菜单,查找并移除目标项
        UINT menuItemCount = GetMenuItemCount(hMenu);
        for (UINT i = 0; i < menuItemCount; i++) {
            WCHAR szMenuItemText[256] = {0};
            if (GetMenuString(hMenu, i, szMenuItemText, ARRAYSIZE(szMenuItemText), MF_BYPOSITION)) {
                // 匹配英文文本,中文系统替换为 L"包含到库中"
                if (lstrcmpiW(szMenuItemText, L"Include in library") == 0) {
                    RemoveMenu(hMenu, i, MF_BYPOSITION);
                    break; // 假设仅存在一个目标项
                }
            }
        }

        // 返回0表示未添加新菜单项,仅修改现有菜单
        return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 0);
    }

    STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO pInvokeInfo) {
        // 本扩展不需要执行命令,直接返回未实现
        return E_NOTIMPL;
    }

    STDMETHODIMP GetCommandString(UINT_PTR idCmd, UINT uType, UINT* pReserved, LPSTR pszName, UINT cchMax) {
        return E_NOTIMPL;
    }
};

// 类工厂:用于创建扩展实例
class CMenuRemoverFactory : public IClassFactory {
private:
    LONG m_cRefCount;

public:
    CMenuRemoverFactory() : m_cRefCount(1) {}
    ~CMenuRemoverFactory() {}

    STDMETHODIMP QueryInterface(REFIID riid, void** ppvObject) {
        if (riid == IID_IClassFactory || riid == IID_IUnknown) {
            *ppvObject = static_cast<IClassFactory*>(this);
            AddRef();
            return S_OK;
        }
        return E_NOINTERFACE;
    }

    STDMETHODIMP_(ULONG) AddRef() {
        return InterlockedIncrement(&m_cRefCount);
    }

    STDMETHODIMP_(ULONG) Release() {
        ULONG refCount = InterlockedDecrement(&m_cRefCount);
        if (refCount == 0) delete this;
        return refCount;
    }

    STDMETHODIMP CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppvObject) {
        if (pUnkOuter) return CLASS_E_NOAGGREGATION;

        CFolderMenuRemover* pExtension = new CFolderMenuRemover();
        HRESULT hr = pExtension->QueryInterface(riid, ppvObject);
        pExtension->Release();
        return hr;
    }

    STDMETHODIMP LockServer(BOOL fLock) {
        return S_OK;
    }
};

// DLL全局变量
HINSTANCE g_hDllInstance;

// DLL入口函数
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
    switch (ul_reason_for_call) {
        case DLL_PROCESS_ATTACH:
            g_hDllInstance = 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** ppvObject) {
    if (!IsEqualCLSID(rclsid, CLSID_FolderMenuRemover)) {
        return CLASS_E_CLASSNOTAVAILABLE;
    }

    CMenuRemoverFactory* pFactory = new CMenuRemoverFactory();
    HRESULT hr = pFactory->QueryInterface(riid, ppvObject);
    pFactory->Release();
    return hr;
}

// 判断是否可卸载
STDAPI DllCanUnloadNow() {
    return S_OK;
}

验证步骤

  1. 编译DLL:使用Visual Studio编译上述代码为对应系统位数(32/64位)的DLL文件。
  2. 用户态注册扩展:
    • 打开注册表编辑器(regedit.exe),导航到HKCU\Software\Classes\Folder\ShellEx\ContextMenuHandlers
    • 新建一个项,命名为FolderMenuRemover,将其默认值设置为代码中定义的CLSID({12345678-1234-1234-1234-567890ABCDEF})
  3. 测试效果:右键点击任意文件夹,检查“Include in library”(或中文“包含到库中”)项是否已消失。
  4. 恢复原状:删除上述新建的注册表项即可,无需管理员权限。

关键注意事项

  • 本地化适配:如果是中文系统,需将代码中的匹配文本改为L"包含到库中";其他语言系统需对应修改为当地语言的菜单项文本。
  • 菜单枚举时机:必须在QueryContextMenu执行完成后、菜单弹出前执行移除操作,上述代码的时机是正确的。
  • 全局钩子替代方案:如果需要对所有文件管理器(包括第三方)生效,可以使用全局WH_GETMESSAGE钩子拦截WM_CONTEXTMENU消息,获取hMenu后执行相同的枚举移除逻辑,同样不需要管理员权限。

内容的提问来源于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 02:44:53