转至繁体中文版     | 网站首页 | 图文教程 | 资源下载 | 站长博客 | 图片素材 | 武汉seo | 武汉网站优化 | 
最新公告:     敏韬网|教学资源学习资料永久免费分享站!  [mintao  2008年9月2日]        
您现在的位置: 学习笔记 >> 图文教程 >> 软件开发 >> Delphi程序 >> 正文
Windows外壳扩展编程之添加右键菜单         ★★★★

Windows外壳扩展编程之添加右键菜单

作者:闵涛 文章来源:闵涛的学习笔记 点击数:1982 更新时间:2009/4/23 18:26:02

Windows外壳扩展编程  www.applevb.com
    在Windows下的一些软件提供了这样的功能:当安装了这些软件之后,当在Windows的Explore中鼠标右键单击文件或者文件夹后,在弹出菜单中就会多出与该软件操作相关的菜单项,点击该项就会激活相应的程序对用户选中的文件进行相应的操作。例如安装了Winzip之后,当用户选中一个文件夹后单击右键,在弹出菜单中就会多出一个Add To Zip和一个 Add To xxx.zip的选项,其中xxx为选中的文件夹的名称。只要单击上面的两个菜单项中的一个,就可以方便的压缩目录了。这样的功能称为Windows外壳扩展(Shell Extensions)
  外壳扩展概述

  下面是与外壳扩展相关的三个重要术语:
  (1)文件对象(File Object)
     文件对象是外壳中的一项,大家最熟识的文件对象是文件和目录,此外,打印机、控制面板程序、共享网
       络等也都是文件对象。
  (2)文件类(File Class)
       文件类是具有某种共同特性的文件对象的集合,比如,扩展名相同的文件属于同一文件类。
  (3)处理程序(Handler)
     处理程序是具体实现某个外壳扩展的代码。

  Windows支持七种类型的外壳扩展(称为Handler),它们相应的作用简述如下:
  (1)Context menu handlers向特定类型的文件对象增添上下文相关菜单;
  (2)Drag-and-drop handlers用来支持当用户对某种类型的文件对象进行拖放操作时的OLE数据传输;
  (3)Icon handlers用来向某个文件对象提供一个特有的图标,也可以给某一类文件对象指定图标;
  (4)Property sheet handlers给文件对象增添属性页,属性页可以为同一类文件对象所共有,也可以给一个
       文件对象指定特有的属性页;
  (5)Copy-hook handlers在文件夹对象或者打印机对象被拷贝、移动、删除和重命名时,就会被系统调用,
       通过为Windows增加Copy-hook handlers,可以允许或者禁止其中的某些操作;
  (6)Drop target handlers在一个对象被拖放到另一个对象上时,就会被系统被调用;
  (7)Data object handlers在文件被拖放、拷贝或者粘贴时,就会被系统被调用。

  Windows的所有外壳扩展都是基于COM(Component Object Model) 组件模型的,外壳是通过接口(Interface)来访问对象的。外壳扩展被设计成32位的进程中服务器程序,并且都是以动态链接库的形式为操作系统提供服务的。因此,如果要对Windows的用户界面进行扩充的话,则具备写COM对象的一些知识是十分必要的。

  写好外壳扩展程序后,必须将它们注册才能生效。所有的外壳扩展都必须在Windows注册表的HKEY_CLASSES_ROOT\CLSID键之下进行注册。在该键下面可以找到许多名字像{0000002F-0000-0000-C000-000000000046}的键,这类键就是全局唯一类标识符。每一个外壳扩展都必须有一个全局唯一类标识符,Windows正是通过此唯一类标识符来找到外壳扩展处理程序的。在类标识符之下的InProcServer32子键下记录着外壳扩展动态链接库在系统中的位置。与某种文件类型关联的外壳扩展注册在相应类型的shellex主键下。如果所处的Windows操作系统为Windows NT,则外壳扩展还必须在注册表中的HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\ShellExtensions\Approved主键下登记。

  注册表HKEY_CLASSES_ROOT主键下有几个特殊的子键,如*、Folder、Drive以及Printer。如果把外壳扩展注册在*子键下,那么这个外壳扩展将对Windows中所有类型的文件有效;如果把外壳扩展注册在Folder子键下,则对所有目录有效。

    上面提到的在Windows Explore中在鼠标右键菜单中添加菜单项(我们成为上下文相关菜单)的操作属于外壳扩展的第一类,即Context menu handlers向特定类型的文件对象增添上下文相关菜单。要动态地在上下文相关菜单中增添菜单项,可以通过写Context Menu Handler来实现。
  编写Context Menu Handler必须实现IShellExtInit和IContextMenu两个接口。除了IUnknown接口所定义的函数之外,Context Menu Handler还需要用到QueryContextMenu、InvokeCommand和GetCommandString这三个非常重要的成员函数。

  (1)QueryContextMenu函数:每当系统要显示一个文件对象的上下文相关菜单时,它首先要调用该函数。为了在上下文相关菜单中添加菜单
 项,我们在该函数中调用InsertMenu函数。

  (2)InvokeCommand函数:当用户选定了某个Context Menu Handler登记过的菜单项后,该函数将会被调用,系统将会传给该函数一个指向
 LPCMINVOKECOMMANDINFO结构的指针。在该函数中要执行与所选菜单项相对应的操作。

  (3)GetCommandString函数:当鼠标指针移到一个上下文相关菜单项上时,在当前窗口的状态条上将会出现与该菜单项相关的帮助信息,此
 信息就是系统通过调用该函数获取的。

    下面我通过具体的例程来说明编写一个比较完整的上下文菜单程序,这个程序是一个文件操作程序,当安装并注册了外壳扩展的服务器动态连接库之后,当选择一个或者多个文件并单击鼠标右键后,在右键菜单中就会多出一个“执行文件操作”的上下文菜单,点击菜单就会弹出相应的程序执行文件操作。
    在整个程序的编写中,外壳扩展的服务器动态连接库是有Delphi4.0编写的,而动态连接库调用的文件操作程序是由VB6编写的。下面首先介绍服务器动态连接库的编写:
    服务器动态连接库的工程文件内容如下:

