首页 > 代码库 > Creating Context Menu / 创建上下文菜单项 / Win32, VC++, Windows, DLL, ATL, COM

Creating Context Menu / 创建上下文菜单项 / Win32, VC++, Windows, DLL, ATL, COM

创建上下文菜单项 

 

1、新建一个ATL Project。

技术分享

2、建议将Project Property中Linker – General - “Register Output” 设为no,C/C++ - “Code Generation” - “Runtime Library” 设为 /MTd。

 技术分享

技术分享

3、在Solution Explorer中右键Add Class,选择ATL Simple Object。并在弹出的对话框中为该Class命名。

 技术分享

技术分享

4、添加完成后建议Build一下Project,MIDL compiler将根据 .idl文件生成IIDs and CLSIDs。

 技术分享

5、(可选)在Solution Explorer中右键Add Resource导入图标资源。

 技术分享

6、切换到新增Class的 .h文件中,使其继承接口IShellExtInit和IContextMenu。并在 .cpp文件中,参照MSDN给出实现。

 技术分享

技术分享

技术分享

技术分享
 1 // MyContextMenu.h : Declaration of the CMyContextMenu 2  3 #pragma once 4 #include "resource.h"       // main symbols 5  6  7  8 #include "ContextMenuExample_i.h" 9 #include <Shlobj.h>10 11 12 13 #if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA)14 #error "Single-threaded COM objects are not properly supported on Windows CE platform, such as the Windows Mobile platforms that do not include full DCOM support. Define _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA to force ATL to support creating single-thread COM object‘s and allow use of it‘s single-threaded COM object implementations. The threading model in your rgs file was set to ‘Free‘ as that is the only threading model supported in non DCOM Windows CE platforms."15 #endif16 17 using namespace ATL;18 19 20 // CMyContextMenu21 22 class ATL_NO_VTABLE CMyContextMenu :23     public CComObjectRootEx<CComSingleThreadModel>,24     public CComCoClass<CMyContextMenu, &CLSID_MyContextMenu>,25     public IDispatchImpl<IMyContextMenu, &IID_IMyContextMenu, 26     &LIBID_ContextMenuExampleLib, /*wMajor =*/ 1, /*wMinor =*/ 0>,27     public IShellExtInit,28     public IContextMenu29 {30 public:31     CMyContextMenu()32     {33     }34 35 DECLARE_REGISTRY_RESOURCEID(IDR_MYCONTEXTMENU)36 37 38 BEGIN_COM_MAP(CMyContextMenu)39     COM_INTERFACE_ENTRY(IMyContextMenu)40     COM_INTERFACE_ENTRY(IDispatch)41     COM_INTERFACE_ENTRY(IShellExtInit)42     COM_INTERFACE_ENTRY(IContextMenu)43 END_COM_MAP()44 45 46 47     DECLARE_PROTECT_FINAL_CONSTRUCT()48 49     HRESULT FinalConstruct();50 51     void FinalRelease();52 53 public:54     // IShellExtInit Method55     HRESULT STDMETHODCALLTYPE Initialize(56         _In_opt_  PCIDLIST_ABSOLUTE pidlFolder,57         _In_opt_  IDataObject *pdtobj,58         _In_opt_  HKEY hkeyProgID);59 60     // IContextMenu Method61     HRESULT STDMETHODCALLTYPE QueryContextMenu(62         _In_  HMENU hmenu,    63         _In_  UINT indexMenu,    64         _In_  UINT idCmdFirst,65         _In_  UINT idCmdLast,66         _In_  UINT uFlags);67 68     HRESULT STDMETHODCALLTYPE InvokeCommand(69         _In_  CMINVOKECOMMANDINFO *pici);70 71     HRESULT STDMETHODCALLTYPE GetCommandString(72         _In_  UINT_PTR idCmd,73         _In_  UINT uType,74         _Reserved_  UINT *pReserved,75         _Out_writes_bytes_((uType & GCS_UNICODE) ? (cchMax * sizeof(wchar_t)) : cchMax) _When_(!(uType & (GCS_VALIDATEA | GCS_VALIDATEW)), _Null_terminated_)  CHAR *pszName,76         _In_  UINT cchMax);77 78 private:79     HBITMAP MenuIcon1;80     HBITMAP MenuIcon2;81     HBITMAP MenuIcon3;82     HBITMAP MenuIcon4;83 84 85 };86 87 OBJECT_ENTRY_AUTO(__uuidof(MyContextMenu), CMyContextMenu)
MyContextMenu.h
  1 // MyContextMenu.cpp : Implementation of CMyContextMenu  2   3 #include "stdafx.h"  4 #include "MyContextMenu.h"  5   6   7 // CMyContextMenu  8   9 HRESULT CMyContextMenu::FinalConstruct() 10 { 11     HINSTANCE hInstance = _AtlBaseModule.GetModuleInstance(); 12     MenuIcon1 = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_BITMAP1)); 13     MenuIcon2 = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_BITMAP2)); 14     MenuIcon3 = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_BITMAP3)); 15     MenuIcon4 = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_BITMAP4)); 16      17     return S_OK; 18 } 19  20 void CMyContextMenu::FinalRelease() 21 { 22     if (MenuIcon1 != NULL) 23     { 24         DeleteObject(MenuIcon1); 25     } 26     if (MenuIcon2 != NULL) 27     { 28         DeleteObject(MenuIcon2); 29     } 30     if (MenuIcon3 != NULL) 31     { 32         DeleteObject(MenuIcon3); 33     } 34     if (MenuIcon4 != NULL) 35     { 36         DeleteObject(MenuIcon4); 37     } 38 } 39  40 HRESULT CMyContextMenu::Initialize( 41     _In_opt_  PCIDLIST_ABSOLUTE pidlFolder, 42     _In_opt_  IDataObject *pdtobj, 43     _In_opt_  HKEY hkeyProgID) { 44     HRESULT hr; 45     UINT    nFileCount; 46  47     FORMATETC fmt = 48     { 49         CF_HDROP, 50         NULL, 51         DVASPECT_CONTENT, 52         -1, 53         TYMED_HGLOBAL 54     }; 55  56     STGMEDIUM sm = 57     { 58         TYMED_HGLOBAL 59     }; 60  61     hr = pdtobj->GetData(&fmt, &sm); 62  63     if (FAILED(hr)) 64     { 65         return hr; 66     } 67  68     // query quantity of selected files 69     nFileCount = DragQueryFile((HDROP)sm.hGlobal, 0xFFFFFFFF, NULL, 0); 70  71     if (nFileCount == 1) // deal with only one file 72     { 73         // analyze selected file 74  75     } 76     else 77     { 78         hr = E_INVALIDARG; 79     } 80  81     ReleaseStgMedium(&sm); 82  83     return hr; 84 } 85  86 // IContextMenu Method 87 HRESULT CMyContextMenu::QueryContextMenu( 88     _In_  HMENU hmenu, 89     _In_  UINT indexMenu, 90     _In_  UINT idCmdFirst, 91     _In_  UINT idCmdLast, 92     _In_  UINT uFlags) { 93  94     UINT uCmdID = idCmdFirst; 95     LPCWSTR text1 = TEXT("新增层叠菜单项1"); 96     LPCWSTR text2 = TEXT("新增菜单项2"); 97     LPCWSTR text3 = TEXT("新增菜单项3"); 98     LPCWSTR text4 = TEXT("新增菜单项4"); 99     // do nothing when flag includes CMF_DEFAULTONLY.100     if (uFlags & CMF_DEFAULTONLY)101     {102         return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 0);103     }104     InsertMenu(hmenu, indexMenu, MF_SEPARATOR | MF_BYPOSITION, 0, NULL);105     indexMenu++;106     HMENU hSubMenu = CreateMenu();107     if (hSubMenu)108     {109         InsertMenu(hSubMenu, 0, MF_STRING | MF_BYPOSITION, uCmdID++, text2);110         SetMenuItemBitmaps(hSubMenu, 0, MF_BYPOSITION, MenuIcon2, MenuIcon2);111         InsertMenu(hSubMenu, 1, MF_STRING | MF_BYPOSITION, uCmdID++, text3);112         SetMenuItemBitmaps(hSubMenu, 1, MF_BYPOSITION, MenuIcon3, MenuIcon3);113         InsertMenu(hSubMenu, 2, MF_STRING | MF_BYPOSITION, uCmdID++, text4);114         SetMenuItemBitmaps(hSubMenu, 2, MF_BYPOSITION, MenuIcon4, MenuIcon4);115         // InsertMenu(hSubMenu, 3, MF_SEPARATOR | MF_BYPOSITION, 0, NULL);//插入分隔线116     }117     InsertMenu(hmenu, indexMenu, MF_STRING | MF_POPUP | MF_BYPOSITION, (UINT_PTR)hSubMenu, text1);118     SetMenuItemBitmaps(hmenu, indexMenu, MF_BYPOSITION, MenuIcon1, MenuIcon1);119     indexMenu++;120     InsertMenu(hmenu, indexMenu, MF_SEPARATOR | MF_BYPOSITION, 0, NULL);121     indexMenu++;122 123     // inform the explorer how many menu item we have added124     return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, uCmdID - idCmdFirst);125 }126 127 HRESULT CMyContextMenu::InvokeCommand(128     _In_  CMINVOKECOMMANDINFO *pici) {129     if (0 != HIWORD(pici->lpVerb))130         return E_INVALIDARG;131     // get index of added menu item132     switch (LOWORD(pici->lpVerb))133     {134     case 0:135     {136         // 执行新增菜单项2触发的操作137         STARTUPINFO si = { sizeof(si) };138         PROCESS_INFORMATION pi;139         TCHAR szCommandLine[] = TEXT("notepad");140         BOOL bCreateRet = CreateProcess(NULL, szCommandLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);141 142         break;143     }144     case 1:145     {146         // 执行新增菜单项3触发的操作147         STARTUPINFO si = { sizeof(si) };148         PROCESS_INFORMATION pi;149         TCHAR szCommandLine[] = TEXT("write");150         BOOL bCreateRet = CreateProcess(NULL, szCommandLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);151 152         break;153     }154     case 2:155     {156         // 执行新增菜单项4触发的操作157         STARTUPINFO si = { sizeof(si) };158         PROCESS_INFORMATION pi;159         TCHAR szCommandLine[] = TEXT("cmd");160         BOOL bCreateRet = CreateProcess(NULL, szCommandLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);161 162         break;163     }164     default:165     {166         return E_INVALIDARG;167         break;168     }169     }170     return S_OK;171 }172 173 HRESULT CMyContextMenu::GetCommandString(174     _In_  UINT_PTR idCmd,175     _In_  UINT uType,176     _Reserved_  UINT *pReserved,177     _Out_writes_bytes_((uType & GCS_UNICODE) ? (cchMax * sizeof(wchar_t)) : cchMax) _When_(!(uType & (GCS_VALIDATEA | GCS_VALIDATEW)), _Null_terminated_)  CHAR *pszName,178     _In_  UINT cchMax) {179     USES_CONVERSION;180     LPCTSTR szPrompt;181     // copy help info to cache when explorer ask182     if (uType & GCS_HELPTEXT)183     {184         switch (idCmd)185         {186         case 0:187             szPrompt = _T("新增菜单项2说明文字");188             break;189         case 1:190             szPrompt = _T("新增菜单项3说明文字");191             break;192         case 2:193             szPrompt = _T("新增菜单项4说明文字");194             break;195         default:196             //ATLASSERT(0);           // should never get here197             return E_INVALIDARG;198             break;199         }200         if (uType & GCS_UNICODE)201         {202             lstrcpynW((LPWSTR)pszName, T2CW(szPrompt), cchMax);203         }204         else205         {206             lstrcpynA(pszName, T2CA(szPrompt), cchMax);207         }208         return S_OK;209     }210     return E_INVALIDARG;211 }

7、在 .rgs文件中添加注册表信息,确保各GUID与 .idl文件中的一致。

 1 HKCR 2 { 3     NoRemove CLSID 4     { 5         ForceRemove {9C50C98F-E1FF-41CF-BD54-E9A3BBDDDEF8} = s MyContextMenu Class 6         { 7             ForceRemove Programmable 8             InprocServer32 = s %MODULE% 9             {10                 val ThreadingModel = s Apartment11             }12             TypeLib = s {EB1C2F43-315D-4D8F-9A2A-70E67BE888E2}13             Version = s 1.014         }15     }16 17     NoRemove *18     {19         NoRemove ShellEx20         {21             NoRemove ContextMenuHandlers22             {23                 ForceRemove MyContextMenu = s {9C50C98F-E1FF-41CF-BD54-E9A3BBDDDEF8}24             }25         }26     }27 }

8、Build Project 后打开cmd.exe,通过regsvr32命令注册或解注册生成的 .dll文件。

 技术分享

10、查看效果如下图所示。

 技术分享

 

——————————————————

本文为本人原创,如需转载请注明出处。

Creating Context Menu / 创建上下文菜单项 / Win32, VC++, Windows, DLL, ATL, COM