扩展 Internet Explorer 脚本引擎






4.88/5 (27投票s)
2002年12月8日
3分钟阅读

143285

3057
本文展示了如何通过添加自定义事件接收器和对象来扩展 IE 的脚本引擎
引言
托管 Web 浏览器控件很方便。然而,浏览器托管的一个限制是其对容器对象的访问相对有限。与容器的 IDispatch
接口进行通信的标准方法是通过 document.external
。这对于提供基本扩展来说很好,但如何接收事件?使用自定义枚举类型?添加自己的对象?
本文将介绍如何通过 IDispatchEx
接口将您自己的自动化对象添加到任何脚本引擎。它还将展示如何轻松地接收 HTML 页面中提供的 COM 事件。
我将使用 Internet Explorer 的脚本引擎来演示源代码的功能,但它在任何使用脚本的应用程序中都很有用。
工作原理
CDispExSinkConnector
类是实现这一切的关键。它通过挂钩脚本的 IDispatchEx
接口来工作。新项通过使用 InvokeEx
方法插入到脚本命名空间中。这个功能强大的方法允许您在运行时向脚本引擎添加几乎任何类型的数据。
第一步是向 CDispExSinkConnector
对象提供 IDispatchEx
接口,以用于访问脚本引擎。此接口可以从 Internet Explorer 使用 IHTMLDocument::get_Script()
方法检索,该方法返回一个 IDispatch
接口,然后可以对其进行 QueryInterface()
以获取 IDispatchEx
。
IDispatchEx::GetDispID()
用于在脚本引擎中创建新成员。一旦创建了成员 DISPID
,就可以使用 InvokeEx()
来设置属性值。
为了接收事件,CDispExSinkConnector
会创建一个自定义的 IDispatch
实现,该实现加载包含事件 DispID 的类型库。当事件方法被触发时,对象将在脚本命名空间中搜索与事件名称匹配且前面带有传递给 ConnectObject()
的前缀的函数。如果找到,则调用该函数。
您可能会对内存和接口泄漏感到好奇。CDispExSinkConnector
对象本身实现了一个骨架 IDispatch
接口,并在调用 SetDispEx()
时插入到脚本引擎中,从而保持对象的持久性。当 IE 脚本引擎在后续页面加载时重置时,它会自动删除。
类布局
CDispExSinkConnector
的重要公共方法是:
CDispExSinkConnector : public IDispatch { ... HRESULT ConnectObject(IUnknown *pUnk, BSTR bstrPrefix, const GUID *piid, const GUID *plibid, WORD wMajorVer = 1, WORD wMinorVer = 0, LCID lcid = 1033); HRESULT DisconnectObject(IUnknown *pUnk, const GUID *piid); HRESULT AddNamedObject(BSTR bsName, IDispatch *pDisp); HRESULT RemoveNamedObject(BSTR bsName); HRESULT AddTypeLib(REFGUID guidTypeLib, WORD wMaj, WORD wMin); void SetEnabled(BOOL bEnabled); BOOL GetEnabled(); void SetDispEx(IDispatchEx *pDispEx); BOOL GetDispEx(IDispatchEx **ppDispEx); ... };
方法摘要
ConnectObject()
将由 pUnk
参数指定的事件源对象连接到脚本引擎。当源触发事件时,CDispExSinkConnector
将在脚本命名空间中搜索一个名为 bstrPrefix
+ eventFunction
的函数。因此,例如,如果我想连接一个只有一个名为 OnNumberChanged 的事件方法的连接对象,并指定 "ExtObject_" 作为前缀,那么在脚本中将调用以下方法:
ExtObject_OnNumberChanged()在 JScript 中,事件函数定义如下:
function ExtObject_OnNumberChanged(newNumber) { ... }
事件传递的任何参数也将传递给脚本函数。DisconnectObject()
将断开由 pUnk
参数指定的对象与脚本引擎的连接。
AddNamedObject()
将由 pDisp
参数指定的 IDispatch
接口添加到脚本引擎,允许通过 bsName
参数提供的名称访问它。同样,例如,如果我想让 ExtObject 的 DispID 接口可以从我的脚本访问,我只需使用以下代码:
pDispExSinkConnector->AddNamedObject(CComBSTR("ExtObject"), pDispExtObj);
该对象现在可以从脚本中作为 ExtObject 对象访问。RemoveNamedObject()
将从脚本引擎中删除之前通过 AddNamedObject
插入的任何对象。
AddTypeLib()
将从提供的类型库中将枚举类型添加到脚本引擎。如果您需要调用需要枚举类型作为参数的方法,这很有用,因为您可以使用枚举类型,就像在低级语言中使用它一样。
Using the Code
与 IE 一起使用该代码非常简单。最好挂钩 Web 浏览器的 DWebBrowserEvents2
接口,并使用 DocumentComplete 通知来设置 CDispExSinkConnector
对象。
STDMETHOD(OnDocumentComplete)(IDispatch* pDisp, VARIANT* URL) { CComPtr<IDispatch> pDispDoc; CComQIPtr<IDispatch> pScriptDisp; CComQIPtr<IDispatchEx> pDispEx; CComQIPtr<IHTMLDocument> pHTMLDoc; CComQIPtr<IWebBrowser2> pWebBrowser; CDispExSinkConnector *pDispExSinkConnector = NULL; pWebBrowser = pDisp; pWebBrowser->get_Document(&pDispDoc); // Get the IDispatchEx interface pHTMLDoc = pDispDoc; pHTMLDoc->get_Script(&pScriptDisp); pDispEx = pScriptDisp; // Create a new sink connector that will attach to this document // object. This object is reference counted, and IE will properly // free it when the document is unloaded pDispExSinkConnector = new CDispExSinkConnector(); pDispExSinkConnector->SetDispEx(pDispEx); if(m_pExtObject) { CComPtrpUnk; CComQIPtr pDisp = m_pExtObject; pDisp->QueryInterface(IID_IUnknown, (void **) &pUnk); // Add the named object pDispExSinkConnector->AddNamedObject(CComBSTR("ExtObject"), pDisp); pDispExSinkConnector->ConnectObject(pUnk, CComBSTR("ExtObject_"), &DIID__IExtObjectEvents, &LIBID_DISPEXOBJECTLib); } return S_OK; }
从现在开始,您将接收事件,并且能够从 HTML 页面中提供的脚本访问该对象。
历史
- 2003/02/28 - 修复了 DispExSinkConnector.h 中的接口泄漏
- 2002/12/08 - 初始版本