library contextmenu;
    uses
ComServ,
  ContextMenuHandler in ''''Unit2.pas'''';
//   contmenu_TLB in ''''contmenu_TLB.pas'''';

exports
   DllGetClassObject,
   DllCanUnloadNow,
   DllRegisterServer,
   DllUnregisterServer;

{$R *.TLB}

{$R *.RES}

begin

end.

    将工程文件保存为contextmenu.dpr。
    服务器动态连接库的单位文件内容如下:   

unit ContextMenuHandler;

interface
   uses Windows,ActiveX,ComObj,ShlObj,Classes;

type
   TContextMenu = class(TComObject,IShellExtInit,IContextMenu)
   private
      FFileName: array[0..MAX_PATH] of Char;
   protected
      function IShellExtInit.Initialize = SEIInitialize; // Avoid compiler warning
      function SEIInitialize(pidlFolder: PItemIDList; lpdobj: IDataObject;
               hKeyProgID: HKEY): HResult; stdcall;
      function QueryContextMenu(Menu: HMENU; indexMenu, idCmdFirst, idCmdLast,
               uFlags: UINT): HResult; stdcall;
      function InvokeCommand(var lpici: TCMInvokeCommandInfo): HResult; stdcall;
      function GetCommandString(idCmd, uType: UINT; pwReserved: PUINT;
               pszName: LPSTR; cchMax: UINT): HResult; stdcall;
end;

const

   Class_ContextMenu: TGUID = ''''{19741013-C829-11D1-8233-0020AF3E97A9}'''';

{全局唯一标识符(GUID)是一个16字节(128为)的值,它唯一地标识一个接口(interface)}
var
   FileList:TStringList;
   Buffer:array[1..1024]of char;

implementation

uses ComServ, SysUtils, ShellApi, Registry,UnitForm;

function TContextMenu.SEIInitialize(pidlFolder: PItemIDList; lpdobj: IDataObject;
   hKeyProgID: HKEY): HResult;
var
   StgMedium: TStgMedium;
   FormatEtc: TFormatEtc;
   FileNumber,i:Integer;
begin
   file://如果lpdobj等于Nil,则本调用失败
   if (lpdobj = nil) then begin
      Result := E_INVALIDARG;
      Exit;
   end;

   file://首先初始化并清空FileList以添加文件
   FileList:=TStringList.Create;
   FileList.Clear;
   file://初始化剪贴版格式文件
   with FormatEtc do begin
      cfFormat := CF_HDROP;
      ptd := nil;
      dwAspect := DVASPECT_CONTENT;
      lindex := -1;
      tymed := TYMED_HGLOBAL;
   end;
   Result := lpdobj.GetData(FormatEtc, StgMedium);
   if Failed(Result) then Exit;

   file://首先查询用户选中的文件的个数
   FileNumber := DragQueryFile(StgMedium.hGlobal,$FFFFFFFF,nil,0);
   file://循环读取,将所有用户选中的文件保存到FileList中
   for i:=0 to FileNumber-1 do begin
      DragQueryFile(StgMedium.hGlobal, i, FFileName, SizeOf(FFileName));
      FileList.Add(FFileName);
      Result := NOERROR;
   end;

   ReleaseStgMedium(StgMedium);
end;

function TContextMenu.QueryContextMenu(Menu: HMENU; indexMenu, idCmdFirst,
   idCmdLast, uFlags: UINT): HResult;
begin
   Result := 0;
   if ((uFlags and $0000000F) = CMF_NORMAL) or
      ((uFlags and CMF_EXPLORE) <> 0) then begin
 // 往Context Menu中加入一个菜单项
    InsertMenu(Menu, indexMenu, MF_STRING or MF_BYPOSITION, idCmdFirst,
      PChar(''''执行文件操作''''));
 // 返回增加菜单项的个数
   Result := 1;
   end;
end;

function TContextMenu.InvokeCommand(var lpici: TCMInvokeCommandInfo): HResult;
var
//   sFile:TFileStream;
   charSavePath:array[0..1023]of char;
   sSaveFile:String;
   i:Integer;
   F: TextFile;
   FirstLine: string;
begin
   // 首先确定该过程是被系统而不是被一个程序所调用
   if (HiWord(Integer(lpici.lpVerb)) <> 0) then
   begin
      Result := E_FAIL;
      Exit;
   end;
   // 确定传递的参数的有效性
   if (LoWord(lpici.lpVerb) <> 0) then begin
      Result := E_INVALIDARG;
      Exit;
   end;

   file://建立一个临时文件保存用户选中的文件名
   GetTempPath(1024,charSavePath);
   sSaveFile:=charSavePath+''''chen0001.tmp'''';

   AssignFile(F,sSaveFile);   { next file in Files property }
   ReWrite(F);
   file://将文件名保存到临时文件中
   for i:= 0 to FileList.Count -1 do begin
      FirstLine:=FileList.Strings[i];
      Writeln(F,FirstLine);    { Read the first line out of the file }
   end;
   CloseFile(F);
   file://调用文件操作程序对用户选中的文件进行操作
   ShellExecute(0,nil,''''c:\FileOP.exe'''',PChar(sSaveFile),charSavePath,SW_NORMAL);

   Result := NOERROR;
end;

 

function TContextMenu.GetCommandString(idCmd, uType: UINT; pwReserved: PUINT;
         pszName: LPSTR; cchMax: UINT): HRESULT;
begin
   if (idCmd = 0) then begin
   if (uType = GCS_HELPTEXT) then
      {返回该菜单项的帮助信息,此帮助信息将在用户把鼠标移动到该菜单项时出现在状态条上。}
      StrCopy(pszName, PChar(''''点击该菜单项将执行文件操作''''));
      Result := NOERROR;
   end
   else
      Result := E_INVALIDARG;
end;

 

type
   TContextMenuFactory = class(TComObjectFactory)
   public
   procedure UpdateRegistry(Register: Boolean); override;
end;

 

procedure TContextMenuFactory.UpdateRegistry(Register: Boolean);
var
   ClassID: string;
begin
 

[1] [2] [3]  下一页


没有相关教程
教程录入:mintao    责任编辑:mintao 
  • 上一篇教程:

  • 下一篇教程:
  • 【字体: 】【发表评论】【加入收藏】【告诉好友】【打印此文】【关闭窗口
      注:本站部分文章源于互联网,版权归原作者所有!如有侵权,请原作者与本站联系,本站将立即删除! 本站文章除特别注明外均可转载,但需注明出处! [MinTao学以致用网]
      网友评论:(只显示最新10条。评论内容只代表网友观点,与本站立场无关!)

    同类栏目
    · C语言系列  · VB.NET程序
    · JAVA开发  · Delphi程序
    · 脚本语言
    更多内容
    热门推荐 更多内容
  • 没有教程
  • 赞助链接
    更多内容
    闵涛博文 更多关于武汉SEO的内容
    500 - 内部服务器错误。

    500 - 内部服务器错误。

    您查找的资源存在问题,因而无法显示。

    | 设为首页 |加入收藏 | 联系站长 | 友情链接 | 版权申明 | 广告服务
    MinTao学以致用网

    Copyright @ 2007-2012 敏韬网(敏而好学,文韬武略--MinTao.Net)(学习笔记) Inc All Rights Reserved.
    闵涛 投放广告、内容合作请Q我! E_mail:admin@mintao.net(欢迎提供学习资源)

    站长:MinTao ICP备案号:鄂ICP备11006601号-18

    闵涛站盟:医药大全-武穴网A打造BCD……
    咸宁网络警察报警平台