Click here to Skip to main content
14,970,700 members
Articles / Desktop Programming / ATL
Posted 7 Dec 2002


82 bookmarked

Extending the Internet Explorer Scripting Engine

Rate me:
Please Sign up or sign in to vote.
4.88/5 (28 votes)
27 Feb 20033 min read
This article shows you how to extend IE's scripting engine by adding custom event sinks and objects

Sample Image - dispexsinkconnector.gif


Hosting the web browser control is cool. However, one of the limitations of browser hosting is the relatively limited access to container object. The standard way of talking to the container's IDispatch interface is via document.external. This is nice for providing basic extensions, but what about sinking events? Using custom enumeration types? Adding your own objects?

This article will show you how to add your own automation objects to any scripting engine through the IDispatchEx interface. It will also show you how you can easily sink COM events from script provided in your HTML page.

I will use the scripting engine from Internet Explorer to demonstrate functionality of the source code, but it is useful in any application that uses scripting.

How it Works

The CDispExSinkConnector class is what makes this all possible. It works by hooking into the script's IDispatchEx interface. New items are inserted into the scripting namespace by using the InvokeEx method. This extremely powerful method allows you add nearly any type of data to the scripting engine at runtime.

The first step is to provide the CDispExSinkConnector object with the IDispatchEx interface to use to access the scripting engine. This interface can be retrieved from Internet Explorer using the IHTMLDocument::get_Script() method, which returns an IDispatch interface which can then be QueryInterface()'d for an IDispatchEx.

IDispatchEx::GetDispID() is used to create new members inside the scripting engine. Once the member DISPID has been created, InvokeEx() is used to set the property value.

In order to sink events, CDispExSinkConnector creates a custom implementation of IDispatch which loads the type library containing the event dispinterface. When event methods are fired, the object will search the script namespace for a function matching event name preceeded by the prefix passed to ConnectObject(). If found, the function is then invoked.

You may be wondering about memory and interface leaks. The CDispExSinkConnector object itself implements a skeleton IDispatch interface and is inserted into the scripting engine when SetDispEx() is called, which maintains the object's persistence. It is automatically removed when the IE scripting engine is reset on a subsequent page load.

Class Layout

The important public methods of CDispExSinkConnector are:

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);

Method Summary

ConnectObject() will connect the event source object specified by the pUnk parameter to the scripting engine. When an event is fired by the source, CDispExSinkConnector will search for a function in the scripting namespace named bstrPrefix + eventFunction. So for example, if I wanted to hook up my connectable object which has one event method called OnNumberChanged, and I specified "ExtObject_" as the prefix, the following method would be called in script:

In JScript the event function would be defined as follows:
function ExtObject_OnNumberChanged(newNumber) { ... }
Any parameters passed by the event will also be passed to the script function.

DisconnectObject() will disconnect the object specified by the pUnk parameter from the scripting engine.

AddNamedObject() will add the IDispatch interface specified by the pDisp parameter to the script engine, allowing it to be accessed by the name provided by the bsName parameter. Again, for example, if I wanted to allow the dispatch interface of the ExtObject to be accessible from my script, I could just use the following code:

pDispExSinkConnector->AddNamedObject(CComBSTR("ExtObject"), pDispExtObj);
The object is now accessible from script as the ExtObject object.

RemoveNamedObject() removed any object previously inserted by AddNamedObject from the scripting engine.

AddTypeLib() will add enumeration types from the provided type library to the scripting engine. This is useful if you need to call methods which require enumeration types as parameters, since you can use the enumeration type, just as you would in a lower level language.

Using the Code

To use the code with IE is straightforward. It's a good idea to sink to the web browser's DWebBrowserEvents2 interface, and use the DocumentComplete notification to setup the CDispExSinkConnector object:

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;

    // Get the IDispatchEx interface
    pHTMLDoc = pDispDoc;
    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();

        CComPtr<IUNKNOWN> pUnk;
        CComQIPtr<IDISPATCH> 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; 

From this point on, you will receive events and be able to access the object from script provided in the HTML page.


  • 02/28/2003 - Fixed interface leak in DispExSinkConnector.h
  • 12/08/2002 - Initial version


This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


About the Author

Web Developer
Canada Canada
No Biography provided

Comments and Discussions

QuestionA question to the author about the intended License Pin
Member 102982071-Oct-13 18:43
MemberMember 102982071-Oct-13 18:43 
QuestionGetting RPC_E_SYS_CALL_FAILED in IDispatchEx::GetDispID() for an out-of-proc IDispatch pointer Pin
SP4208617-Oct-12 11:17
MemberSP4208617-Oct-12 11:17 
Questionhow about "new" operation? [modified] Pin
leeshipon16-Nov-09 20:02
Memberleeshipon16-Nov-09 20:02 
GeneralThis is great Pin
Anonymous27-Oct-04 15:21
MemberAnonymous27-Oct-04 15:21 
GeneralHelp, my brain is full Pin
shanbaum15-Nov-03 9:19
Membershanbaum15-Nov-03 9:19 
GeneralRe: Help, my brain is full Pin
shanbaum17-Nov-03 5:31
Membershanbaum17-Nov-03 5:31 
QuestionWithout ATL ... ? Pin
User 64185516-Oct-03 4:18
MemberUser 64185516-Oct-03 4:18 
AnswerRe: Without ATL ... ? Pin
stereo17-Oct-03 4:02
Memberstereo17-Oct-03 4:02 
GeneralRe: Without ATL ... ? Pin
User 64185517-Oct-03 4:44
MemberUser 64185517-Oct-03 4:44 
GeneralProblem with class Pin
Member 6092806-Oct-03 22:30
MemberMember 6092806-Oct-03 22:30 
GeneralThe Proper Way... Pin
Heath Stewart28-Feb-03 6:32
protectorHeath Stewart28-Feb-03 6:32 
GeneralRe: The Proper Way... Pin
stereo28-Feb-03 6:53
Memberstereo28-Feb-03 6:53 
GeneralRe: The Proper Way... Pin
igor196013-Jul-03 13:20
Memberigor196013-Jul-03 13:20 
GeneralAnother Problem Pin
Brian Shifrin27-Feb-03 22:16
MemberBrian Shifrin27-Feb-03 22:16 
GeneralRe: Another Problem Pin
stereo28-Feb-03 3:34
Memberstereo28-Feb-03 3:34 
GeneralRe: Another Problem Pin
Brian Shifrin28-Feb-03 16:00
MemberBrian Shifrin28-Feb-03 16:00 
GeneralYes, but...... Pin
Brian Shifrin17-Dec-02 8:14
MemberBrian Shifrin17-Dec-02 8:14 
GeneralRe: Yes, but...... Pin
stereo17-Dec-02 8:24
Memberstereo17-Dec-02 8:24 
GeneralProblem with demo-files Pin
Mick (CBOSS)15-Dec-02 22:44
MemberMick (CBOSS)15-Dec-02 22:44 
GeneralRe: Problem with demo-files Pin
stereo16-Dec-02 4:17
Memberstereo16-Dec-02 4:17 
GeneralAlternative solution Pin
igor19609-Dec-02 14:13
Memberigor19609-Dec-02 14:13 
GeneralRe: Alternative solution Pin
jweston1-Sep-04 14:26
Memberjweston1-Sep-04 14:26 
GeneralQuite an achivement Pin
ramky9-Dec-02 4:57
Memberramky9-Dec-02 4:57 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.