您的位置:首页 > 运维架构 > Shell

VC Windows shell扩展编程(为系统右键菜单添加二级菜单傻瓜教程)

2012-08-07 16:26 525 查看
打开VC6,新建一个工程,选ATL COM APPWIZARD,工程名写BlogTest。然后OK。如果要用到MFC,那把Support MFC打上勾,然后按完成。
新工程生成完毕后,在Class View里根结点按右键,选New Atl Object...,再选Simple Object,在short name里填上类名,我填Blog,其他会自动填写完毕,OK

在Blog.h文件头上先添加如下代码:

  #include "shlobj.h"

  #include "comdef.h"

  在class ATL_NO_VTABLE CBlog :后面添加如下代码:

  public IShellExtInit,

  public IContextMenu

  

  在EGIN_COM_MAP(CBlog)后面添加如下代码

  COM_INTERFACE_ENTRY(IShellExtInit)

  COM_INTERFACE_ENTRY(IContextMenu)

  

  在// IBlog后添加如下代码

  // IDesPdm

  protected:

  TCHAR m_szFile [MAX_PATH];

  public:

  // IShellExtInit

  STDMETHOD(Initialize)(LPCITEMIDLIST, LPDATAOBJECT, HKEY);

  public:

  // IContextMenu

  STDMETHOD(GetCommandString)(UINT, UINT, UINT*, LPSTR, UINT);

  STDMETHOD(InvokeCommand)(LPCMINVOKECOMMANDINFO);

  STDMETHOD(QueryContextMenu)(HMENU, UINT, UINT, UINT, UINT);

  到这里为止头文件已经定义好了,现在要去为Blog.cpp添加代码了。

  首先添加如下代码:

  HRESULT CBlog::Initialize ( LPCITEMIDLIST pidlFolder, LPDATAOBJECT pDataObj, HKEY hProgID )

  {

{

FORMATETC fmt = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };

STGMEDIUM stg = { TYMED_HGLOBAL };

HDROP hDrop;

TCHAR szFile [MAX_PATH];

int nNumFiles;

// 在数据对象内查找 CF_HDROP 型数据.

if ( FAILED( pDataObj->GetData ( &fmt, &stg )))

{

// Nope! Return an "invalid argument" error back to Explorer.

return E_INVALIDARG;

}

// 获得指向实际数据的指针

hDrop = (HDROP) GlobalLock ( stg.hGlobal );

// 检查非NULL.

if ( NULL == hDrop )

{

return E_INVALIDARG;

}

// 检查在该操作中有几个文件被选择.

nNumFiles = DragQueryFile ( hDrop, 0xFFFFFFFF, NULL, 0 );

if(0 == nNumFiles)

{

GlobalUnlock ( stg.hGlobal );

ReleaseStgMedium ( &stg );

return E_INVALIDARG;

}

/*// 有效性检查 – 保证最少有一个文件名.

UINT uNumFiles = DragQueryFile ( hDrop, 0xFFFFFFFF, NULL, 0 );

if ( 0 == uNumFiles )

{

GlobalUnlock ( stg.hGlobal );

ReleaseStgMedium ( &stg );

return E_INVALIDARG;

} */

for ( int uFile = 0; uFile < nNumFiles; uFile++ )

{

//取得下一个文件名.

if ( 0 == DragQueryFile ( hDrop,

uFile, szFile, MAX_PATH ))

continue;

//m_lsFiles.AddTail(szFile);

m_mapInt2StrFiles[uFile] = szFile;

} // end for

GlobalUnlock ( stg.hGlobal );

ReleaseStgMedium ( &stg );

return ( m_mapInt2StrFiles.size() > 0 ) ? S_OK : E_INVALIDARG;

}

  }

  这个函数用来抓取所有选中文件的绝对路径,保存到m_mapInt2StrFiles这个MAP里(MAP用法就不细说了,查阅STL相关内容)

  HRESULT CBlog::QueryContextMenu ( HMENU hmenu,UINT uMenuIndex, UINT uidFirstCmd, UINT uidLastCmd, UINT uFlags )

  {

UINT uCmdID = uidFirstCmd;

char *szMenuText_Popup = "自定义菜单";

   char *szMenuText_1 = "自定义菜单1...";

char *szMenuText_2 = "自定义菜单2...";

char *szMenuText_3 = "自定义菜单3...";

char *szMenuText_4 = "自定义菜单4...";

// 如果标志包含 CMF_DEFAULTONLY 我们不作任何事情.

if ( uFlags & CMF_DEFAULTONLY )

{

return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, 0 );

}

InsertMenu(hmenu, uMenuIndex, MF_SEPARATOR | MF_BYPOSITION, 0, NULL);

uMenuIndex++;

HMENU hSubMenu = CreateMenu();

if(hSubMenu)

