l

成都手游码农一枚
随笔 - 32, 文章 - 0, 评论 - 117, 引用 - 0
数据加载中……

windows shell扩展右键菜单。

由于工作的需求要实现像winrara那样的shell扩展菜单,带图标的,针对某个类型的文件执行相应的操作,
查了不少资料,终于解决了,效果如下图中红色矩形部分。



最先想到的当然是最方便的,直接添加注册表,只需在HCLR中需要处理的类型(即右键选中的文件类型)的注册表项中的shell子键中加入新项,
修改默认值为显示名称,显示效果如上图的<用“记事本”打开>菜单项。(*)代表任意类型的文件。



然后再新项中加入command子键,默认值设置为运行程序的参数,如"notepad.exe %1”,其中%1代表右键选中的文件。

这样当点击菜单后就可以执行你指定的程序("notepad")并传入选中的文件名作为命令参数,这样就可以处理这个文件了。

其实这样足以满足需求了,但效果上还差了一点,很明显少了个图标。既然winrar都能实现,肯定是还有其它实现的方法了。

于是就是处理图标了,然而最终没能找到好的方法,但如果是win7的话,还是有的,那就是在command中加入icon项,值为

你的目标程序,然而如果是xp就行不通了。

本想简单的解决,但没想到还是得用上复杂的方法,仅仅为了一个图标 - -;。

复杂的方法只能是通过扩展shell接口来实现,首先肯定得牵涉到COM,所以关于COM可以上网查下资料,再这里就不多说(其实

让我讲我也不一定讲得清楚),这里我就写下得注意的地方就是了。

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, 0sizeof(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 自己注册。

下载地址:http://www.cppblog.com/Files/shly/CtxMenuExt.rar

注意:注册后的DLL无法删除,有种方法可以就是regsvr32 /u 取消注册dll, 然后重启explorer.exe。

posted on 2011-10-09 21:37 l1989 阅读(11829) 评论(8)  编辑 收藏 引用 所属分类: windows

评论

# re: windows shell扩展右键菜单。[未登录]  回复  更多评论   

很好收藏!
2011-10-11 11:13 | 饭中淹

# re: windows shell扩展右键菜单。  回复  更多评论   

总感觉COM入门太难
2011-10-11 17:31 | LoveBeyond

# re: windows shell扩展右键菜单。  回复  更多评论   

@LoveBeyond
嗯嗯,我也觉得好难,要不是受工作所迫,我才不会去看这些折磨自己。。。
2011-10-11 17:38 | azl

# re: windows shell扩展右键菜单。[未登录]  回复  更多评论   

我测试了下, 点击文件的右键菜单能显示。 但是点击文件夹就没有, 如果让文件夹的右键菜单中也增加???
2011-12-08 12:53 | WONDER

# re: windows shell扩展右键菜单。  回复  更多评论   

@WONDER
怎么点击文件夹也有自定义菜单,你实现了么
2013-04-09 20:07 | zxgwxit

# re: windows shell扩展右键菜单。[未登录]  回复  更多评论   

恩。很不错。对于简单的用第一种方法就可以了,不需要创建com组件。第二种创建com组件的方法,也可以学习一下。
2013-09-21 11:41 | 王小亮

# re: windows shell扩展右键菜单。[未登录]  回复  更多评论   

我拿这个例子操作了为什么,右键就是不见菜单呢,求帮助@WONDER
2013-10-07 21:04 | Jeremy

# re: windows shell扩展右键菜单。  回复  更多评论   

注册了重启explorer以后,右键菜单还是没有新加项嘛
2014-06-24 13:56 | forcj

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理