介绍
向IE中添加的自定义菜单,可以被用来执行可执行文件,脚本语言和COM对象的方法,对于前两种执行方式,相对比较简单,只需要对注册表进行编程就可以了,但是能够执行的功能非常有限,而使用COM组件扩展IE菜单则相对复杂,但是能够执行的功能也相对是最强大的,所以下面我就主要介绍基于COM的菜单扩展方式,还要注意的一点是只有IE5及以后的版本才支持菜单的扩展。
创建COM组件
对于一个IE菜单项COM组件来说,它最少需要实现IOleCommandTargetCOM接口,如果COM组件还想要对当前IE中显示的页面进行操作的话,就还需要实现IObjectWithSite接口,对于IObjectWithSite接口,我将在后面的工具条按钮扩展中讨论它的实现。
首先在Delphi中用New | Other命令调出New Items对话框,然后在ActiveX页面中选择创建一个新的ActiveX Library,将项目保存为IEMenu.dpr,然后再次调用New | Other | ActiveX命令,新建一个名为TIEHelloWorld的Com Object对象,然后添加IOleCommandTarget接口的方法实现,完成的类的定义如下:
type
TIEHelloWorld = class(TComObject, IOleCommandTarget)
protected
//IOleCommandTarget方法接口
function QueryStatus(CmdGroup: PGUID; cCmds: Cardinal;
prgCmds: POleCmd; CmdText: POleCmdText): HResult; stdcall;
function Exec(CmdGroup: PGUID; nCmdID, nCmdexecopt: DWORD;
const vaIn: OleVariant; var vaOut: OleVariant): HResult; stdcall;
end;
IOleCommandTarget包含QueryStatus
和
Exec
两个方法,其中文档上说QueryStatus方法会被IE调用来获得当前菜单的状态,我们需要返回OLECMDF_ENABLED或者其它值来表示是否允许点击,但是在实际当中,我发现IE从来没有调用过我的菜单扩展的QueryStatus方法,不过不管怎么说,还是按正常的来实现。
function TIEHelloWorld.QueryStatus(CmdGroup: PGUID; cCmds: Cardinal;
prgCmds: POleCmd; CmdText: POleCmdText): HResult;
begin
//允许点击菜单
prgCmds^.cmdf:=OLECMDF_ENABLED;
Result := S_OK;
end;
当用户点击菜单后,IE会调用IOleCommandTarget接口的Exec方法来执行用户自定义的操作。
function TIEHelloWorld.Exec(CmdGroup: PGUID; nCmdID, nCmdexecopt: DWORD;
const vaIn: OleVariant; var vaOut: OleVariant): HResult;
begin
Result := S_OK;
//nCmdI为0时,表示菜单和工具条按钮被点击了
try
ShowMessage('Hello IE');
except
Result:=E_FAIL;
end;
//注意下面的话语句会导致AV,不知道为什么?
//vaOut := Null;
end;
Exec方法带有很多参数,其中IE在调用菜单扩展和工具条按钮扩展组件的的Exec方法时,会设定nCmdI为0,其它参数对于我们来说,都不需要关心,只要在成功执行代码后,返回S_OK,在失败时返回E_Fail就可以了。实现了IOleCommandTarget接口之后,一个最简单的IE菜单扩展就完成了。
注册组件
要想让IE在启动后能够发现菜单扩展COM组件,并加载菜单进行显示,需要在注册表中填写一些配置信息。
1、 要在HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer\Extensions\项目下新建一个关键字,名为菜单扩展的Guid的字符串形式。
2、 然后在新建的HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer\Extensions\<菜单扩展Guid>关键字下再创建一个名为ClsidExtension的项目,值也为扩展的Guid。
3、 然后在HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer\Extensions\<菜单扩展Guid>关键字下添加名为MenuText的字符串字段,写入IE用来显示菜单扩展的菜单项标题文本。
4、 我们还可以创建一个MenuStatusBar,IE会在鼠标停留在我们的扩展对应的菜单项时,在IE的状态条上显示MenuStatusBar的文本。这个注册表项不是必须项目,可以省略。
5、 通常情况下,IE会在工具菜单下显示我们的扩展菜单,但是我们也可以创建一个MenuCustomize字段,设定其值为help。这时IE会将我们的菜单放到帮助主菜单下面。如果我们的菜单扩展是用来向IE中添加上下文帮助的,可以设定这个选项。这个注册表项同样也是可以忽略的。
注意:如果将注册表位置由HKEY_LOCAL_MACHINE改成HKEY_CURRENT_USER,则菜单扩展会对所有该机器上用户生效。
为了实现添加必要的注册表信息,编写AddMenuItem方法来实现注册功能:
//添加IE菜单
procedure AddMenuItem(MenuText, StatusBarText, Guid:
string; HelpMenu: Boolean);
var
Reg: TRegistry;
begin
Reg := TRegistry.Create;
with Reg do
begin
RootKey := HKEY_LOCAL_MACHINE;
OpenKey('\Software\Microsoft\Internet Explorer\Extensions\' + Guid, True);
if HelpMenu then
WriteString('MenuCostumize', 'help');
WriteString('CLSID', '{1FBA04EE-3024-11d2-8F1F-0000F87ABD16}');
WriteString('MenuText', MenuText);
WriteString('MenuStatusBar', StatusBarText);
WriteString('ClsidExtension', Guid);
CloseKey;
CloseKey;
Free;
end;
end;
同时我们还要注销菜单扩展的功能,方法RemoveMenuItem
被用来删除注册表项,删除注册表项是
Guid
来进行的,因为
Guid
是可以保证唯一性。
//删除IE菜单
procedure RemoveMenuItem(Guid: string);
var
Reg: TRegistry;
begin
Reg := TRegistry.Create;
with Reg do
begin
RootKey := HKEY_LOCAL_MACHINE;
DeleteKey('\Software\Microsoft\Internet Explorer\Extensions\' + Guid);
free;
end;
end
;
最后,我们定义TIEHelloMenuFactory的TComObjectFactory派生类来调用上面的方法实现COM的组件的注册和注销:
type
TIEHelloMenuFactory = class(TComObjectFactory)
public
procedure UpdateRegistry(Register: Boolean); override;
end;
…
{ TIEHelloMenuFactory }
procedure TIEHelloMenuFactory.UpdateRegistry(Register: Boolean);
begin
inherited;
if Register then
//添加到Tools菜单下
AddMenuItem('HelloIE', 'Hello IE MenuItem', GuidToString(ClassID), False)
else
//Todo: 通过Guid进行删除
RemoveMenuItem(GuidToString(ClassID));
end;
initialization
TIEHelloMenuFactory.Create(ComServer, TIEHelloWorld, Class_IEHelloWorld,
'IEHelloWorld', '', ciMultiInstance, tmApartment);
end.
完成程序后,我们可以在Delphi IDE中使用菜单 Run | Register ActiveX Server和UnRegister ActiveX Server来注册创建好的菜单扩展,然后启动IE,运行后的效果示意图:
总结
上面我们介绍了如何创建一个最简单的菜单扩展,接下来我将探讨IE扩展的一些更为高级的技术。