让MFC程序支持右键菜单给程序传送命令行参数
问题:
1.给系统添加右键菜。
2.选中的文件(文件夹)--右键---菜单-- 操作选中的文件(文件夹)
3.文件名,路径等参数传递给程序。,如同winrar一样。
4.程序根据传递来的参数执行相应的模块。
右键菜单预备知识:
注册右键弹出事件的方法,请按照以下对应关系:
\HKEY_CLASSES_ROOT\Folder\Shell 任意文件夹
\HKEY_CLASSES_ROOT\Directory\Shell 任意目录
\HKEY_CLASSES_ROOT\Drive\Shell 任意驱动器
\HKEY_CLASSES_ROOT\*\Shell 任意文件
新建一个主键,并在主键下添加一个特定主键(command),修改command的键值指到你的应用程序。
例如:
建立\HKEY_CLASSES_ROOT\Folder\Shell\Test,将在Folder上按下右键时出现自定义的菜单
建立\HKEY_CLASSES_ROOT\Folder\Shell\Test,并设定该主键为自定义的菜单,则将在Folder上按下右键时出现自定义的菜单菜单钮。
建立\HKEY_CLASSES_ROOT\Folder\Shell\Test\command,并设定command"缺省"键值为"c:\tools\Test.exe" "%1",则将以Folder名为第一参数启动ATest.exe。
* 注意command键值的双引号不可缺省另外:
\HKEY_CLASSES_ROOT\Folder\Shellex
\HKEY_CLASSES_ROOT\Directory\Shellex\HKEY_CLASSES_ROOT\Drive\Shellex
\HKEY_CLASSES_ROOT\*\Shellex可以按应用程序在Registroy中的注册ID调用。这样可以调用.DLL和钩子函数。
但建议按第一种方法调用应用程序,因为注册一个ID实在麻烦。如果你的应用程序支持DDE,你可以参考
\HKEY_CLASSES_ROOT\Directory\Shell\Find键的设定来进行DDE设置。
注册表中的%1为系统默认变量
%1 表示程序操作的文件
%2 表示系统默认的打印机
%3 表示资料扇区
%4 表示操作的Port 端口
解决方案
1、通过手动或程序修改注册表实现
2、Windows Shell扩展接口来实现
实现方法:1、通过手动或程序修改注册表实现
1、在[HKEY_CLASSES_ROOT/Directory/shell] 分支下建立一个自己命名的子键,例如:ProjTest;
2、将新建子键ProjTest下的默认键值设置为你想在文件夹右键菜单中显示的字符串,如:Test测试;
3、在刚才新建的子键ProjTest下面再新建一子键,但是名字必须是command;
4、将刚才新建子键command下面的默认键值设置为:E:/SoftWare/ProjTest.exe "%1",其中软件的路径可以根据自己的程序位置来写,但是后面的 "%1" 一定要记得加上,他代表的就是右键单击文件夹所在的路径,即我们调用 GetCommandLine 函数所获得的附加参数。
我们右键单击某个文件夹之后,在弹出的菜单中就已经有我们添加的一项即“工程目录清理器”
5、在程序里修改 InitInstance() 函数代码如下:
- BOOL CProjTestApp::InitInstance()
- {
- //省略的其他代码……;
- CWinApp::InitInstance();
- //for the command line;
- int argc = 0;
- LPWSTR *lpszArgv = NULL;
- LPTSTR szCmdLine = GetCommandLine(); //获取命令行参数;
- lpszArgv = CommandLineToArgvW(szCmdLine, &argc); //拆分命令行参数字符串;
- if (argc >= 2) {
- WCHAR szPath[500] = {0};
- swprintf(szPath, _T("%s%s%s"), _T("确定要操作 [ "), lpszArgv[1], _T(" ] 目录吗?")); //格式化字符串;
- if (MessageBox(NULL, szPath, _T("友情提示"), MB_OKCANCEL|MB_ICONQUESTION) == IDOK) { //进行删除提示;
- m_bStartFromCmd = TRUE;
- CleanStart(...); //调用CleanStart函数进行工程目录的清理工作;
- }
- return FALSE;
- }
- //command line end;
- CProjCleanerDlg dlg;
- m_pMainWnd = &dlg;
- INT_PTR nResponse = dlg.DoModal();
- if (nResponse == IDOK)
- {
- }
- else if (nResponse == IDCANCEL)
- {
- }
- return FALSE;
- }
1.创建工程这里我用的VS2005,创建一个ATL 项目,属性不需要改,点默认的就可以了。
2.右击项目,添加,类,添加一个新ATL简单对象。
3.编辑代码,关于IShellExtInit和IContextMenu接口可以查看MSDN,上面写的很详细。
class ATL_NO_VTABLE CCContextMenuExt :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CCContextMenuExt, &CLSID_CContextMenuExt>,
public IDispatchImpl<ICContextMenuExt, &IID_ICContextMenuExt, &LIBID_CtxMenuExtLib, /*wMajor =*/ 1, /*wMinor =*/ 0>,
public IShellExtInit,
public IContextMenu
{
public:
CCContextMenuExt()
{
}
DECLARE_REGISTRY_RESOURCEID(IDR_CCONTEXTMENUEXT)
BEGIN_COM_MAP(CCContextMenuExt)
COM_INTERFACE_ENTRY(ICContextMenuExt)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY(IShellExtInit)
COM_INTERFACE_ENTRY(IContextMenu)
END_COM_MAP()
DECLARE_PROTECT_FINAL_CONSTRUCT()
HRESULT FinalConstruct()
{
m_hBitmap = LoadBitmap(_hInstance, MAKEINTRESOURCE(IDB_MENU));
return S_OK;
}
void FinalRelease()
{
if (m_hBitmap)
{
DeleteObject(m_hBitmap);
}
}
public:
enum
{
IDM_CTXMENU = 0,
};
public:
HRESULT STDMETHODCALLTYPE Initialize(
/* [in] */ LPCITEMIDLIST pidlFolder,
/* [in] */ IDataObject *pdtobj,
/* [in] */ HKEY hkeyProgID)
{
HRESULT hr;
UINT nFileCount;
UINT nLen;
FORMATETC fmt =
{
CF_HDROP,
NULL,
DVASPECT_CONTENT,
-1,
TYMED_HGLOBAL
};
STGMEDIUM sm =
{
TYMED_HGLOBAL
};
hr = pdtobj->GetData(&fmt, &sm);
if (FAILED(hr))
{
return hr;
}
nFileCount = DragQueryFile((HDROP)sm.hGlobal, 0xFFFFFFFF, NULL, 0);
if (nFileCount >= 1)
{
nLen = DragQueryFile((HDROP)sm.hGlobal, 0, m_pszFileName, sizeof(m_pszFileName));
if (nLen >0 && nLen <MAX_PATH)
{
m_pszFileName[nLen] = _T('\0');
hr = S_OK;
}
else
{
hr = E_INVALIDARG;
}
}
else
{
hr = E_INVALIDARG;
}
ReleaseStgMedium(&sm);
return hr;
}
STDMETHOD(QueryContextMenu)(THIS_
HMENU hmenu,
UINT indexMenu,
UINT idCmdFirst,
UINT idCmdLast,
UINT uFlags)
{
MENUITEMINFO mii;
if (uFlags & CMF_DEFAULTONLY)
{
return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 0);
}
memset((void*)&mii, 0, sizeof(mii));
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_STRING | MIIM_CHECKMARKS | MIIM_ID | MIIM_STATE;
mii.cch = lstrlen(SZ_MENUTEXT);
mii.dwTypeData = SZ_MENUTEXT;
/*
这里用hbmpChecked而不用hbmpItem的原因
- -自己动手试试就知道了。
*/
mii.hbmpItem
mii.hbmpChecked = m_hBitmap;
mii.hbmpUnchecked = m_hBitmap;
mii.fState = MFS_ENABLED;
mii.wID = idCmdFirst + indexMenu;
if (!InsertMenuItem(hmenu, indexMenu, TRUE, &mii))
{
return E_FAIL;
}
lstrcpynA(m_pszVerb, "protected_run", 32);
lstrcpynW(m_pwszVerb, L"protected_run", 32);
return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, IDM_CTXMENU + 1);
}
STDMETHOD(InvokeCommand)(THIS_
LPCMINVOKECOMMANDINFO lpici)
{
BOOL fEx = FALSE;
BOOL fUnicode = FALSE;
if(lpici->cbSize == sizeof(CMINVOKECOMMANDINFOEX))
{
fEx = TRUE;
if((lpici->fMask & CMIC_MASK_UNICODE))
{
fUnicode = TRUE;
}
}
if( !fUnicode && HIWORD(lpici->lpVerb))
{
if(StrCmpIA(lpici->lpVerb, m_pszVerb))
{
return E_FAIL;
}
}
else if( fUnicode && HIWORD(((CMINVOKECOMMANDINFOEX *) lpici)->lpVerbW))
{
if(StrCmpIW(((CMINVOKECOMMANDINFOEX *)lpici)->lpVerbW, m_pwszVerb))
{
return E_FAIL;
}
}
else if(LOWORD(lpici->lpVerb) != IDM_CTXMENU)
{
return E_FAIL;
}
else
{
//在此处理点击事件.
MessageBox(NULL, m_pszFileName, _T(""), MB_OK);
return S_OK;
}
return E_FAIL;
}
STDMETHOD(GetCommandString)(THIS_
UINT_PTR idCmd,
UINT uType,
UINT * pwReserved,
LPSTR pszName,
UINT cchMax)
{
HRESULT hr = E_INVALIDARG;
static CHAR szHelpTextA[] = "windows扩展菜单!";
static WCHAR szHelpTextW[] = L"windows扩展菜单!";
if(idCmd != IDM_CTXMENU)
{
return hr;
}
switch(uType)
{
case GCS_HELPTEXTA:
lstrcpynA((CHAR*)pszName, szHelpTextA, cchMax);
break;
case GCS_HELPTEXTW:
lstrcpynW((WCHAR*)pszName, szHelpTextW, cchMax);;
break;
case GCS_VERBA:
lstrcpynA((CHAR*)pszName, m_pszVerb, cchMax);
break;
case GCS_VERBW:
lstrcpynW((WCHAR*)pszName, m_pwszVerb, cchMax);
break;
default:
hr = S_OK;
break;
}
return hr;
}
private:
TCHAR m_pszFileName[MAX_PATH];
HBITMAP m_hBitmap;
CHAR m_pszVerb[32];
WCHAR m_pwszVerb[32];
};
4.修改服务注册、取消注册函数,这里只需在需要处理的文件类型的shllex下的ContextMenuHandlers下创建项,并设置接口ID。
STDAPI DllRegisterServer(void)
{
// 注册对象、类型库和类型库中的所有接口
HRESULT hr;
HKEY hKey;
static char pszGUID[] = "{C2397F2E-4BA3-4B9D-858A-F775761C023B}";
hr = _AtlModule.DllRegisterServer();
if (FAILED(hr))
{
return hr;
}
if (RegCreateKeyA(HKEY_CLASSES_ROOT,
"*\\shellex\\ContextMenuHandlers\\CtxMenu", &hKey) != ERROR_SUCCESS)
{
return E_FAIL;
}
if (RegSetValueA(hKey, NULL, REG_SZ, pszGUID,
(DWORD)strlen(pszGUID)) != ERROR_SUCCESS)
{
RegCloseKey(hKey);
return E_FAIL;
}
return hr;
}
// DllUnregisterServer - 将项从系统注册表中移除
STDAPI DllUnregisterServer(void)
{
RegDeleteKeyA(HKEY_CLASSES_ROOT, "*\\shellex\\ContextMenuHandlers\\CtxMenu");
return _AtlModule.DllUnregisterServer();
}
5.编译运行,VS会自动替你注册,当然也可以用regsvr32 自己注册。