当前位置:天才代写 > tutorial > C语言/C++ 教程 > 用C++ Builder建设上下文菜单扩展处理惩罚器

用C++ Builder建设上下文菜单扩展处理惩罚器

2017-11-06 08:00 星期一 所属: C语言/C++ 教程 浏览:624

副标题#e#

当用户右击一个shell工具时,shell会显示它的上下文菜单。文件系统工具有大量的尺度菜单项,如"剪切"和"拷贝",这些都是缺省的菜单项。假如工具是一个文件,是文件类的成员,就可以或许在注册内外指定附加的菜单项。Shell查抄注册表,看看文件范例是否与一些上下文菜单handler相关联,假如是,shell会咨询这些handler是否添加特另外菜单项。

上下文菜单handler是一种shell扩展handler,它添加呼吁到已有的上下文菜单中。上下文菜单handler都与特定的文件类相关联,而且在显示这类文件的成员的上下文菜单时挪用。通过实现和注册这样一个handler,可以或许动态地添加菜单项到工具的上下文菜单上,从而为非凡的工具定制菜单。

上下文菜单Handler的事情道理

作为一种shell扩展handler,上下文菜单handler同所有其它handler一样, 是历程内COM 工具,即工具作为动态毗连库 (DLL)实现。除了IUnknown接口外,上下文菜单还必需导出IShellExtInit和IContextMenu接口,作为选择,上下文菜单也能导出IContextMenu2和IContextMenu3,这些接口可以实现自画菜单项。

IShellExtInit接口仅仅被shell用来初始化handler,主要的操纵通过handler的IContextMenu接口举办。Shell首先挪用IContextMenu::QueryContextMenu,传送一个HMENU句柄,这个要领用它来增加上下文菜单。假如用户亮选了这些新添加的某个呼吁项, IContextMenu::GetCommandString将被挪用,以取得这条菜单的辅佐信息,把它显示在资源打点器的状态条上。假如用户单击了handler的条目,shell挪用IContextMenu::InvokeCommand,从而handler可以或许执行符合的操纵。

实现IContextMenu接口

1、实现QueryContextMenu要领


#p#副标题#e#

Shell通过挪用IContextMenu::QueryContextMenu,答允handler把它的菜单项添加到菜单中。QueryContextMenu共有5个参数,各参数浸染如下:

1) Hmenu:HMENU范例,暗示上下文菜单的句柄。
2) IndexMenu:第一个被添加的菜单索引。
3) IdCmdFirst:添加的菜单ID初值。
4) idCmdLast:添加的菜单ID最大值。
5) uFlags:与上下文菜单相关的状态符号,共有3种,如下:

CMF_DEFAULTONLY 用户选择了缺省的呼吁,凡是是通过双击工具发生。QueryContextMenu 在把节制返回给shell前不该该修改菜单。

CMF_NODEFAULT 菜单没有缺省的条目,这个要领应该把它的呼吁加到菜单中。

CMF_NORMAL 上下文菜单将被正常显示,这个要领应该把它的呼吁加到菜单中。

必需留意的是,任何添加的菜单项的ID必需落在idCmdFirst和idCmdLast两个参数中间,凡是,添加的第一个菜单项ID设为idCmdFirst,今后每添加一个菜单项,就把ID加1,这样,纵然shell挪用了不止一个handler,也可以确保菜单项的ID不高出idCmdLast和大概的ID最大值。
在ID和idCmdFirst之间,菜单项ID的command offset(呼吁偏移)是差异的,应该生存handler添加到上下文菜单中的每个菜单项的offset,因为假如shell按顺序挪用GetCommandString可能InvokeCommand,可以利用它来辨别菜单项的ID。

还应该为每一个添加的呼吁赋予一个verb。Verb是语言独立的字符串,当挪用InvokeCommand时,经常用verb来取代偏移以辨别呼吁。
QueryContextMenu 要领利用InsertMenu或InsertMenuItem 添加新的菜单项,然后返回一个严格配置为SEVERITY_SUCCESS的HRESULT值,把它的值配置为被分派的最大的呼吁ID。譬喻,如果idCmdFirst是5,添加了3个菜单项,ID别离是5,7,8,则返回值应该是MAKE_HRESULT(SEVERITY_SUCCESS, 0, 8 – 5 + 1)。

#p#副标题#e#

以下是一个QueryContextMenu实例:

HRESULT __stdcall TAddContextMenuImpl::QueryContextMenu(HMENU hmenu,
     UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
{
  if(!(CMF_DEFAULTONLY & uFlags))
  {
   InsertMenu(hmenu, indexMenu, MF_STRING | MF_BYPOSITION,idCmdFirst,
   _T("选择打开方法..."));
   return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 1);
  }
  return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(0));
}

2、实现GetCommandString 要领

