Windows外壳支持几种搜索功能,允许用户定位命名空间对象(如文件、局域网上的电脑、打印机、回收站等),那么我们可不可以自己创建一个搜索引擎并注册到系统来搜索特定的对象呢?答案是可以的,但这需要实现搜索扩展。
搜索扩展的工作方式
用户可以有两种方式来实现搜索引擎,第一种是从开始菜单调用查找,可以显示一个当前可用的搜索引擎列表,如图2.1所示。第二种是从资源管理器中调用查找 功能,在文件夹上点右键选查找菜单可以调出查找功能。但它是在Windows 2000资源管理器搜索引擎上使用一个工具条上的搜索按钮来进行查找的。点击后显示如图2.2所示的搜索界面。
图2.1 图2.2
Windows 2000和早期的Win 9X版本在实现搜索扩展和注册扩展方面有很多不同。表2.1列举了它们的主要区别。
表2.1
Windows 2000之前的版本 | Windows 2000及之后的版本 |
通过上下文相关的菜单扩展来实现的(对于上下文相关的扩展已经有无数书籍介绍过了) | 通过上下文相关的菜单扩展来实现或通过DHTML(动态HTML)文档来实现 |
搜索扩展可以是静态的或是动态,区别在于静态的扩展调用完后会从内存卸载。动态扩展一旦被调用,就驻留内存,直到外壳终止 | 通过上下文菜单形式实现的扩展既可以是动态的也可以是静态的。以DHTML文档形式实现的只能是静态的 |
搜索扩展的菜单出现在开始菜单的查找子菜单中以及资源管理器的工具菜单的查找子菜单 | 扩展只出现在开始菜单的查找子菜单中。要想生成类似搜索面板的界面,必须实现一个Band对象(面板对象) |
注册扩展
扩展需要在注册表的子键HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\ CurrentVersion\ Explorer\FindExtensions下注册。同时注册过程依赖于扩展是静态还是动态的。
注册一个静态扩展
这种扩展通常对于比较小的DLL(动态连接库)比较有效,因为它可以加载得很快,并且不占用内存。另外如果你用DHTML实现扩展也必须是静态的,注册方 式:创建一个子键FindExtensions subkey,名字不能被别的扩展使用,也不能同其他搜索扩展同名。
上下文相关的扩展的注册需要设定Name子键,缺省值为自己的类标示符(CLSID GUID),在Name子键下创建一个子键0, 然后设定它的缺省值为想要显示在查找菜单中的名字。你还可以通过在字符串中加入&符号实现快捷方式。另外还可以在0键下建立一个 DefaultIcon子键来指定要显示在菜单右边的图标。设定值为包含图标的文件名全称,并用逗号隔开,跟着一个以0为底的图标索引值。
下面的例子注册了一个MySearchEngine搜索扩展,菜单项文本为"My Search Engine",图标在C:\MyDir\MySearch.dll中,其索引值为2。
HKEY_LOCAL_MACHINE
Software
Microsoft
Windows
CurrentVersion
Explorer
FindExtensions
Static
MySearchEngine={MySearchEngine CLSID GUID}
0=&My Search Engine
DefaultIcon=c:\MyDir\MySearch.dll,2
基于DHTML文档的扩展
在Windows 2000你可以实现一个DHTML形式的扩展,它的名字列在搜索子菜单下,当用户选择后,会调用浏览器来打开对应的查找文档。你也可以指定一个显示在浏览 器右侧的DHTML文档。注意:没有办法在缺省的查找面板中调用第三方的扩展。查找引擎可以直接从浏览器调用,但必须实现为band 对象。
要注册基于DHTML的扩展,设定扩展的Name子键为CLSID_ShellSearchExt的字符串形式 (当前是 {169A0691-8DF9-11d1-A1C4-00C04FD75D13}) 然后创建下列子键:
在Name子键下创建0 子键,设定缺省值为要显示的菜单标题,同样也可以如上添加缺省图标。然后在 0键下建立SearchGUID子键,赋GUID的值给 DHTML文档,并设定SearchGUID为扩展本身类标示符(GUID)的字符串形式。要说明的是这里的GUID不需要在 HKEY_CLASSES_ROOT\CLSID下注册。在SearchGUID下创建Url子键,设定值为将要调用的HTML文档的路径。在 SearchGUID下创建UrlNavNew 子键,设定值为将要显示在浏览器右侧的HTML文档的路径。
下面是一个DHTML文档搜索扩展的例子:菜单文本仍然为"My Search Engine",图标也没有变化,文档为C:\MyDir\MySearch.htm,同时将被显示在资源管理器工具条右侧的文档为C:\MyDir \MySearchPage.htm。
HKEY_LOCAL_MACHINE
Software
Microsoft
Windows
CurrentVersion
Explorer
FindExtensions
Static
MySearchEngine={169A0691-8DF9-11d1-A1C4-00C04FD75D13}
0=&My Search Engine
DefaultIcon=c:\MyDir\MySearch.dll,2
SearchGUID={My Search GUID}
Url=C:\MyDir\MySearch.htm
UrlNavNew=\MyDir\MySearchPage.htm
注册动态搜索扩展
如果扩展是以上下文菜单形式实现的,它也可以是动态的。动态扩展驻留内存因而比静态的响应得更快。如果你的扩展加载需要很长时间,或者经常被调用,那么应该实现为动态扩展。
动态扩展也是注册在KEY_LOCAL_MACHINE\Software\Microsoft\Windows\ CurrentVersion\ Explorer\FindExtensions子键下的。创建FindExtensions子键设定值为扩展的类标示符(CLSID GUID),动态扩展不支持菜单图标,下面是一个例子:
HKEY_LOCAL_MACHINE
Software
Microsoft
Windows
CurrentVersion
Explorer
FindExtensions
MySearchEngine={MySearchEngine CLSID GUID}
0=&My Search Engine
不像静态扩展, 我们不需要在注册表中指定菜单标题。当扩展被加载后,外壳会调用扩展的 IContextMenu.QueryContextMenu 方法来向搜索菜单加入子菜单实现搜索扩展。
实现静态扩展的例子
搜索扩展即可以作为上下文菜单扩展来实现(对于上下文菜单扩展的实现,由于许多书讲到了这个问题,这里就不进行解释了,不了解的朋友请自行查阅相关书 籍),也可以使用DHTML文档来实现,因为以上下文菜单扩展方式实现的搜索扩展可以适用于全部Windows版本,因此这里只介绍这类扩展的实现,至于 DHTML扩展的实现,请参看MSDN。
静态扩展的流程如下:
当用户激活扩展菜单命令后,外壳加载动态连接库,然后调用IContextMenu.InvokeCommand 方法通知扩展启动搜索引擎。
当IContextMenu.InvokeCommand 方法被调用后,CMINVOKECOMMANDINFO 记录中的lpVerb 成员传递了命令标识符,lpVerb 的低字部分等于命令的subkey名,通常为0,扩展应该负责启动搜索引擎。
动态扩展的实现非常类似,当IShellExtInit.Initialize 方法被调用后, pidlFolder 和 lpdobj 参数都被设定为NULL。
下面我们将实现一个最简单的静态搜索扩展的例子,当在搜索中选中这个扩展后,会调用RegEdit的查找对话框,在注册表中查找信息。
首先,选菜单项New | ActiveX | Com Object来创建一个COM对象,然后在单元中实现IContextMenu接口。代码实现如下:
unit CXFindReg;
interface
uses
Windows, ActiveX, Classes, ComObj, Shlobj, Registry, Sysutils, Dialogs;
type
TCXFindRegMenu = class(TComObject, IContextMenu)
protected
{Declare IContextMenu methods here}
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_CXFindRegMenu: TGUID = '{A57C4071-0A7E-11D4-9266-5254AB159E5E}';
implementation
uses ComServ;
{ TCXFindMenu }
function TCXFindRegMenu.GetCommandString(idCmd, uType: UINT;
pwReserved: PUINT; pszName: LPSTR; cchMax: UINT): HResult;
begin
if (idCmd = 0) then begin
if (uType = GCS_HELPTEXT) then
// return help string for menu item
StrCopy(pszName, '查找注册表');
Result := NOERROR;
end
else
Result := E_INVALIDARG;
end;
function TCXFindRegMenu.InvokeCommand(
var lpici: TCMInvokeCommandInfo): HResult;
var
H: THandle;
begin
Result := E_FAIL;
//确认不是被一个应用程序调用
if (HiWord(Integer(lpici.lpVerb)) <> 0) then
begin
Exit;
end;
// 确认传递过来的参数有效
if (LoWord(lpici.lpVerb) <> 0) then begin
Result := E_INVALIDARG;
Exit;
end;
//执行命令
H := WinExec(PChar('regedit.exe'), lpici.nShow);
if (H < 32) then
ShowMessage('无法执行Regedit.exe')
else
//发送Ctrl+F 组合按键调用查找对话框
//这里使用Sendkeys函数(实现在sndkey32.pas单元中,
//位于Delphi5光盘的Info\Extras\SendKeys目录下)
SendKeys('^F', Wait);
Result := NOERROR;
end;
function TCXFindRegMenu.QueryContextMenu(Menu: HMENU; indexMenu, idCmdFirst,
idCmdLast, uFlags: UINT): HResult;
begin
Result:=E_NOTIMPL;
end;
type
TCXFindRegMenuFactory = class(TComObjectFactory)
public
procedure UpdateRegistry(Register: Boolean); override;
end;
procedure TCXFindRegMenuFactory.UpdateRegistry(Register: Boolean);
var
ClassID: string;
begin
if Register then begin
inherited UpdateRegistry(Register);
ClassID := GUIDToString(Class_CXFindRegMenu);
with TRegistry.Create do
begin
RootKey:=HKEY_LOCAL_MACHINE;
OpenKey('\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\
FindExtensions\Static\CXFindRegMenu',True);
WriteString('',ClassID);
OpenKey('\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\
FindExtensions\Static\CXFindRegMenu\0',True);
WriteString('','עq');
OpenKey('\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\
FindExtensions\Static\CXFindRegMenu\0\DefaultIcon',True);
WriteString('',GetCurrentDir+'\RegMenu.Ico');
OpenKey('\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\
FindExtensions\Static\CXFindRegMenu\0\HelpText',True);
WriteString('','עq');
CloseKey;
Free;
end;
if (Win32Platform = VER_PLATFORM_WIN32_NT) then
with TRegistry.Create do
try
RootKey := HKEY_LOCAL_MACHINE;
OpenKey('SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions', True);
OpenKey('Approved', True);
WriteString(ClassID, 'CXExperts Find Registry Menu');
finally
Free;
end;
end
else begin
with TRegistry.Create do
begin
RootKey:=HKEY_LOCAL_MACHINE;
DeleteKey('\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\
FindExtensions\Static\CXFindRegMenu\0\DefaultIcon');
DeleteKey('\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\
FindExtensions\Static\CXFindRegMenu\0\HelpText');
DeleteKey('\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\
FindExtensions\Static\CXFindRegMenu\0');
DeleteKey('\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\
FindExtensions\Static\CXFindRegMenu');
CloseKey;
Free;
end;
inherited UpdateRegistry(Register);
end;
end;
initialization
TCXFindRegMenuFactory.Create(ComServer, TCXFindRegMenu, Class_CXFindRegMenu,
'CXFindRegMenu', '', ciMultiInstance, tmApartment);
end.
搜索扩展的实现是非常简单的,上面代码大部分都是在修改注册表。