旧版快捷菜单(IContextMenu)实现

发布时间:2026/8/8 10:07:35
旧版快捷菜单(IContextMenu)实现 图例文件资源管理器右键菜单 | | V 初始化 IShellExtInit | | 调用 Initialize V 启用 IContextMenu (快捷菜单拓展接口) | | 调用 QueryContextMenu V InsertMenuItem 对象1 - 显示为菜单项1 InsertMenu 对象2 - 显示为菜单项2 (可能是带子菜单的项) | | 调用 InvokeCommand V 执行命令IShellExtInit的实现/** [in] pidlFolder 类型 PCIDLIST_ABSOLUTE 指向唯一标识文件夹的 ITEMIDLIST 结构的指针。 对于属性表扩展此参数为 NULL。 对于快捷菜单扩展它是包含要显示其快捷菜单的项的文件夹的项目标识符列表。 对于非默认拖放菜单扩展此参数指定目标文件夹。 [in] pdtobj 类型 IDataObject* 指向 IDataObject 接口对象的指针该对象可用于检索正在执行操作的对象。 [in] hkeyProgID 类型 HKEY 文件对象或文件夹类型的注册表项。 */ HRESULT Initialize( _In_ PCIDLIST_ABSOLUTE pidlFolder, _In_ IDataObject* pdtobj, _In_ HKEY hkeyProgID );案例private: std::vectorstd::wstring m_selectedFiles; std::wstring m_folderPath; public: STDMETHOD(Initialize)( _In_ PCIDLIST_ABSOLUTE pidlFolder, _In_ IDataObject* pdtobj, _In_ HKEY hkeyProgID )override{ // 获取文件夹folderPath TCHAR szFolderPath[MAX_PATH] { 0 }; if (pidlFolder ! NULL) { if (SHGetPathFromIDListW(pidlFolder, szFolderPath)) { // szFolderPath 现在包含文件夹的完整路径 // 例如C:\Users\Username\Documents m_folderPath szFolderPath; } } // 获取选中的文件 if (pdtobj) { FORMATETC fmt { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; STGMEDIUM stg { 0 }; if (SUCCEEDED(pdtobj-GetData(fmt, stg))) { HDROP hDrop (HDROP)GlobalLock(stg.hGlobal); if (hDrop) { UINT nFiles DragQueryFileW(hDrop, 0xFFFFFFFF, NULL, 0); for (UINT i 0; i nFiles; i) { wchar_t szFile[MAX_PATH]; if (DragQueryFileW(hDrop, i, szFile, MAX_PATH)) { m_selectedFiles.push_back(szFile); } } GlobalUnlock(stg.hGlobal); } ReleaseStgMedium(stg); } } return S_OK; }IContextMenu的实现方法声明STDMETHOD(QueryContextMenu)(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) override; STDMETHOD(InvokeCommand)(LPCMINVOKECOMMANDINFO pici) override; STDMETHOD(GetCommandString)(UINT_PTR idCmd, UINT uType, UINT *pwReserved, LPSTR pszName, UINT cchMax) override;/// summary /// 获取有关快捷菜单命令的信息包括命令的帮助字符串和与语言无关或 规范的名称。 /// /summary /// param nameidCmd菜单命令标识符偏移量/param /// param nameuType指定要返回的信息的标志。 /param /// param namepReserved保留。 应用程序在调用此方法时必须指定 NULL 处理程序在调用时必须忽略此参数。/param /// param namepszName要接收正在检索的以 null 结尾的字符串的缓冲区的地址。/param /// param namecchMax要接收以 null 结尾的字符串的缓冲区的大小以字符为单位。/param /// returnsS_OK/returns HRESULT GetCommandString( UINT_PTR idCmd, UINT uType, UINT* pReserved, CHAR* pszName, UINT cchMax ); /// summary /// 执行与快捷菜单项关联的命令。 /// /summary /// param namepici指向 CMINVOKECOMMANDINFO 或 CMINVOKECOMMANDINFOEX 结构的指针该结构包含有关命令的详细信息。/param /// returnsS_OK/returns HRESULT InvokeCommand( CMINVOKECOMMANDINFO* pici ); /// summary /// 将命令添加到快捷菜单。 /// /summary /// param namehmenu快捷菜单的句柄。 添加菜单项时处理程序应指定此句柄。/param /// param nameindexMenu要插入第一个新菜单项的从零开始的位置。/param /// param nameidCmdFirst处理程序可以为菜单项标识符指定的最小值。/param /// param nameidCmdLast处理程序可以为菜单项标识符指定的最大值。/param /// param nameuFlags指定如何更改快捷菜单的可选标志。 可多选/param /// returnsS_OK/returns HRESULT QueryContextMenu( HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags );QueryContextMenu/// summary /// 将程序菜单添加到快捷菜单。 /// /summary /// param namehmenu快捷菜单的句柄。 添加菜单项时处理程序应指定此句柄。/param /// param nameindexMenu 定位参数要插入第一个新菜单项的从零开始的位置。 /// 定位菜单程序总快捷菜单中的位置0为首-1为末 /// /param /// param nameidCmdFirst第一个命令ID处理程序可以为菜单项标识符指定的最小值。/param /// param nameidCmdLast最后一个命令ID处理程序可以为菜单项标识符指定的最大值。/param /// param nameuFlags指定如何更改快捷菜单的可选标志。 可多选 // uFlags 简要说明 // CMF_DEFAULTONLY : (最重要!) 用户意图为双击/回车执行默认操作而非打开菜单。应立即返回不添加任何项。 // CMF_EXTENDEDVERBS : 通常由 Shift右键 触发应显示高级/扩展菜单项。 // CMF_VERBSONLY : 通常来自“发送到”菜单应仅添加核心操作命令避免分隔符等。 // CMF_EXPLORE : 菜单在资源管理器窗口内弹出而非桌面或对话框。 // CMF_CANRENAME : 当前选择项支持重命名操作通常为单个对象。 // CMF_ASYNCVERBSTATE : 允许异步计算菜单项状态启用/禁用用于避免耗时操作阻塞菜单弹出。 // CMF_DISABLEDVERBS : 应显示即使为灰色不可用的菜单项。 // CMF_NORMAL : 无特殊标志常规菜单模式。 // CMF_NODEFAULT : 菜单不应包含默认命令无粗体项。 // CMF_INCLUDESTATIC : (主要用于旧式静态扩展) 应包含静态菜单项。 // CMF_RESERVED : 保留标志应忽略。 /// CMF_NOVERBS × /// CMF_ITEMMENU × /// CMF_OPTIMIZEFORINVOKE × /// CMF_SYNCCASCADEMENU × /// CMF_DONOTPICKDEFAULT × /// /// /param /// returnsS_OK/returns HRESULT QueryContextMenu( HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags );InsertMenu/// summary /// 创建普通菜单并绑定到程序菜单上。 /// /summary /// param namehmenu快捷菜单的句柄。/param /// param nameuPosition由uFlags决定/param /// param nameuFlags /// MF_BYCOMMAND : uPosition为标识符 /// MF_BYPOSITION : uPosition为菜单相对位置 /// MF_BITMAP : lpNewItem包含位图 /// MF_CHECKED : 复选标记 /// MF_DISABLED : 禁用模式(不灰色) /// MF_ENABLED : 启用模式 /// MF_GRAYED : 禁用模式(灰色) /// MF_MENUBARBREAK : 新列通过垂直线与旧列分割 /// MF_MENUBREAK : 新列不分割 /// MF_OWNERDRAW : 自定义菜单lpNewItem /// MF_POPUP : uIDNewItem包含子菜单 /// MF_SEPARATOR : 水平分隔线 /// MF_STRING : lpNewItem是文本字符串 /// MF_UNCHECKED : 取消复选标记 /// /param /// param nameuIDNewItem新菜单项的标识符或子菜单的句柄/param /// param namelpNewItem /// 由uFlags决定 /// 如果uFlags MF_BITMAP : 位图句柄 /// 如果uFlags MF_OWNERDRAW : 自定义菜单参数 /// 如果uFlags MF_STRING : 字符串指针 /// /param BOOL InsertMenuA( HMENU hMenu, UINT uPosition, UINT uFlags, UINT_PTR uIDNewItem, LPCSTR lpNewItem );InsertMenuItem/// summary /// 创建普通菜单并绑定到程序菜单上。 /// /summary /// param namehmenu快捷菜单的句柄/param /// param nameitem由fByPosition决定/param /// param namefByPosition /// FALSE : 菜单项标识符 /// TRUE : 菜单项位置 /// /param /// param namelpmi /// 指向 MENUITEMINFOA 结构体 /// /param BOOL InsertMenuItemA( HMENU hmenu, UINT item, BOOL fByPosition, LPCMENUITEMINFOA lpmi );MENUITEMINFO/* UINT cbSize; 结构的大小直接设置为sizeof(MENUITEMINFO) UINT fMask; 配置项 MIIM_BITMAP 开启配置hbmpItem参数 MIIM_CHECKMARKS 开启配置hbmpChecked 和 hbmpUnchecked 参数 MIIM_DATA 开启配置dwItemData 参数 MIIM_FTYPE 开启配置fType 参数 MIIM_ID 开启配置wID 参数 MIIM_STATE 开启配置fState 参数 MIIM_STRING 开启配置dwTypeData 参数 MIIM_SUBMENU 开启配置hSubMenu 参数 MIIM_TYPE 开启配置fType 和 dwTypeData 参数 UINT fType; MFT_BITMAP 启用位图dwTypeData低位为位图句柄 MFT_MENUBARBREAK 垂直线项新列与旧列分开 MFT_MENUBREAK 垂直线项新列不会与旧列分开 MFT_OWNERDRAW 自定义菜单配置dwTypeData值 MFT_RADIOCHECK 由hbmpChecked决定单选按钮 MFT_RIGHTJUSTIFY 右对齐菜单项和任何后续项 MFT_RIGHTORDER 指定菜单从右向左级联 MFT_SEPARATOR 水平分隔线 MFT_STRING 启用文本字符串dwTypeData参数为字符串句柄 UINT fState; MFS_CHECKED 启用hbmpChecked 项 MFS_DEFAULT 指定菜单项为默认值菜单只能包含一个默认菜单项以粗体显示。 MFS_DISABLED 禁用菜单项并将其灰显 MFS_ENABLED 启用菜单项 MFS_GRAYED 禁用菜单项并将其灰显 MFS_HILITE 突出显示菜单项 MFS_UNCHECKED 取消选中菜单项 MFS_UNHILITE 从菜单项中删除突出显示 UINT wID; 标识菜单项的应用程序定义值 HMENU hSubMenu; 下拉菜单或子菜单的句柄可为NULL HBITMAP hbmpChecked; 可选框 HBITMAP hbmpUnchecked; 可选框 ULONG_PTR dwItemData; LPSTR dwTypeData; UINT cch; HBITMAP hbmpItem; HBMMENU_CALLBACK 自定义 HBMMENU_MBAR_CLOSE 菜单栏的“关闭”按钮。 HBMMENU_MBAR_CLOSE_D 菜单栏的“已禁用关闭”按钮。 HBMMENU_MBAR_MINIMIZE 菜单栏的最小化按钮 HBMMENU_MBAR_MINIMIZE_D 已禁用菜单栏的最小化按钮。 HBMMENU_MBAR_RESTORE 菜单栏的“还原”按钮。 HBMMENU_POPUP_CLOSE 子菜单的“关闭”按钮。 HBMMENU_POPUP_MAXIMIZE 子菜单的最大化按钮 HBMMENU_POPUP_MINIMIZE 子菜单的最小化按钮。 HBMMENU_POPUP_RESTORE 子菜单的“还原”按钮。 HBMMENU_SYSTEM dwItemData中指定的窗口的 Windows 图标或图标。 */ typedef struct tagMENUITEMINFOA { UINT cbSize; UINT fMask; UINT fType; UINT fState; UINT wID; HMENU hSubMenu; HBITMAP hbmpChecked; HBITMAP hbmpUnchecked; ULONG_PTR dwItemData; LPSTR dwTypeData; UINT cch; HBITMAP hbmpItem; } MENUITEMINFOA, *LPMENUITEMINFOA;InvokeCommand/// summary /// 执行与快捷菜单项关联的命令。 /// /summary /// param namepici指向 CMINVOKECOMMANDINFO 或 CMINVOKECOMMANDINFOEX 结构的指针该结构包含有关命令的详细信息。/param /// returnsS_OK/returns HRESULT InvokeCommand( CMINVOKECOMMANDINFO* pici );CMINVOKECOMMANDINFO/* DWORD cbSize; 此结构的大小 DWORD fMask; 标志位 CMIC_MASK_HOTKEY 激活dwHotKey参数键盘快捷方式 CMIC_MASK_ICON 激活hIcon参数图标 CMIC_MASK_FLAG_NO_UI 系统无法显示用户界面元素 CMIC_MASK_NO_CONSOLE 禁止创建新控制台 CMIC_MASK_FLAG_SEP_VDM 16 位应用程序时才有效在专用虚拟 DOS 中运行 CMIC_MASK_ASYNCOK 等待 DDE 会话终止然后再返回 CMIC_MASK_NOASYNC CMIC_MASK_SHIFT_DOWN 已按下 SHIFT 键 CMIC_MASK_CONTROL_DOWN 已按下 CTRL 键 CMIC_MASK_FLAG_LOG_USAGE 指示InvokeCommand的实现可能需要跟踪为“最近使用的文档”菜单等功能调用的项。 CMIC_MASK_NOZONECHECKS 不要执行区域检查 HWND hwnd; 父窗口句柄 LPCSTR lpVerb; 命令行为 CMDSTR_RUNAS Runas CMDSTR_PRINT 打印 CMDSTR_PREVIEW 预览 CMDSTR_OPEN 打开 LPCSTR lpParameters; 命令参数 LPCSTR lpDirectory; 工作目录 int nShow; 窗口显示参数 DWORD dwHotKey; 指定键盘快捷方式由fMask的 CMIC_MASK_HOTKEY 控制 HANDLE hIcon; 指定图标由fMask的 CMIC_MASK_ICON 控制 */ typedef struct _CMINVOKECOMMANDINFO { DWORD cbSize; DWORD fMask; HWND hwnd; LPCSTR lpVerb; LPCSTR lpParameters; LPCSTR lpDirectory; int nShow; DWORD dwHotKey; HANDLE hIcon; } CMINVOKECOMMANDINFO;CMINVOKECOMMANDINFOEX/* DWORD cbSize; 此结构的大小 DWORD fMask; 标志位 CMIC_MASK_HOTKEY 激活dwHotKey参数键盘快捷方式 CMIC_MASK_ICON 激活hIcon参数图标 CMIC_MASK_FLAG_NO_UI 系统无法显示用户界面元素 CMIC_MASK_UNICODE 激活Unicode字符串数据包括 lpVerbW、 lpParametersW、 lpDirectoryW 和 lpTitleW 成员 CMIC_MASK_NO_CONSOLE 禁止创建新控制台 CMIC_MASK_HASLINKNAME lpTitle 成员包含快捷方式文件的完整路径 CMIC_MASK_HASTITLE 激活 lpTitle 参数 CMIC_MASK_FLAG_SEP_VDM 16 位应用程序时才有效在专用虚拟 DOS 中运行 CMIC_MASK_ASYNCOK 等待 DDE 会话终止然后再返回 CMIC_MASK_NOASYNC CMIC_MASK_SHIFT_DOWN 已按下 SHIFT 键 CMIC_MASK_CONTROL_DOWN 已按下 CTRL 键 CMIC_MASK_FLAG_LOG_USAGE 指示InvokeCommand的实现可能需要跟踪为“最近使用的文档”菜单等功能调用的项。 CMIC_MASK_NOZONECHECKS 不要执行区域检查 CMIC_MASK_PTINVOKE 激活 ptInvoke 参数 HWND hwnd; 父窗口句柄 LPCSTR lpVerb; 命令行为 CMDSTR_RUNAS Runas CMDSTR_PRINT 打印 CMDSTR_PREVIEW 预览 CMDSTR_OPEN 打开 LPCSTR lpParameters; 命令参数 LPCSTR lpDirectory; 工作目录 int nShow; 窗口显示参数 DWORD dwHotKey; 指定键盘快捷方式由fMask的 CMIC_MASK_HOTKEY 控制 HANDLE hIcon; 指定图标由fMask的 CMIC_MASK_ICON 控制 LPCSTR lpTitle; LPCWSTR lpVerbW; Unicode 谓词 LPCWSTR lpParametersW; Unicode 参数 LPCWSTR lpDirectoryW; Unicode 目录 LPCWSTR lpTitleW; Unicode 标题 POINT ptInvoke; 命令调用点 */ typedef struct _CMINVOKECOMMANDINFOEX { DWORD cbSize; DWORD fMask; HWND hwnd; LPCSTR lpVerb; LPCSTR lpParameters; LPCSTR lpDirectory; int nShow; DWORD dwHotKey; HANDLE hIcon; LPCSTR lpTitle; LPCWSTR lpVerbW; LPCWSTR lpParametersW; LPCWSTR lpDirectoryW; LPCWSTR lpTitleW; POINT ptInvoke; } CMINVOKECOMMANDINFOEX;GetCommandString/** * brief 获取有关快捷菜单命令的信息包括命令的帮助字符串和与语言无关或 规范的名称。 * param idCmd [in]菜单命令标识符偏移量 * param uType [in]指定要返回的信息的标志 * GCS_HELPTEXTA (ANSI)将 pszName 设置为包含命令帮助文本 * GCS_HELPTEXTW (Unicode)将 pszName 设置为包含命令帮助文本 * GCS_VALIDATEA 如果菜单项存在则返回S_OK否则返回S_FALSE。 * GCS_VALIDATEW * GCS_VERBA 将 pszName 设置为包含菜单项与语言无关的命令名称的字符串 * GCS_VERBW * param pReserved 保留。 应用程序在调用此方法时必须指定 NULL 处理程序在调用时必须忽略此参数。 * param pszName [out]要接收正在检索的以 null 结尾的字符串的缓冲区的地址。 * param cchOut [out]要接收以 null 结尾的字符串的缓冲区的大小以字符为单位 * return S_OK * summary * */ HRESULT GetCommandString( UINT_PTR idCmd, UINT uType, UINT* pReserved, CHAR* pszName, UINT cchMax );注册表结构IContextMenu的注册表需求分为两部分一部分是标记拓展组件唯一标识的CLSID key另一部分是注册到右键菜单指定环境的 file_extension Key 如全部文件的* 和AllFilesystemObjects 、DesktopBackground桌面背景目录等详细参考COM注册表项架构。HKCR { file_extension { shell {...} shellex {...} } CLSID { ...ATL项目自动生成 } }参考链接微软文档-IContextMenu (shobjidl_core.h) - Win32 apps | Microsoft Learn微软文档-InsertMenuA 函数 winuser.h - Win32 apps | Microsoft Learn微软文档-InsertMenuItemA 函数 (winuser.h) - Win32 apps | Microsoft Learn微软文档-IContextMenuGetCommandString (shobjidl_core.h) - Win32 apps | Microsoft Learn微软文档-IContextMenuInvokeCommand (shobjidl_core.h) - Win32 apps | Microsoft Learn微软文档-CMINVOKECOMMANDINFO (shobjidl_core.h) - Win32 apps | Microsoft Learn微软文档-CMINVOKECOMMANDINFOEX (shobjidl_core.h) - Win32 apps | Microsoft Learn