假如用户高亮了一个handler添加的菜单项,shell将挪用handler的GetCommandString要领。这个要领需要通报菜单项的偏移值(ID)、指定信息范例的符号、一个预留的参数、一个字符串缓冲区以及缓冲区的巨细。

一般,这个要领可以不消处理惩罚,以下示例措施直接返回S_OK。

HRESULT __stdcall TAddContextMenuImpl::GetCommandString(UINT idCmd, UINT uFlags,
UINT *pwReserved, LPSTR pszName, UINT cchMax)
{
  return S_OK;
}

3、实现InvokeCommand要领

#p#分页标题#e#

当在上下文菜单中选择一个菜单项时,shell就会挪用InvokeCommand,汇报handler运行相关联的呼吁。在Shlobj.h中,参数pici被声明为CMINVOKECOMMANDINFO布局,但实际上,它常常指向CMINVOKECOMMANDINFOEX布局,这个布局是CMINVOKECOMMANDINFO的扩展版本,有几个成员答允通报Unicode字符串。

CMINVOKECOMMANDINFO的成员简介如下:

1) cbSize :布局的巨细。

2) fMask :为0,或下列符号的组合。

#p#副标题#e#

CMIC_MASK_ASYNCOK 在返回之前期待DDE会话竣事

CMIC_MASK_FLAG_NO_UI 当执行呼吁时,系统防备显示用户接口元素(如错误信息)

CMIC_MASK_HOTKEY dwHotKey 成员有效

CMIC_MASK_ICON hIcon成员有效

CMIC_MASK_NO_CONSOLE 假如上下文菜单handler必需建设新历程,正常环境下将建设一个节制台,配置CMIC_MASK_NO_CONSOLE符号可以克制建设新的节制台

3) hwnd :拥有上下文菜单窗口的句柄,handler可以利用这个句柄显示本身的信息提示框和对话框。

4) lpVerb :32位值,高位字包括0,低位字是呼吁的菜单ID偏移。当用户选择一个菜单呼吁时,Shell用MAKEINTRESOURCE宏发生这个值,假如高位字不是0,那么这个成员指向一个以NULL末了的字符串,指出呼吁的语言无关的名称,即上文的verb。典范环境下,当呼吁被一个应用措施激活时,这个成员是一个字符串。系统提供了下面几个预界说的常数值:

值:CMDSTR_NEWFOLDER  字符串:"NewFolder"

值:CMDSTR_VIEWDETAILS  字符串:"ViewDetails"

值:CMDSTR_VIEWLIST  字符串:"ViewList"

5) lpParameters :呼吁传送的参数字符串,对付shell扩展插入的菜单项,这个成员老是NULL。

6) lpDirectory :目次名称,对付shell扩展插入的菜单项,这个成员老是NULL。

7) nShow :显示窗口或启动应用措施时,通报给ShowWindow函数的参数。

8) dwHotKey :分派给被呼吁激活的应用措施的热键。假如fMask 不是CMIC_MASK_HOTKEY,这个成员被忽略。

9) hIcon :被呼吁激活的应用措施利用的图标。假如fMask 不是CMIC_MASK_ICON,这个成员被忽略。

以下示例先打开一个"选择文件"的对话框,然后用所选择的措施打开在资源打点器中被选择的文件。为了简化,假定在资源打点器只选择了一个文件。

#p#副标题#e# HRESULT __stdcall TAddContextMenuImpl::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
{
  if(HIWORD(pici->lpVerb)==0)
  {
   if(LOWORD(pici->lpVerb)==0) // 添加的第一个菜单项
   {
     TOpenDialog *Dlg=new TOpenDialog(NULL);
     Dlg->Title="打开\"";
     Dlg->Title=Dlg->Title+g_szFilePath+"\"";
     Dlg->Options.Clear();
     Dlg->Options << ofFileMustExist << ofPathMustExist << ofNoChangeDir;
     if(Dlg->Execute())
     {
      ShellExecute(pici->hwnd,"open",Dlg->FileName.c_str(),g_szFilePath,NULL,SW_SHOW);      }
     return S_OK;
   }
  }
  return S_FALSE;
}

注册上下文菜单Handler

上下文菜单与文件类可能文件夹相关联。对付文件类,handler注册在文件类的HKEY_CLASSES_ROOT\ProgID\Shellex\ContextMenuHandlers子键下。在ContextMenuHandlers下建设一个以handler子键,把子键的缺省值配置为handler的CLSID的字符串值,就可以完成注册。

也可以或许把handler关联到文件夹,注册的要领与上面雷同,不外是在HKEY_CLASSES_ROOT\FolderType\Shellex\ContextMenuHandlers增加子键, 个中的FolderType 是文件夹范例的名称。

