C++Builder创建上下文菜单扩展处理器
来源:优易学  2011-12-27 11:51:51   【优易学:中国教育考试门户网】   资料下载   IT书店

  当用户右击一个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方法
  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)。
  以下是一个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方法
  当在上下文菜单中选择一个菜单项时,shell就会调用InvokeCommand,告诉handler运行相关联的命令。在Shlobj.h中,参数pici被声明为CMINVOKECOMMANDINFO结构,但实际上,它经常指向CMINVOKECOMMANDINFOEX结构,这个结构是CMINVOKECOMMANDINFO的扩展版本,有几个成员允许传递Unicode字符串。
  CMINVOKECOMMANDINFO的成员简介如下:
  1) cbSize :结构的大小。
  2) fMask :为0,或下列标志的组合。
  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,这个成员被忽略。
  以下示例先打开一个"选择文件"的对话框,然后用所选择的程序打开在资源管理器中被选择的文件。为了简化,假定在资源管理器只选择了一个文件。
  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;
  }

[1] [2] 下一页

责任编辑:小草

文章搜索:
 相关文章
热点资讯
资讯快报
热门课程培训