让MFC程序支持右键菜单给程序传送命令行参数

来源:互联网 发布:程序员需要考什么证书 编辑:程序博客网 时间:2024/06/03 12:33

                                       让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() 函数代码如下:

  1. BOOL CProjTestApp::InitInstance()
  2. {
  3.         //省略的其他代码……;
  4.         CWinApp::InitInstance();
  5.        
  6.  //for the command line;
  7.         int argc = 0;
  8.         LPWSTR *lpszArgv = NULL;
  9.         LPTSTR szCmdLine = GetCommandLine(); //获取命令行参数;
  10.         lpszArgv = CommandLineToArgvW(szCmdLine, &argc); //拆分命令行参数字符串;
  11.         if (argc >= 2) {
  12.                 WCHAR szPath[500] = {0};
  13.                 swprintf(szPath, _T("%s%s%s"), _T("确定要操作 [ "), lpszArgv[1], _T(" ] 目录吗?")); //格式化字符串;
  14.                 if (MessageBox(NULL, szPath, _T("友情提示"), MB_OKCANCEL|MB_ICONQUESTION) ==  IDOK) { //进行删除提示;
  15.                         m_bStartFromCmd = TRUE;
  16.                         CleanStart(...); //调用CleanStart函数进行工程目录的清理工作;
  17.                 }
  18.                 return FALSE;
  19.         }
  20.         //command line end;
  21.         CProjCleanerDlg dlg;
  22.         m_pMainWnd = &dlg;
  23.         INT_PTR nResponse = dlg.DoModal();
  24.         if (nResponse == IDOK)
  25.         {
  26.         }
  27.         else if (nResponse == IDCANCEL)
  28.         {
  29.         }
  30.         return FALSE;
  31. }
实现方法:2、Windows Shell扩展接口来实现

1.创建工程这里我用的VS2005,创建一个ATL 项目,属性不需要改,点默认的就可以了。

2.右击项目,添加,类,添加一个新ATL简单对象。

3.编辑代码,关于IShellExtInit和IContextMenu接口可以查看MSDN,上面写的很详细。

// CCContextMenuExt

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。

// DllRegisterServer - 将项添加到系统注册表
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 自己注册。


原创粉丝点击