假如一个文件类有上下文菜单与它关联,那么双击一个工具将自动启动缺省的呼吁,而不会挪用handler的QueryContextMenu要领。当工具被双击时,为了指定挪用handler的QueryContextMenu要领,必需在handler的CLSID键下建设一个ShellEx\MayChangeDefaultMenu的子键。这样,当与handler关联的工具被双击时,QueryContextMenu 被挪用,并且uFlags参数会包括CMF_DEFAULTONLY 符号。

#p#副标题#e#

留意,假如配置了MayChangeDefaultMenu键,当一个关联的项目被双击时,会强制系统载入handler的DLL。假如handler不改变缺省行动,就不该该配置MayChangeDefaultMenu,不然会引起系统不须腹地载入这个DLL。仅仅当在大概改变上下文菜单的缺省行动时,才应该在配置上下文菜单handler的这个值。

建设工程

作为Borland的产物,用C++ Builder建设shell扩展的进程与Delphi有雷同之处,但它究竟是C++语言,所以也有与VC雷同之的处所。

#p#分页标题#e#

1. 选择File菜单的New菜单项,翻到New Items对话框的ActiveX页,双击ActiveX Library项,建设一个新的COM工程,把工程定名为MyContextMenu。从New Items 对话框的ActiveX页选择COM Object项,将打开COM Server领导。把"COClass"改为AddContextMenu,选择Apartment线程模式。其它不要改写。C++ Builder自动发生一个接口和一个类。默认的类名是TAddContextMenuImpl,回收自动生成的IAddContextMenu接口。我们必需本身添加新的接口IShellExtInit和IContextMenu,如下所示,粗体是添加的内容:

#include <shlobj.h> // 声明IShellExtInit和IContextMenu的头文件
class ATL_NO_VTABLE TAddContextMenuImpl :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<TAddContextMenuImpl, &CLSID_AddContextMenu>,
public IShellExtInit,
public IContextMenu,
public IAddContextMenu
{
  private:
  char g_szFilePath[MAX_PATH];
  public:
  … …
  BEGIN_COM_MAP(TAddContextMenuImpl)
  COM_INTERFACE_ENTRY(IAddContextMenu)
  COM_INTERFACE_ENTRY(IContextMenu) // 导出IContextMenu接口
  COM_INTERFACE_ENTRY(IShellExtInit) // 导出IShellExtInit接口
  END_COM_MAP()
  … …
};

2. 实现IShellExtInit接口的Initialize要领,在类界说中增加如下内容:

#p#副标题#e#

STDMETHOD (Initialize)(LPCITEMIDLIST pidlFolder,LPDATAOBJECT lpdobj,HKEY hkeyProgID);

Initialize要领的代码如下,从lpdobj工具中取出资源打点器中选择的文件名,措施假定只选择了一个文件。

HRESULT __stdcall TAddContextMenuImpl::
Initialize(LPCITEMIDLIST pidlFolder, LPDATAOBJECT lpdobj, HKEY hkeyProgID)
{
  FORMATETC fmt = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
  STGMEDIUM stg = { TYMED_HGLOBAL };
  HDROP hDrop;
  if (FAILED(lpdobj->GetData(&fmt, &stg))) return E_FAIL;
  hDrop = (HDROP)GlobalLock(stg.hGlobal);
  if ( hDrop == NULL)
  {
    ReleaseStgMedium(&stg);
    return E_OUTOFMEMORY;
  }
  DragQueryFile(hDrop, 0, g_szFilePath, MAX_PATH);
  GlobalUnlock(stg.hGlobal);
  ReleaseStgMedium(&stg);
  return S_OK;
}

3. 实现IContextMenu接口的各个要领,内容如上文所示,声明如下:

public:
STDMETHOD (QueryContextMenu)(HMENU hmenu,UINT indexMenu,UINT idCmdFirst,UINT idCmdLast,UINT uFlags);
STDMETHOD (InvokeCommand)(LPCMINVOKECOMMANDINFO pici);
STDMETHOD (GetCommandString)(UINT idCmd,UINT uFlags,UINT *pwReserved,LPSTR pszName,UINT cchMax);

最后,把工程编译为DLL文件,运行菜单[Run->Register ActiveX Server],把DLL注册。与Delphi和VC对比,C++ Builder好像有些缺陷。首先,它实现时过分巨大,生成的文件一大堆。最贫苦的是,它无法实现自动注册为shell扩展,它没有VC的rgs文件,像Delphi那样改写UpdateRegistry函数,怎么也不可,仿佛这个函数没有挪用一样。无奈,只好本身动手向注册表添加必需的项目(如图)。可是,C++ Builder给出了3个CLSID,很疑惑人,正确的CLSID应该是类AddContextMenu的,C++ Builder给它定名为CLSID_AddContextMenu。

用C++ Builder建树上下文菜单扩展处理惩罚处罚器

注册后,在资源打点器右击任何文件,如readme.txt,都将打开一个选择文件的对话框,然后shell用选择的文件打开readme.txt。

 

    关键字:

天才代写-代写联系方式