How to Create a Binary Element Behavior using ATL

This document describes how to create a binary element behavior. A binary element behavior is an element behavior that is implemented in C++ and compiled. Because it is compiled, a binary element behavior has increased performance over a script-based element behavior. If the performance of a script-based element behavior is inadequate, you can re-implement it as a binary element behavior. A binary element behavior can also be used in situations where a script-based element behavior cannot; binary element behaviors, unlike script-based element behaviors, have access to the entire operating system so they can do anything an ordinary application can—such as access the file system.

You should have knowledge of Microsoft Visual C++ and Active Template Library (ATL) to implement the behavior described in this document.

The following topics are discussed in this document.

  • Creating the Structure of a Binary Element Behavior
  • Creating the Web Page
  • Related topics

Creating the Structure of a Binary Element Behavior

The following steps show how to create the structure of a binary element behavior. The example discussed in this document merely provides a framework for creating your own binary element behavior, rather than providing a full-featured binary element behavior. The example creates one class, CBehavior, which implements the four interfaces that make up a binary element behavior. The four interfaces are:

  1. Create a new ATL COM AppWizard project.

    In Visual C++, choose New from the File menu and select ATL COM AppWizard. Name the project and choose a location to save the project files. In this article, the project is named "Example." Accept the default values in the ATL COM AppWizard.

  2. Add a new ATL object of type Internet Explorer Object.

    Choose New ATL Object from the Insert menu in Visual C++. From the ATL Object Wizard dialog box, choose Internet Explorer Object. Click Next and type a short name for the binary element behavior. In this article, the binary element behavior is named "Behavior."

    On the Attributes tab of the same dialog box, choose Single to select the single threading model. Accept the default for all other options.

  3. Make the Internet Explorer object derive from the IElementBehavior, IElementBehaviorFactory, IElementNamespaceFactory, and IElementNamespaceFactoryCallback interfaces.

    Place the following code in the Behavior.h file in the class definition of CBehavior.

    public IElementBehavior,
    public IElementBehaviorFactory,
    public IElementNamespaceFactory,
    public IElementNamespaceFactoryCallback
    

    Note  Be sure to add a comma to the end of the line before these new derivations.

     

    The declaration of the CBehavior class becomes:

    // CBehavior
    class ATL_NO_VTABLE CBehavior :
        public CComObjectRootEx<CComSingleThreadModel>,
        public CComCoClass<CBehavior, &CLSID_Behavior>,
        public IObjectWithSiteImpl<CBehavior>,
        public IDispatchImpl<IBehavior, &IID_IBehavior, &LIBID_EXAMPLELib>,
    
        // Interfaces added to implement a binary element behavior.
    
        public IElementBehavior,
        public IElementBehaviorFactory,
        public IElementNamespaceFactory,
        public IElementNamespaceFactoryCallback
    {
    
    // ...
    
    };
    
  4. Add the IElementBehavior, IElementBehaviorFactory, IElementNamespaceFactory, and IElementNamespaceFactoryCallback interfaces to the COM map in Behavior.h. The COM map becomes:

    BEGIN_COM_MAP(CBehavior)
        COM_INTERFACE_ENTRY(IBehavior)
        COM_INTERFACE_ENTRY(IDispatch)
        COM_INTERFACE_ENTRY(IObjectWithSite)
        COM_INTERFACE_ENTRY(IElementBehavior)
        COM_INTERFACE_ENTRY(IElementBehaviorFactory)
        COM_INTERFACE_ENTRY(IElementNamespaceFactory)
        COM_INTERFACE_ENTRY(IElementNamespaceFactoryCallback)
    END_COM_MAP()
    
  5. Add declarations for the binary element behavior's methods, as the following example shows.

    // IElementBehavior
    STDMETHOD(Init)(IElementBehaviorSite* pBehaviorSite);
    STDMETHOD(Notify)(LONG lEvent, VARIANT* pVar);
    STDMETHOD(Detach)();
    
    // IElementBehaviorFactory
    STDMETHOD(FindBehavior)(BSTR bstrBehavior, BSTR bstrBehaviorUrl,
    IElementBehaviorSite* pSite, IElementBehavior** ppBehavior);
    
    // IElementNamespaceFactory
    STDMETHOD(Create)(IElementNamespace * pNamespace);
    
    // IElementNamespaceFactoryCallback
    STDMETHOD(Resolve)(BSTR bstrNamespace, BSTR bstrTagName, BSTR bstrAttrs, IElementNamespace* pNamespace);
    
  6. Add public properties to cache the pointers to your IElementBehaviorSite and IElementBehaviorSiteOM2 interfaces.

    // Members
    IElementBehaviorSite        *_pSite;
    IElementBehaviorSiteOM2        *_pOMSite;
    

    The following sample shows the complete declaration of the CBehavior class.

    /////////////////////////////////////////////////////////////////////////////
    // CBehavior
    class ATL_NO_VTABLE CBehavior :
        public CComObjectRootEx<CComSingleThreadModel>,
        public CComCoClass<CBehavior, &CLSID_Behavior>,
        public IObjectWithSiteImpl<CBehavior>,
        public IDispatchImpl<IBehavior, &IID_IBehavior, &LIBID_EXAMPLELib>,
    
        //
        // Behavior-specific interfaces
        //
    
        public IElementBehavior,
        public IElementBehaviorFactory,
        public IElementNamespaceFactory,
        public IElementNamespaceFactoryCallback
    {
    public:
        CBehavior()
        {
        }
    
        // IElementBehavior
        STDMETHOD(Init)(IElementBehaviorSite* pBehaviorSite);
        STDMETHOD(Notify)(LONG lEvent, VARIANT* pVar);
        STDMETHOD(Detach)();
    
        // IElementBehaviorFactory
        STDMETHOD(FindBehavior)(BSTR bstrBehavior, BSTR bstrBehaviorUrl,
            IElementBehaviorSite* pSite, IElementBehavior** ppBehavior);
    
        // IElementNamespaceFactory
        STDMETHOD(Create)(IElementNamespace * pNamespace);
    
        // IElementNamespaceFactoryCallback
        STDMETHOD(Resolve)(BSTR bstrNamespace, BSTR bstrTagName, BSTR bstrAttrs, IElementNamespace* pNamespace);
    
        // Members
        IElementBehaviorSite        *_pSite;
        IElementBehaviorSiteOM2        *_pOMSite;
    
    DECLARE_REGISTRY_RESOURCEID(IDR_BEHAVIOR)
    
    DECLARE_PROTECT_FINAL_CONSTRUCT()
    
    BEGIN_COM_MAP(CBehavior)
        COM_INTERFACE_ENTRY(IBehavior)
        COM_INTERFACE_ENTRY(IDispatch)
        COM_INTERFACE_ENTRY(IObjectWithSite)
        COM_INTERFACE_ENTRY(IElementBehavior)
        COM_INTERFACE_ENTRY(IElementBehaviorFactory)
        COM_INTERFACE_ENTRY(IElementNamespaceFactory)
        COM_INTERFACE_ENTRY(IElementNamespaceFactoryCallback)
    END_COM_MAP()
    
    // IBehavior
    public:
    };
    
  7. Add Mshtml.h to the Stdafx.h include file.

    Add #include "mshtml.h" below #include <atlcom.h>.

  8. Add the implementation for your derived methods.

    For the IElementBehavior interface, you must implement the IElementBehavior::Init, IElementBehavior::Notify, and IElementBehavior::Detach methods. In the examples in this document, Behavior.cpp contains the implementation.

    The IElementBehavior::Init method is implemented as shown in the following example.

    STDMETHODIMP CBehavior::Init(IElementBehaviorSite* pBehaviorSite)
    {
        HRESULT    hr;
    
        // Cache the IElementBehaviorSite interface pointer.
        _pSite = pBehaviorSite;
        _pSite->AddRef();
    
        // Cache the IElementBehaviorSiteOM interface pointer.
        hr = _pSite->QueryInterface(&_pOMSite);
    
        return hr;
    }
    

    The IElementBehavior::Notify method is implemented as shown in the following example.

    STDMETHODIMP CBehavior::Notify(LONG lEvent, VARIANT* pVar)
    {
        HRESULT            hr            = S_OK;
    
        IHTMLElement     *pElem          = NULL;
        IDispatch        *pDisp          = NULL;
        IHTMLDocument2   *pDoc           = NULL;
        IHTMLWindow2     *pWindow        = NULL;
    
        OLECHAR FAR* szMember           = L"simple";
        OLECHAR FAR* szItem             = L"LogTest";
        DISPID dispid;
        DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
    
        IDispatch *pWinDisp = NULL;
    
        switch (lEvent)
        {
        case BEHAVIOREVENT_CONTENTREADY:
            // End tag of the master element has been parsed (we can access the element).
            break;
    
        case BEHAVIOREVENT_DOCUMENTREADY:
            // HTML document has been parsed (we can access the document object model).
    
            hr = _pSite->GetElement(&pElem);
            if (hr)
                goto Cleanup;
    
            hr = pElem->get_document(&pDisp);
            if (hr)
                goto Cleanup;
    
            hr = pDisp->QueryInterface(IID_IHTMLDocument2, (LPVOID *) &pDoc);
            if (hr)
                goto Cleanup;
    
            hr = pDoc->get_parentWindow(&pWindow);
            if (hr)
                goto Cleanup;
    
            hr = pWindow->QueryInterface( IID_IDispatch, (LPVOID *) &pWinDisp);
            if (hr)
                goto Cleanup;
    
            hr = pWinDisp->GetIDsOfNames( IID_NULL, &szItem, 1, LOCALE_SYSTEM_DEFAULT, &dispid );
            if (hr)
                goto Cleanup;
    
            hr = pWinDisp->Invoke( dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
                                    DISPATCH_METHOD, &dispparamsNoArgs, NULL, NULL, NULL );
            if (hr)
                goto Cleanup;
    
        default:
            break;
        }
    
    Cleanup:
    
        if (pElem)
            pElem->Release();
        if (pDisp)
            pDisp->Release();
        if (pDoc)
            pDoc->Release();
        if (pWindow)
            pWindow->Release();
        if (pWinDisp)
            pWinDisp->Release();
    
        return hr;
    }
    

    The IElementBehavior::Detach method is implemented as shown in the following example.

    STDMETHODIMP CBehavior::Detach()
    {
        if (_pSite)
            _pSite->Release();
    
        if (_pOMSite)
            _pOMSite->Release();
    
        return S_OK;
    }
    
    
  9. For the IElementBehaviorFactory interface, you must implement the IElementBehaviorFactory::FindBehavior. In this example, the implementation is in the Behavior.cpp file.

    STDMETHODIMP CBehavior::FindBehavior(BSTR bstrBehavior, BSTR bstrBehaviorUrl,
            IElementBehaviorSite* pSite, IElementBehavior** ppBehavior)
    {
        HRESULT     hr = E_FAIL;
    
        CComBSTR    bstr;
    
        if (bstrBehavior == NULL)
        {
            goto Cleanup;
        }
    
        bstr = _T("MYTAG");
        if (wcscmp(bstrBehavior, bstr) == 0)
        {
            CComObject<CBehavior> * pBehavior;
    
            hr = CComObject<CBehavior>::CreateInstance(&pBehavior);
            if (hr)
                goto Cleanup;
    
            hr = pBehavior->QueryInterface(IID_IElementBehavior, (void**)ppBehavior);
        }
    
    Cleanup:
    
        return hr;
    }
    
    

    You must also implement the IElementNamespaceFactory::Create method, as shown in the following example.

    STDMETHODIMP CBehavior::Create(IElementNamespace * pNamespace)
    {
        CComBSTR bstr = L"MYTAG";
        pNamespace->AddTag(bstr, 0);
    
        return S_OK;
    }
    
    

    The final method you must implement is the IElementNamespaceFactoryCallback::Resolve method.

    STDMETHODIMP CBehavior::Resolve(BSTR bstrNamespace, BSTR bstrTagName,
                                    BSTR bstrAttrs, IElementNamespace* pNamespace)
    {
        return S_OK;
    }
    
    

Creating the Web Page

The next part of creating a binary element behavior is to create a Web page to host it. The following steps illustrate how to do this.

  1. Put an XMLNS attribute in the html element. This creates a namespace named CUSTOM.

    <HTML XMLNS:CUSTOM>
    
  2. Place an object element within the head element and set the ID and CLASSID attributes of the head element to the class identifier (CLSID) in your Behavior.rgs file. (Behavior.rgs is in the Resource Files section of Visual C++ 6.) The completed object tag is:

    <OBJECT ID="customFactory" CLASSID="clsid:A96EF4A7-FC47-11D3-A354-00C04F608392"></OBJECT>
    
  3. Add an IMPORT processing instruction after the object element. Set the NAMESPACE attribute to the namespace that you declared with the XMLNS attribute. Set the IMPLEMENTATION attribute to the ID attribute in the object element. The completed IMPORT element is:

    <?IMPORT NAMESPACE="CUSTOM" IMPLEMENTATION="#customFactory" />
    
  4. To use the binary element behavior, place a <CUSTOM:MYTAG></CUSTOM:MYTAG> element in the file.

Conceptual

Safe Initialization and Scripting for ActiveX Controls

Using Rendering Behaviors

Security Considerations: Element Behaviors