| HRESULT IContextMenu::InvokeCommand ( LPCMINVOKECOMMANDINFO pCmdInfo ); |
CMINVOKECOMMANDINFO 结构带有大量的信息, 但我们只关心 lpVerb 和 hwnd 这两个成员.
lpVerb参数有两个作用 – 它或是可被激发的verb(动作)名, 或是被点击的菜单项的索引值.
hwnd 是用户激活我们的菜单扩展时所在的浏览器窗口的句柄.
因为我们只有一个扩展的菜单项, 我们只要检查lpVerb 参数, 如果其值为0, 我们可以认定我们的菜单项被点击了.
我能想到的最简单的代码就是弹出一个信息框, 这里的代码也就做了这么多. 信息框显示所选的文件的文件名以证实代码正确地工作.
HRESULT CSimpleShlExt::InvokeCommand ( LPCMINVOKECOMMANDINFO pCmdInfo )
{
// 如果lpVerb 实际指向一个字符串, 忽略此次调用并退出.
if ( 0 != HIWORD( pCmdInfo->lpVerb ))
return E_INVALIDARG;
// 点击的命令索引 – 在这里,唯一合法的索引为0.
switch ( LOWORD( pCmdInfo->lpVerb ))
{
case 0:
{
TCHAR szMsg [MAX_PATH + 32];
wsprintf ( szMsg, _T("The selected file was:\n\n%s"), m_szFile );
MessageBox ( pCmdInfo->hwnd, szMsg, _T("SimpleShlExt"),
MB_ICONINFORMATION );
return S_OK;
}
break;
default:
return E_INVALIDARG;
break;
}
}
|
注册Shell扩展
现在我们已经实现了所有需要的COM接口. 可是我们怎样才能让浏览器使用我们的扩展呢?
ATL 自动生成注册COM DLL服务器的代码, 但这只是让其它程序可以使用我们的DLL.
为了告诉浏览器使用我们的扩展, 我们需要在文本文件类型的注册表键下注册扩展:
HKEY_CLASSES_ROOT\txtfile
在这个键下, 有个名为 ShellEx 的键保存了一个与文本文件关联的Shell扩展的列表. 在 ShellEx 键下, ContextMenuHandlers 键保存了上下文菜单扩展的列表.
每个扩展都在ContextMenuHandlers下创建了一个子键并将其默认值设为扩展COM的GUID.
所以, 对于我们这个简单的扩展, 我们将创建下键:
HKEY_CLASSES_ROOT\txtfile\ShellEx\ContextMenuHandlers\SimpleShlExt
并将其默认值设为我们的 GUID: "{5E2121EE-0300-11D4-8D3B-444553540000}".
你不必写任何代码就可以完成注册操作. 如果你看一下Fileview页的文件列表, 你会看到SimpleShlExt.rgs.
该文本文件将被ATL分析, 并指导ATL在该COM服务器注册时添加附加的注册键, 而注销时又该删除哪些键. 以下是所指定添加的注册表项:
HKCR
{
NoRemove txtfile
{
NoRemove ShellEx
{
NoRemove ContextMenuHandlers
{
ForceRemove SimpleShlExt = s '{5E2121EE-0300-11D4-8D3B-444553540000}'
}
}
}
}
|
每一行代表一个注册表键, "HKCR"是 HKEY_CLASSES_ROOT 的缩写.
NoRemove 关键字表示当该COM服务器注销时该键 不用被删除.
最后一行有些复杂. ForceRemove 关键字表示如果该键已存在, 那么在新键添加之前该键先应被删除.
这行脚本的余下部分指定一个字符串,它将被存为 SimpleShlExt 键的默认值.
在这我插几句话. 我们是在 HKCR\txtfile下注册的. 但是 "txtfile" 名并不是一个永久的或预定好的名称.
如果你看一下 HKCR\.txt, 该键的默认值正是txtfile. 这样就会有一些副作用:
1.我们不能可靠地使用 RGS 教本,因为 "txtfile" 可能不是正确的键名.
2. 一些文本编辑软件可能安装到系统并直接关联到 .TXT 文件. 如果它改变了HKCR\.txt 键的默认值, 所有现存的Shell扩展都将停止工作.
在我看来,这确是个设计上的错误. 我认为微软也是这么想的, 因为最新的扩展类型, 如QueryInfo扩展注册在 .txt 键下.
好了,到此为止. 最后还有一个注册细节. 在NT/2000上, 我们还得将我们的扩展放到 "approved" 扩展列表中.
如果我们不这样做, 我们的扩展不会被没有管理员权限的用户调用. 该列表保存在:
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved
在该键下, 我们要创建一个以我们的GUID 为名的字符串键,键的内容任意. 与之有关的代码在DllRegisterServer() 和 DllUnregisterServer() 函数中.
只是些简单的注册表获取, 我也就不在这写出了. 你可以在例子工程代码中找到它.
调试Shell 扩展
最终你会写一个不会这么简单的扩展, 那时你就不得不进行调试.
打开你的工程设置, 在 Debug 页” Executable for debug session”编辑框中输入浏览器程序的全路径, 如:"C:\windows\explorer.exe".
如果你使用的是 NT 或 2000, 并且你已经设置了上述的 DesktopProcess 注册键, 那么当你按F5进行调试时就会打开一个新的浏览器窗口.
只要你在这个窗口内完成你所有的工作,当你关闭该窗口时扩展同时会被卸出内存,这样就不会防碍我们重建 DLL了.
在Windows 9x上, 在重新调试之前你不得不关闭Shell. 你可以: 点击 “开始”, 再点击”关闭”. 按住 Ctrl+Alt+Shift 并点击”取消”.
这样就会关闭Shell, 你会看到桌面消失了.
接着,你可以切换到 MSVC 再按 F5进行调试. 要中止调试, 按 Shift+F5 关闭. 完成调试后, 你可以从”开始 运行” Explorer.exe以正常重起.
扩展的样子
下面就是我们添加自定义菜单项后的样子:

看,我们的菜单项在那!
下图是浏览器状态栏的帮助提示字符串的显示:

弹出信息框如下图, 显示了所选的文件名:
