`
cloudtech
  • 浏览: 4601807 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
文章分类
社区版块
存档分类
最新评论

自己动手写一个简单的Windows shell扩展程序

 
阅读更多

作者:朱金灿

来源:http://blog.csdn.net/clever101

关于什么叫Windowsshell扩展程序,这里不作介绍,不懂的同学请google之。

一.Shell程序编写

这里采用的开发环境为WindowsXP+sp3, VS 2005 + sp1 (应该支持VS 2005以上的VS版本,VC 6.0估计不支持)。

1.新建一个ATL项目,输入工程名:ImportShell,具体如下图:

2. 在应用程序设置中的服务器类型中选择:动态链接库(DLL),其它选项采用默认设置,具体如下图:

这样单击完成后就新建了ATL工程。

3.新建一个ATL简单对象(英文版的VS为ATLSimple Object),具体如下图:

4.输入一个简称:ImportShellExt,其它的VS会帮你自动填写,具体如下图:


新建CImportShellExt类需要新继承两个基类:IShellExtInit和IContextMenu。新加的接口函数主要有四个:

当我们的shell扩展被加载时, Explorer 将调用我们所实现的COM对象的 QueryInterface() 函数以取得一个 IShellExtInit 接口指针.

该接口仅有一个方法 Initialize(), 其函数原型为:

HRESULTIShellExtInit::Initialize ( LPCITEMIDLIST pidlFolder, LPDATAOBJECT pDataObj,HKEY hProgID );

Explorer 使用该方法传递给我们各种各样的信息.

PidlFolder是用户所选择操作的文件所在的文件夹的 PIDL 变量. (一个 PIDL [指向ID列表的指针] 是一个数据结构,它唯一地标识了在Shell命名空间的任何对象, 一个Shell命名空间中的对象可以是也可以不是真实的文件系统中的对象.)

pDataObj 是一个IDataObject 接口指针,通过它我们可以获取用户所选择操作的文件名。

hProgID 是一个HKEY注册表键变量,可以用它获取我们的DLL的注册数据.

一旦 Explorer 初始化了扩展,它就会接着调用 IContextMenu 的方法让我们添加菜单项, 提供状态栏上的提示, 并响应执行用户的选择。

添加IContextMenu 方法的函数原型: public:


修改上下文菜单IContextMenu 有三个方法.

第一个是QueryContextMenu(), 它让我们可以修改上下文菜单. 其原型为:


hmenu 上下文菜单句柄.

uMenuIndex 是我们应该添加菜单项的起始位置.

uidFirstCmd 和 uidLastCmd 是我们可以使用的菜单命令ID值的范围.

uFlags 标识了Explorer 调用QueryContextMenu()的原因。

而返回值根据你所查阅的文档的不同而不同.
Dino Esposito 的书中说返回值是你所添加的菜单项的个数.
而 VC6.0所带的MSDN 又说它是我们添加的最后一个菜单项的命令ID加上1.
而最新的 MSDN 又说:
将返回值设为你为各菜单项分配的命令ID的最大差值,加上1.
例如, 假设
idCmdFirst 设为5,而你添加了三个菜单项 ,命令ID分别为 5, 7, 和8.
这时返回值就应该是: MAKE_HRESULT(SEVERITY_SUCCESS, 0, 8 - 5 + 1).

我是一直按 Dino 的解释来做的, 而且工作得很好.
实际上, 他的方法与最新的 MSDN 是一致的, 只要你严格地使用 uidFirstCmd作为第一个菜单项的ID,再对接续的菜单项ID每次加1.

我们暂时的扩展仅加入一个菜单项,所以
QueryContextMenu() 非常简单:


首先我们检查 uFlags.
你可以在 MSDN中找到所有标志的解释, 但对于上下文菜单扩展而言, 只有一个值是重要的:
CMF_DEFAULTONLY.
该标志告诉Shell命名空间扩展保留默认的菜单项,这时我们的Shell扩展就不应该加入任何定制的菜单项,这也是为什么此时我们要返回 0 的原因.
如果该标志没有被设置, 我们就可以修改菜单了 (使用
hmenu 句柄), 并返回 1 告诉Shell我们添加了一个菜单项。

下一个要被调用的IContextMenu 方法是 GetCommandString().如果用户是在浏览器窗口中右击文本文件,或选中一个文本文件后单击文件菜单时,状态栏会显示提示帮助.
我们的
GetCommandString() 函数将返回一个帮助字符串供浏览器显示.

GetCommandString() 的原型是:


idCmd 是一个以0为基数的计数器,标识了哪个菜单项被选择.
因为我们只有一个菜单项, 所以
idCmd 总是0. 但如果我们添加了3个菜单项, idCmd 可能是0, 1, 或 2.
uFlags 是另一组标志(我以后会讨论到的).
PwReserved 可以被忽略.
pszName 指向一个由Shell拥有的缓冲区,我们将把帮助字符串拷贝进该缓冲区.
cchMax 是该缓冲区的大小.
返回值是
S_OKE_FAIL.

GetCommandString() 也可以被调用以获取菜单项的动作("verb") .
verb 是个语言无关性字符串,它标识一个可以加于文件对象的操作。
ShellExecute()的文档中有详细的解释, 而有关verb的内容足以再写一篇文章, 简单的解释是:verb 可以直接列在注册表中(如"open" 和 "print"等字符串), 也可以由上下文菜单扩展创建. 这样就可以通过调用ShellExecute()执行实现在Shell扩展中的代码.

不管怎样, 我说了这多只是为了解释清楚
GetCommandString() 的作用.
如果 Explorer 要求一个帮助字符串,我们就提供给它. 如果 Explorer 要求一个verb, 我们就忽略它. 这就是
uFlags 参数的作用.
如果
uFlags 设置了GCS_HELPTEXT 位,则 Explorer 是在要求帮助字符串. 而且如果 GCS_UNICODE 被设置,我们就必须返回一个Unicode字符串.

我们的
GetCommandString() 如下:

这里没有什么特别的代码; 我用了硬编码的字符串并把它转换为相应的字符集.
如果你从未使用过ATL字符串转化宏,你一定要学一下,因为当你传递Unicode字符串到COM和OLE函数时,使用转化宏会很有帮助的.
我在上面的代码中使用了T2CW 和 T2CA 将TCHAR 字符串分别转化为Unicode 和 ANSI字符串.
函数开头处的
USES_CONVERSION 宏其实声明了一个将被转化宏使用的局部变量.

要注意的一个问题是:
lstrcpyn() 保证了目标字符串将以null为结束符.
这与C运行时(CRT)函 数
strncpy()不同.当要拷贝的源字符串的长度大于或等于cchMax 时 strncpy()不会添加一个 null 结束符.
我建议总使用
lstrcpyn(), 这样你就不必在每一个strncpy()后加入检查保证字符 串以null为结束符的代码.

IContextMenu 接口的最后一个方法是InvokeCommand(). 当用户点击我们添加的菜单项时该方法将被调用. 其函数原型是:


CMINVOKECOMMANDINFO 结构带有大量的信息, 但我们只关心 lpVerbhwnd 这两个成员.
lpVerb参数有两个作用 – 它或是可被激发的verb(动作)名, 或是被点击的菜单项的索引值.
hwnd 是用户激活我们的菜单扩展时所在的浏览器窗口的句柄.

因为我们只有一个扩展的菜单项, 我们只要检查
lpVerb 参数,如果其值为0, 我们可以认定我们的菜单项被点击了。我能想到的最简单的代码就是弹出一个信息框, 这里的代码也就做了这么多. 信息框显示所选的文件夹的名字。具体代码如下:


这时可能你会问:操作系统是如何知道我们要插入这个菜单的?这里涉及到一个COM组件的注册问题。所谓COM组件的注册,简单来说是将COM组件的相关信息写进注册表,然后操作系统通过读取注册表的相关信息来加载COM组件。Shell程序的注册分为两步:

第一步在Win NT/Win 2000上确保你的Shell扩展能被没有管理员权限的用户调用,需要在注册表HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\ShellExtensions\Approved添加我们的程序信息。这个需要在工程中的DllRegisterServer函数(注册函数)和DllUnregisterServer函数(反注册函数)。代码如下:


这里的一个问题是reg.SetValue ( _T("ImportShell extension"),

_T("{06001B8E-8858-4CEE-8E91-60E12A6C81A7}") );

中的键名和键值是如何来的。实际上当你新建COM简单对象后,就会自动生成一个ImportShellExt.rgs文件,打开这个ImportShellExt.rgs文件,就会有如下的文件:

ImportShell.ImportShellExt.1 = s 'ImportShellExt Class'

{

CLSID = s '{06001B8E-8858-4CEE-8E91-60E12A6C81A7}'

}

ImportShell.ImportShellExt = s 'ImportShellExt Class'

{

CLSID = s '{06001B8E-8858-4CEE-8E91-60E12A6C81A7}'

CurVer = s 'ImportShell.ImportShellExt.1'

}

这个键名一般取自程序名+ extension,如ImportShell extension,键值则来自它的guid的字符串形式: {06001B8E-8858-4CEE-8E91-60E12A6C81A7}。

第二步则涉及到该Shell程序所操作的文件类型。比如我们要求它在选中文件夹才弹出我们这个右键菜单。这时就需要在ImportShellExt.rgs文件添加一些信息:

NoRemove Folder

{

NoRemove ShellEx

{

NoRemove ContextMenuHandlers

{

ForceRemove ImportShellExt = s'{06001B8E-8858-4CEE-8E91-60E12A6C81A7}'

}

}

}

上面这个其实很好理解的:每一行代表一个注册表键, "HKCR"是HKEY_CLASSES_ROOT 的缩写.
NoRemove 关键字表示当该COM服务器注销时该键 不用被删除.
最后一行有些复杂. ForceRemove 关键字表示如果该键已存在, 那么在新键添加之前该键先应被删除. 这行脚本的余下部分指定一个字符串,它将被存为ImportShell键的默认值。

如果你要操作txt文件,可以添加这样的信息:

NoRemove .txt

{

NoRemove ShellEx

{

NoRemove ContextMenuHandlers

{

ForceRemove ImportShellExt = s'{06001B8E-8858-4CEE-8E91-60E12A6C81A7}'

}

}

}

如果要操作任意类型的文件,则是:

NoRemove *

{

NoRemove ShellEx

{

NoRemove ContextMenuHandlers

{

ForceRemove ImportShellExt = s'{06001B8E-8858-4CEE-8E91-60E12A6C81A7}'

}

}

}

二.Shell程序调试

在Win NT/2000上, 你可以找到如下键:

HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer

并创建一个名为DesktopProcess的DWORD值 1. 这会使桌面和任务栏运行在同一个进程中, 而其他每一个Explorer 窗口都运行在它自己的每一个进程内. 也就是说,你可以在单个的Explorer窗口内进行调试, 而后只要你关闭该窗口,你的DLL就会被马上卸载, 这就避免了因为DLL正被Windows使用而无法替换更新. 而如果不幸出现这种情况,你就不得不注销登录后再重新登录进Windows从而强制卸载使用中的Shell扩展DLL。

按F5开始,这时会弹出一个对话框,这时请输入exploer.exe的路径,如下图:

这时一般会出现一个警告框,按是不予理会,如下图:


接着是打开一个我的文档的窗口,如下图:


这时就可以在代码中设置断点调试了。

三.Shell程序的部署

Shell程序的部署很简单,就是在生成的dll的目录下新建两个批处理文件:

install.bat——shell程序的安装脚本,内容为:

regsvr32.exe ImportShell.dll

uninstall.bat ——shell程序的卸载脚本,内容为:

regsvr32.exe /u ImportShell.dll

运行这两个批处理文件就能安装或卸载shell程序。

四.遇到问题及解决办法

链接器工具错误 LINK : fatal error LNK1168: cannot open..\outdir\Debug\ImportShell.dll for writing。

在改变注册com对象的guid会出现该问题。解决办法是打开任务管理器,杀死所有explorer.exe,然后新建一个explorer进程。

参考文献:

  1. 《Windows Shell扩展编程完全指南》,作者:Michael Dunn。

文中参考工程源码下载地址:CSDN下载








分享到:
评论

相关推荐

    Windows shell扩展程序

    关于什么叫Windows shell扩展程序,这里不作介绍,不懂的同学请google之。这里采用的开发环境为Windows XP+sp3, VS 2005 + sp1 (应该支持VS 2005以上的VS版本,VC 6.0估计不支持)。

    Windows Shell扩展编程完全指南(配套代码).zip

    第二节 - 如何编写一次操作多个文件对象的Shell扩展 第三节-如何编写为文件对象弹出提示信息框的Shell扩展 第四节 - 如何编写提供定制拖放功能的Shell扩展 第五节-如何编写添加属性页到文件属性对话框中的Shell...

    《Windows Shell扩展编程完全指南》.rar

    《Windows Shell扩展编程完全指南》.rar 《Windows Shell扩展编程完全指南》.rar

    Windows Shell扩展编程完全指南

    另一个扩展添加菜单项到文件夹窗口背景上下文菜单. -------------------------------------------------------------------------------- 第八节-如何使用信息栏扩展添加定制的信息栏到资源浏览器详细资料列表...

    Windows Shell 扩展编程完全指南

     第二节 - 如何编写一次操作多个文件对象的Shell扩展  第三节-如何编写为文件对象弹出信息框的Shell扩展  第四节 - 如何编写提供定制拖放功能的Shell扩展  第五节-如何编写添加属性页到文件属性对话框中的...

    Windows Shell扩展编程完全指南(配套代码)

    这是戴维整理的关于 Windows Shell 扩展方面的技术文章,希望方便那些希望学习 Shell 扩展知识而又没有学习资料的人。国内关于 Shell 扩展方面的资料奇缺,希望这个资料能有点用。资料中一部分文章为戴维自己翻译,...

    Windows Shell扩展编程完全指南 chm

    Windows Shell扩展编程完全指南 chm Windows Shell扩展编程完全指南 chm

    Windows+Shell扩展编程完全指南

    第二节 - 如何编写一次操作多个文件对象的Shell扩展 第三节-如何编写为文件对象弹出提示信息框的Shell扩展 第四节 - 如何编写提供定制拖放功能的Shell扩展 第五节-如何编写添加属性页到文件属性对话框中的Shell...

    Windows Shell扩展编程完全指南(含源码)

    Windows Shell扩展编程完全指南(含源码).chm

    Windows shell扩展技术探讨C++源代码程序小实例

    Windows shell扩展技术探讨C++源代码程序小实例

    Windows Shell扩展及实例代码

    Shell扩展及其原理的概要说明,常见shell handler及其接口的说明,shell handler的开发过程,附件是一个实例代码。 适合于不怎么了解Shell扩展的人,其实之前我也没弄过Shell扩展,这是搞了 几天Shell扩展的一点心得...

    windows shell扩展编程完全指南

    从网上找到的一些windows shell扩展方面的技术文章及代码,希望方便那些希望学习Shell扩展知识而又没有学习资料的人。本资料由同济大学戴维制作 资料从网上各处收集,版权归原作者所有,由戴维进行了整理与加工,...

    Windows Shell扩展编程完全指南.vc源代码

    Windows Shell扩展编程完全指南.vc源代码

    Windows Shell扩展编程完全指南(含代码)

    Windows Shell扩展编程完全指南 第一节 - 一步步教你如何编写Shell扩展 第二节 - 如何编写一次操作多个文件对象的Shell扩展 第三节-如何编写为文件对象弹出提示信息框的Shell扩展 第四节 - 如何编写提供定制...

    Windows Shell 编程.pdf

    看过一些对windows 外壳的扩展程序,在使用上一般都是直接利用windows的外壳API做一些工作,因为外壳操作需要一些比较专业的知识,因此,大部分编程人员特别是使用集成编程环境的程序人员对windows shell的扩展编程...

    Win Shell扩展编程

    所谓的Shell扩展就是能够添加某种功能到Windows ...但对于那些没有这本书的,或只对Shell扩展本身感兴趣的朋友,我写了这个编程指南希望能够帮助你理解怎样编写Shell扩展。 该指南假设你理解COM和ATL的基本原理及应用。

    Windows shell扩展编程

    Windows Shell扩展编程完全指南

Global site tag (gtag.js) - Google Analytics