你的位置:首页 > 操作系统

[操作系统]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 'Apartment'11       }12       TypeLib = s '{EB1C2F43-315D-4D8F-9A2A-70E67BE888E2}'13       Version = s '1.0'14     }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、查看效果如下图所示。

 

 

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

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