65.9K
CodeProject 正在变化。 阅读更多。
Home

扩展 Internet Explorer 脚本引擎

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.88/5 (27投票s)

2002年12月8日

3分钟阅读

viewsIcon

143285

downloadIcon

3057

本文展示了如何通过添加自定义事件接收器和对象来扩展 IE 的脚本引擎

Sample Image - dispexsinkconnector.gif

引言

托管 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)
    {
        CComPtr pUnk;
        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 - 初始版本
© . All rights reserved.