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

求可收集Windows第三方右键菜单项及处理程序的C++实现方案

获取Windows第三方右键菜单项及处理程序的C++实现

Windows右键菜单项主要存储在注册表的多个位置,部分第三方应用会通过**Shell扩展(COM组件)**动态添加菜单项,这类项不会直接显示文本字符串,而是通过CLSID指向对应的组件,这也是你搜索Notepad++文本却找不到的原因之一。以下是实现思路和完整代码:

一、关键注册表位置

右键菜单项分为两类,对应不同注册表路径:

  1. 普通静态菜单项
    • 所有文件:HKEY_CLASSES_ROOT\*\shell
    • 文件夹:HKEY_CLASSES_ROOT\Directory\shell、HKEY_CLASSES_ROOT\Folder\shell
    • 指定文件类型:HKEY_CLASSES_ROOT\<扩展名>\shell(如.txt\shell)
  2. Shell扩展菜单项
    • 所有文件:HKEY_CLASSES_ROOT\*\shellex\ContextMenuHandlers
    • 文件夹:HKEY_CLASSES_ROOT\Directory\shellex\ContextMenuHandlers
    • 指定文件类型:HKEY_CLASSES_ROOT\<扩展名>\shellex\ContextMenuHandlers

二、C++实现代码

以下代码会遍历指定注册表路径,收集菜单项名称和对应的处理程序路径,同时支持解析Shell扩展的CLSID:

#include <Windows.h>
#include <Shlwapi.h>
#include <vector>
#include <string>
#include <iostream>

#pragma comment(lib, "Shlwapi.lib")

// 展开环境变量(如%ProgramFiles%)
BOOL ExpandEnvironmentStringsWrapper(LPCWSTR lpSrc, std::wstring& lpDst)
{
    DWORD bufSize = ExpandEnvironmentStringsW(lpSrc, nullptr, 0);
    if (bufSize == 0) return FALSE;

    lpDst.resize(bufSize);
    bufSize = ExpandEnvironmentStringsW(lpSrc, &lpDst[0], bufSize);
    if (bufSize == 0) return FALSE;

    lpDst.resize(bufSize - 1); // 去掉末尾的NULL字符
    return TRUE;
}

// 根据CLSID获取Shell扩展处理程序路径
BOOL GetShellExHandlerPath(LPCWSTR lpCLSID, std::wstring& handlerPath)
{
    std::wstring clsidPath = L"CLSID\\";
    clsidPath += lpCLSID;
    clsidPath += L"\\InprocServer32"; // 优先找进程内组件,找不到再试LocalServer32

    HKEY hKey;
    LONG lResult = RegOpenKeyExW(HKEY_CLASSES_ROOT, clsidPath.c_str(), 0, KEY_READ, &hKey);
    if (lResult != ERROR_SUCCESS)
    {
        clsidPath = L"CLSID\\";
        clsidPath += lpCLSID;
        clsidPath += L"\\LocalServer32";
        lResult = RegOpenKeyExW(HKEY_CLASSES_ROOT, clsidPath.c_str(), 0, KEY_READ, &hKey);
        if (lResult != ERROR_SUCCESS) return FALSE;
    }

    WCHAR szPath[MAX_PATH] = {0};
    DWORD dwSize = sizeof(szPath);
    lResult = RegQueryValueExW(hKey, nullptr, nullptr, nullptr, (LPBYTE)szPath, &dwSize);
    RegCloseKey(hKey);

    if (lResult != ERROR_SUCCESS) return FALSE;

    return ExpandEnvironmentStringsWrapper(szPath, handlerPath);
}