{

InsertMenu(hSubMenu, 0, MF_STRING | MF_BYPOSITION, uCmdID++, szMenuText_1);

SetMenuItemBitmaps(hSubMenu, 0, MF_BYPOSITION, m_hRegBmp, m_hRegBmp);

InsertMenu(hSubMenu, 1, MF_STRING | MF_BYPOSITION, uCmdID++, szMenuText_2);

SetMenuItemBitmaps(hSubMenu, 1, MF_BYPOSITION, m_hRegBmp, m_hRegBmp);

//InsertMenu(hSubMenu, 2, MF_SEPARATOR | MF_BYPOSITION, 0, NULL);

InsertMenu(hSubMenu, 2, MF_STRING | MF_BYPOSITION, uCmdID++, szMenuText_3);

SetMenuItemBitmaps(hSubMenu, 2, MF_BYPOSITION, m_hRegBmp, m_hRegBmp);

InsertMenu(hSubMenu, 3, MF_STRING | MF_BYPOSITION, uCmdID++, szMenuText_4);

SetMenuItemBitmaps(hSubMenu, 3, MF_BYPOSITION, m_hRegBmp, m_hRegBmp);

}

InsertMenu(hmenu, uMenuIndex, MF_STRING | MF_POPUP | MF_BYPOSITION, (UINT_PTR)hSubMenu, szMenuText_Popup);

uMenuIndex++;

  InsertMenu(hmenu, uMenuIndex, MF_SEPARATOR | MF_BYPOSITION, 0, NULL);

uMenuIndex++;

//最后告诉浏览器我们添加了几个菜单项

return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, uCmdID );

  }

这个函数就是添加菜单项的函数。

  InsertMenu(hmenu, uMenuIndex, MF_SEPARATOR | MF_BYPOSITION, 0, NULL);这一句加的是一个空菜单,显示时就是一个菜单里的分隔符。

  SetMenuItemBitmaps(hSubMenu, 2, MF_BYPOSITION, m_hRegBmp, m_hRegBmp);这一句是设定菜单所对应的图标,可在资源中添加一张BMP图,ID设为ID_BMP1,然后在构造函数里加如下代码:

  m_hRegBmp = LoadBitmap ( _Module.GetModuleInstance(),

MAKEINTRESOURCE(IDB_GREATSKYBMP) );

  在头文件里加:HBITMAP m_hRegBmp;

  HRESULT CDesPdm::GetCommandString( UINT idCmd, UINT uFlags,UINT* pwReserved, LPSTR pszName, UINT cchMax )

  {

USES_CONVERSION;

LPCTSTR szPrompt;

// 如果 Explorer 要求帮助字符串,就将它拷贝到提供的缓冲区中.

if ( uFlags & GCS_HELPTEXT )

{

switch ( idCmd )

{

case 0:

szPrompt = _T("自定义菜单1");

break;

case 1:

szPrompt = _T("自定义菜单2");

break;

case 2:

szPrompt = _T("自定义菜单3");

break;

  case 3:

szPrompt = _T("自定义菜单4");

break;

default:

ATLASSERT(0); // should never get here

return E_INVALIDARG;

break;

}

if ( uFlags & GCS_UNICODE )

{

// 我们需要将 pszName 转化为一个 Unicode 字符串, 接着使用Unicode字符串拷贝 API.

lstrcpynW ( (LPWSTR) pszName, T2CW(szPrompt), cchMax );

}

else

{

// 使用 ANSI 字符串拷贝API 来返回帮助字符串.

lstrcpynA ( pszName, T2CA(szPrompt), cchMax );

}

return S_OK;

}

return E_INVALIDARG;

  }

  这个函数是响应资源管理器左下角的帮助信息

  HRESULT CDesPdm::InvokeCommand ( LPCMINVOKECOMMANDINFO pCmdInfo )

  {

//此语句用来正确地切换MFC模块状态

AFX_MANAGE_STATE(AfxGetStaticModuleState());

CString strCmd = "";

// 如果lpVerb 实际指向一个字符串, 忽略此次调用并退出.

if ( 0 != HIWORD( pCmdInfo->lpVerb ))

return E_INVALIDARG;

// 点击的命令索引 – 在这里,唯一合法的索引为0.

switch ( LOWORD( pCmdInfo->lpVerb ))

{

case 0:

{

//执行自定义菜单1的操作

break;

}

case 1:

{

//执行自定义菜单2的操作

break;

}

case 2:

{

//执行自定义菜单3的操作

break;

}

case 3:

{

//执行自定义菜单4的操作

break;

}

default:

{

return E_INVALIDARG;

break;

}

}

return S_OK;

  }

这就是点选了相应菜单后要执行相应操作的函数。

  打开工程里的.idl文件,可以看到有三个不同的注册码,选择最下面一条,我的例子程序是EA29B300-3EA0-4DD2-B2F8-3CC519BFA948

  打开工程里的.rgs文件,删除原来的代码替换成如下代码。

  HKCR

  {

NoRemove *

{

NoRemove ShellEx

{

NoRemove ContextMenuHandlers

{

ForceRemove Blog = s '{EA29B300-3EA0-4DD2-B2F8-3CC519BFA948}'

}

}

}

NoRemove Folder

{

NoRemove ShellEx

{

NoRemove ContextMenuHandlers

{

ForceRemove Blog = s '{EA29B300-3EA0-4DD2-B2F8-3CC519BFA948}'

}

}

}

  }

这是供生成的DLL文件在注册表里注册用的,*表示适用于所有文件,Folder表示适用于文件夹。

  用regsvr32命令注册生成的DLL控件,重启Explore就可查看效果了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: