Creating Context Menu / 创建上下文菜单项 / 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 #endif 16 17 using namespace ATL; 18 19 20 // CMyContextMenu 21 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 IContextMenu 29 { 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 Method 55 HRESULT STDMETHODCALLTYPE Initialize( 56 _In_opt_ PCIDLIST_ABSOLUTE pidlFolder, 57 _In_opt_ IDataObject *pdtobj, 58 _In_opt_ HKEY hkeyProgID); 59 60 // IContextMenu Method 61 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)
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 added 124 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 item 132 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 ask 182 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 here 197 return E_INVALIDARG; 198 break; 199 } 200 if (uType & GCS_UNICODE) 201 { 202 lstrcpynW((LPWSTR)pszName, T2CW(szPrompt), cchMax); 203 } 204 else 205 { 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 'Apartment' 11 } 12 TypeLib = s '{EB1C2F43-315D-4D8F-9A2A-70E67BE888E2}' 13 Version = s '1.0' 14 } 15 } 16 17 NoRemove * 18 { 19 NoRemove ShellEx 20 { 21 NoRemove ContextMenuHandlers 22 { 23 ForceRemove MyContextMenu = s '{9C50C98F-E1FF-41CF-BD54-E9A3BBDDDEF8}' 24 } 25 } 26 } 27 }
8、Build Project 后打开cmd.exe,通过regsvr32命令注册或解注册生成的 .dll文件。
10、查看效果如下图所示。
——————————————————
本文为本人原创,如需转载请注明出处。