// 遍历指定注册表路径下的所有右键菜单项
BOOL EnumShellMenuItems(LPCWSTR lpRegPath, std::vector<std::pair<std::wstring, std::wstring>>& menuItems)
{
    HKEY hKey;
    LONG lResult = RegOpenKeyExW(HKEY_CLASSES_ROOT, lpRegPath, 0, KEY_READ, &hKey);
    if (lResult != ERROR_SUCCESS) return FALSE;

    WCHAR szSubKey[MAX_PATH] = {0};
    DWORD dwSubKeySize = MAX_PATH;
    DWORD dwIndex = 0;

    while (RegEnumKeyExW(hKey, dwIndex++, szSubKey, &dwSubKeySize, nullptr, nullptr, nullptr, nullptr) == ERROR_SUCCESS)
    {
        std::wstring itemName = szSubKey;

        // 读取菜单项显示文本(默认值)
        WCHAR szDisplayName[MAX_PATH] = {0};
        DWORD dwDisplayNameSize = sizeof(szDisplayName);
        lResult = RegQueryValueExW(hKey, szSubKey, nullptr, nullptr, (LPBYTE)szDisplayName, &dwDisplayNameSize);
        if (lResult == ERROR_SUCCESS && wcslen(szDisplayName) > 0)
        {
            itemName = szDisplayName;
        }

        // 判断是普通菜单项还是Shell扩展
        std::wstring commandPath = lpRegPath;
        commandPath += L"\\";
        commandPath += szSubKey;
        commandPath += L"\\command";

        HKEY hCmdKey;
        lResult = RegOpenKeyExW(HKEY_CLASSES_ROOT, commandPath.c_str(), 0, KEY_READ, &hCmdKey);
        if (lResult == ERROR_SUCCESS)
        {
            // 普通菜单项:读取command值
            WCHAR szCmd[MAX_PATH] = {0};
            DWORD dwCmdSize = sizeof(szCmd);
            lResult = RegQueryValueExW(hCmdKey, nullptr, nullptr, nullptr, (LPBYTE)szCmd, &dwCmdSize);
            RegCloseKey(hCmdKey);

            if (lResult == ERROR_SUCCESS)
            {
                std::wstring expandedCmd;
                if (ExpandEnvironmentStringsWrapper(szCmd, expandedCmd))
                {
                    menuItems.emplace_back(itemName, expandedCmd);
                }
            }
        }
        else
        {
            // 尝试Shell扩展路径
            std::wstring shellExPath = lpRegPath;
            shellExPath += L"\\";
            shellExPath += szSubKey;

            WCHAR szCLSID[MAX_PATH] = {0};
            DWORD dwCLSIDSize = sizeof(szCLSID);
            lResult = RegQueryValueExW(hKey, szSubKey, nullptr, nullptr, (LPBYTE)szCLSID, &dwCLSIDSize);
            if (lResult == ERROR_SUCCESS)
            {
                std::wstring handlerPath;
                if (GetShellExHandlerPath(szCLSID, handlerPath))
                {
                    menuItems.emplace_back(itemName, handlerPath);
                }
            }
        }

        dwSubKeySize = MAX_PATH;
        ZeroMemory(szSubKey, sizeof(szSubKey));
    }

    RegCloseKey(hKey);
    return TRUE;
}

// 获取指定文件的类型关联(如.txt对应txtfile)
BOOL GetFileType(LPCWSTR lpFilePath, std::wstring& fileType)
{
    WCHAR szFileType[MAX_PATH] = {0};
    if (!AssocQueryStringW(ASSOCF_NONE, ASSOCSTR_CLASS, lpFilePath, nullptr, szFileType, nullptr))
    {
        fileType = szFileType;
        return TRUE;
    }
    return FALSE;
}

int wmain(int argc, wchar_t* argv[])
{
    std::vector<std::pair<std::wstring, std::wstring>> menuItems;

    if (argc == 2)
    {
        // 收集指定文件的右键菜单项
        std::wstring fileType;
        if (GetFileType(argv[1], fileType))
        {
            std::wstring regPath = L"\\";
            regPath += fileType;
            regPath += L"\\shell";
            EnumShellMenuItems(regPath.c_str(), menuItems);

            // 同时收集所有文件通用的菜单项
            EnumShellMenuItems(L"*\\shell", menuItems);
        }
    }
    else
    {
        // 默认收集所有文件和文件夹的右键菜单项
        EnumShellMenuItems(L"*\\shell", menuItems);
        EnumShellMenuItems(L"Directory\\shell", menuItems);
    }

    // 输出结果
    std::wcout << L"右键菜单项列表:" << std::endl;
    for (const auto& item : menuItems)
    {
        std::wcout << L"名称: " << item.first << std::endl;
        std::wcout << L"处理程序: " << item.second << std::endl;
        std::wcout << L"-------------------------" << std::endl;
    }

    return 0;
}

三、关键说明

  1. 权限要求:运行程序需要管理员权限,否则无法访问部分受保护的注册表项。
  2. 动态菜单项:部分应用会通过IContextMenu接口动态生成菜单项(如一些云盘客户端),这类项无法通过注册表枚举,需要调用Shell API的SHCreateShellItem和IContextMenu接口来枚举,如需支持可扩展代码。
  3. Notepad++示例:Notepad++通常会在HKEY_CLASSES_ROOT\*\shell\Notepad++下添加静态菜单项,其command值为带参数的程序路径,代码会正确解析并输出。
  4. 环境变量处理:代码会自动展开注册表中的环境变量(如%ProgramFiles%),转换为实际路径。

内容的提问来源于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 06:55:03