自己写的小程序中用到的,网上资料相对还是毕竟全的,这里再整理下。毕竟我也不是很了解ATL,里面估计还是有不少问题的,就当作参考吧。
1.创建ATL工程,这个没什么好讲的。
我对COM组件没什么研究,这边就没勾选COM。
2.Project >> Add New Item >> ATL >> ATL Simple Object
3.添加继承关系,需要继承自IShellExtInit和IContextMenu。
IShellExtInit接口用于Shell初始化Shell扩展对象的初始化工作。当用户需要定制Shell的快捷菜单或者属性页时,需实现IContextMenu或IShellPropSheetExt接口的同时,还需实现IShellExtInit接口.Shell会自动调用该接口的Initialize方法来初始化Shell扩展对象。
class ATL_NO_VTABLE CAtlMenuImpl : public CComObjectRootEx<CComSingleThreadModel>, public CComCoClass<CAtlMenuImpl, &CLSID_AtlMenuImpl>, public IDispatchImpl<IAtlMenuImpl, &IID_IAtlMenuImpl, &LIBID_AtlMenuLib, /*wMajor =*/ 1, /*wMinor =*/ 0>, public IShellExtInit, //继承IShellExtInit public IContextMenu //继承IContextMenu
添加到映射表:
BEGIN_COM_MAP(CAtlMenuImpl)
COM_INTERFACE_ENTRY(IShellExtInit)
COM_INTERFACE_ENTRY(IContextMenu)
END_COM_MAP()
添加几个变量:
std::vector<std::wstring> m_fileNameVector; //文件列表容器 UINT m_uiFileNum; HBITMAP m_hRegBmp; //图标
实现继承的虚函数:
//IShellExtInit STDMETHODIMP Initialize(LPCITEMIDLIST, LPDATAOBJECT, HKEY); //初始化Shell扩展对象 //IContextMenu STDMETHODIMP GetCommandString(UINT_PTR, UINT, UINT*, LPSTR, UINT); //获取有关快捷菜单命令的信息,包括帮助字符串以及命令的语言无关或规范名称 STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO); //执行与快捷菜单项关联的命令 STDMETHODIMP QueryContextMenu(HMENU, UINT, UINT, UINT, UINT); //将命令添加到快捷菜单
4.具体的实现
CAtlMenuImpl::CAtlMenuImpl() { //m_hRegBmp = LoadBitmap(g_hInstance, MAKEINTRESOURCE(IDC_MENU)); } STDMETHODIMP CAtlMenuImpl::Initialize(LPCITEMIDLIST pidlFolder, LPDATAOBJECT pDataObj, HKEY hProgID) { FORMATETC fmt = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; STGMEDIUM stg = { TYMED_HGLOBAL }; HDROP hDrop; // Look for CF_HDROP data in the data object. if (FAILED(pDataObj->GetData(&fmt, &stg))) { // Nope! Return an "invalid argument" error back to Explorer. return E_INVALIDARG; } // Get a pointer to the actual data. hDrop = (HDROP)GlobalLock(stg.hGlobal); // Make sure it worked. if (NULL == hDrop) return E_INVALIDARG; // Sanity check - make sure there is at least one filename. m_uiFileNum = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0); HRESULT hr = S_OK; WCHAR wcFileName[MAX_PATH]; for (UINT i = 0; i < m_uiFileNum; i++) { memset(wcFileName, 0, MAX_PATH); if (0 == DragQueryFileW(hDrop, i, wcFileName, MAX_PATH)) { hr = E_INVALIDARG; break; } m_fileNameVector.push_back(wstring(wcFileName)); } GlobalUnlock(stg.hGlobal); ReleaseStgMedium(&stg); return hr; } STDMETHODIMP CAtlMenuImpl::GetCommandString(UINT_PTR idCmd, UINT uFlags, UINT* pwReserved, LPSTR pszName, UINT cchMax) { USES_CONVERSION; // Check idCmd, it must be 0 since we have only one menu item. if (0 != idCmd) return E_INVALIDARG; // If Explorer is asking for a help string, copy our string into the // supplied buffer. if (uFlags & GCS_HELPTEXT) { LPCTSTR szText = _T("This is the simple shell extension's help"); if (uFlags & GCS_UNICODE) { // We need to cast pszName to a Unicode string, and then use the // Unicode string copy API. lstrcpynW((LPWSTR)pszName, T2CW(szText), cchMax); } else { // Use the ANSI string copy API to return the help string. lstrcpynA(pszName, T2CA(szText), cchMax); } return S_OK; } return E_INVALIDARG; } STDMETHODIMP CAtlMenuImpl::InvokeCommand(LPCMINVOKECOMMANDINFO pCmdInfo) { // If lpVerb really points to a string, ignore this function call and bail out. if (0 != HIWORD(pCmdInfo->lpVerb)) return E_INVALIDARG; wchar_t szCurrentDir[MAX_PATH] = { 0 }; wstring exePath = L""; wstring paramStr = L""; wstring parms = L"\""; GetModuleFileNameW(g_hInstance, szCurrentDir, MAX_PATH); exePath = szCurrentDir; exePath = exePath.substr(0, exePath.find_last_of(L'\\')); exePath += L"\\DF.exe"; for (UINT i = 0; i < m_uiFileNum; i++) { parms += m_fileNameVector.at(i).c_str(); if (i != m_uiFileNum - 1) { parms += L"*"; } } parms += L'\"'; paramStr += parms; g_log.LogInfo(L"[InvokeCommand]exePath:%s, param:%s", exePath.c_str(), paramStr.c_str()); ShellExecuteW(pCmdInfo->hwnd, L"open", exePath.c_str(), paramStr.c_str(), NULL, SW_HIDE); return S_OK; } STDMETHODIMP CAtlMenuImpl::QueryContextMenu(HMENU hmenu, UINT uMenuIndex, UINT uidFirstCmd, UINT uidLastCmd, UINT uFlags) { UINT uCmdID = uidFirstCmd; UINT uMenuCount = 0; // If the flags include CMF_DEFAULTONLY then we shouldn't do anything. if (uFlags & CMF_DEFAULTONLY) return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 0); InsertMenu(hmenu, uMenuIndex++, MF_SEPARATOR | MF_BYPOSITION, 0, NULL); uMenuCount++; HMENU hNextDocSecurity = CreateMenu(); InsertMenuW(hmenu, uMenuIndex, MF_POPUP | MF_BYPOSITION, (UINT_PTR)uCmdID, _T("呵呵呵呵")); SetMenuItemBitmaps(hmenu, 0, MF_BYPOSITION, NULL, NULL); uMenuIndex++; uMenuCount++; return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, uMenuCount); }
5.代码注册
打开idl文件,记录下最下面的uuid
fc71b946-3310-4bb2-9f54-ac8a98a02f22
在AtlMenu.cpp中DllRegisterServer和DllUnregisterServer增加对应的注册表信息
// DllRegisterServer - Adds entries to the system registry. _Use_decl_annotations_ STDAPI DllRegisterServer(void) { // registers object, typelib and all interfaces in typelib if (0 == (GetVersion() & 0x80000000UL)) { g_log.LogInfo(L"[DllRegisterServer] start"); //写相应的注册表键值 CRegKey reg; LONG lRet = reg.Open(HKEY_LOCAL_MACHINE, _T("Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved"), KEY_SET_VALUE); if (ERROR_SUCCESS != lRet) { g_log.LogError(L"[DllRegisterServer] Open HKEY_LOCAL_MACHINE failed:%d", GetLastError()); return E_ACCESSDENIED; } lRet = reg.SetStringValue(_T("AtlMenu"), _T("{fc71b946-3310-4bb2-9f54-ac8a98a02f22}")); if (ERROR_SUCCESS != lRet) { g_log.LogError(L"[DllRegisterServer] SetStringValue failed:%d", GetLastError()); return E_ACCESSDENIED; } lRet = reg.Create(HKEY_CLASSES_ROOT, _T("*\\shellex\\ContextMenuHandlers\\AtlMenu")); if (ERROR_SUCCESS != lRet) { g_log.LogError(L"[DllRegisterServer] Creating shellex\\ContextMenuHandlers register failed:%d", GetLastError()); return E_ACCESSDENIED; } lRet = reg.SetStringValue(NULL, _T("{fc71b946-3310-4bb2-9f54-ac8a98a02f22}")); if (ERROR_SUCCESS != lRet) { g_log.LogError(L"[DllRegisterServer] Set \\shellex\\ContextMenuHandlers register failed:%d", GetLastError()); return E_ACCESSDENIED; } //写文件夹注册表项 lRet = reg.Create(HKEY_CLASSES_ROOT, _T("Folder\\shellex\\ContextMenuHandlers\\AtlMenu")); if (ERROR_SUCCESS != lRet) { g_log.LogError(L"[DllRegisterServer] Create Folder reg failed:%d", GetLastError()); return E_ACCESSDENIED; } lRet = reg.SetStringValue(_T("AtlMenu"), _T("{fc71b946-3310-4bb2-9f54-ac8a98a02f22}")); if (ERROR_SUCCESS != lRet) { g_log.LogError(L"[DllRegisterServer] SetStringValue ( Folder AtlMenu ) failed:%d", GetLastError()); return E_ACCESSDENIED; } lRet = reg.Create(HKEY_CLASSES_ROOT, _T("Directory\\shellex\\ContextMenuHandlers\\AtlMenu")); if (ERROR_SUCCESS != lRet) { g_log.LogError(L"[DllRegisterServer] Creating HKEY_CLASSES_ROOT\\Directory\\shellex register failed:%d", GetLastError()); return E_ACCESSDENIED; } lRet = reg.SetStringValue(NULL, _T("{fc71b946-3310-4bb2-9f54-ac8a98a02f22}")); if (ERROR_SUCCESS != lRet) { g_log.LogError(L"[DllRegisterServer] Set HKEY_CLASSES_ROOT\\diretory\\shell register failed:%d", GetLastError()); return E_ACCESSDENIED; } g_log.LogInfo(L"[DllRegisterServer] finished"); } // registers object, typelib and all interfaces in typelib HRESULT hr = _AtlModule.DllRegisterServer(); return hr; } // DllUnregisterServer - Removes entries from the system registry. _Use_decl_annotations_ STDAPI DllUnregisterServer(void) { //删除相关注册表键、值 if (0 == (GetVersion() & 0x80000000UL)) { CRegKey reg; LONG lRet; lRet = reg.Open(HKEY_LOCAL_MACHINE, _T("Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved"), KEY_SET_VALUE); if (ERROR_SUCCESS == lRet) { lRet = reg.DeleteValue(_T("AtlMenu")); } lRet = RegDeleteKeyW(HKEY_CLASSES_ROOT, L"*\\shellex\\ContextMenuHandlers\\AtlMenu"); lRet = RegDeleteKeyW(HKEY_CLASSES_ROOT, L"Folder\\shellex\\ContextMenuHandlers\\AtlMenu"); lRet = RegDeleteKeyW(HKEY_CLASSES_ROOT, L"Directory\\shellex\\ContextMenuHandlers\\AtlMenu"); } HRESULT hr = _AtlModule.DllUnregisterServer(); return hr; }
6.管理员启动cmd运行regsvr32注册dll就可以看到具体的效果了