TheChaseMan's Frenetic SoapBox

Always looking for better ways to do things...

HOWTO: Create a binary DHTML behavior with VS.NET 2003

1. Create a new ATL project named TestBehavior.

2. Click Finish when the ATL wizard opens.

3. In the TestBehavior project, add a reference to:

 C:\WINDOWS\System32\stdole2.tlb
 C:\WINDOWS\System32\MSHTML.TLB
 C:\WINDOWS\System32\shdocvw.dll
 objsafe.tlb (See KB article Q182598 from MSDN)

4. In Class Vew, add a new class using the ATL Simple Object tempalte.

5. Set the short name to CBehavior and then click Finish.

6. In the CBehavior.h file, implement the IElementBehavior, IElementBehaviorFactory, IElementNamespaceFactory, IElementNamespaceFactoryCallback, and IObjectSafety interfaces by chaning the class declaration to the following:

class ATL_NO_VTABLE CBehavior :
   public ICBehavior,
   public SHDOCVW::IElementBehavior,
   public SHDOCVW::IElementBehaviorFactory,
   public SHDOCVW::IElementNamespaceFactory,
   public SHDOCVW::IElementNamespaceFactoryCallback,
   public IObjectSafety

7. Add the following public declarations under the CBehavior(){} constructor in Behavior.h:

 //IObjectSafety
 STDMETHOD(GetInterfaceSafetyOptions)(REFIID riid, DWORD* pdwSupportedOptions, DWORD* pdwEnabledOptions);
 STDMETHOD(SetInterfaceSafetyOptions)(REFIID riid, DWORD dwOptionsSetMask, DWORD dwEnabledOptions);
 
 // 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);

8. Add the following public members to the CBehavior class in the header file:

public:
    // Members
    IElementBehaviorSite *theBehaviorSite;
    IElementBehaviorSiteOM2 *theBehaviorSiteOM2;
    IHTMLBodyElement *theBodyElement;
    IHTMLElement *theBodyHTMLElement;
    IDispatch *theWindowDisp;
    IDispatch *theDocumentDisp;
    IHTMLDocument2 *theHTMLDocument;
    IHTMLWindow2 *theHTMLWindow;

9. Create a private function declaration in the CBehavior class in the header file:

private:
 void DoMyCustomThing(void);

10. In the Behavior.cpp file, add the following code:

void CBehavior::DoMyCustomThing(void)
{
 BSTR s = L"Hello World!";
 theBodyHTMLElement->put_innerHTML(s);
}

STDMETHODIMP CBehavior::Init(IElementBehaviorSite* pBehaviorSite)
{
    HRESULT hr;

    // Cache the IElementBehaviorSite interface pointer.
    theBehaviorSite = pBehaviorSite;
    theBehaviorSite->AddRef();

    // Cache the IElementBehaviorSiteOM interface pointer.
    hr = theBehaviorSite->QueryInterface(&theBehaviorSiteOM2);

    return hr;
}

STDMETHODIMP CBehavior::Notify(LONG lEvent, VARIANT* pVar)
{
    HRESULT hr = S_OK;
    IDispatch *pWinDisp = NULL;
 IHTMLElement *pElem = NULL;
 IDispatch *pDisp = NULL;
 IHTMLDocument2 *pDoc = NULL;
 IHTMLWindow2 *pWindow = 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 = theBehaviorSite->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;

  //Custom Process
  theBodyHTMLElement = pElem;
  theDocumentDisp = pDisp;
  theHTMLDocument = pDoc;
  theHTMLWindow = pWindow;
  this->DoMyCustomThing();

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

STDMETHODIMP CBehavior::Detach()
{
    if (theBehaviorSite)
        theBehaviorSite->Release();

    if (theBehaviorSiteOM2)
        theBehaviorSiteOM2->Release();

    return S_OK;
}

STDMETHODIMP CBehavior::FindBehavior(BSTR bstrBehavior, BSTR bstrBehaviorUrl,
        IElementBehaviorSite* pSite, IElementBehavior** ppBehavior)
{
    HRESULT     hr = E_FAIL;
 //For Debugging
 //Sleep(10000);

    CComObject * pBehavior;

    hr = CComObject::CreateInstance(&pBehavior);
    if (hr)
        goto Cleanup;

    hr = pBehavior->QueryInterface(IID_IElementBehavior, (void**)ppBehavior);

Cleanup:

    return hr;
}

STDMETHODIMP CBehavior::Create(IElementNamespace * pNamespace)
{
    return S_OK;
}

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

STDMETHODIMP CBehavior::GetInterfaceSafetyOptions(REFIID riid, DWORD* pdwSupportedOptions,
              DWORD* pdwEnabledOptions)
{
    return S_OK;
}

STDMETHODIMP CBehavior::SetInterfaceSafetyOptions(REFIID riid, DWORD dwOptionsSetMask,
              DWORD dwEnabledOptions)
{
    return S_OK;
}


11. From the build menu, click Build Solution.

12. Create a new HTML file and add the following HTML, with the exception of setting the clsid value to the value specified in the CBehavior.h file just above the CBehavior class declaration.

<HTML XMLNS:CUSTOM>
<HEAD>
<TITLE>Production Code</title>
<!-- DON'T FORGET TO CHANGE THE clsid: VALUE!!! -->
<object ID=objStart CLASSID="clsid:23B312F9-E480-4BB1-B8C6-F7E251015CE5"></object>

</HEAD>
<BODY Style="behavior:url(#objStart)" bColor='gray'>

</BODY>
</HTML>


Digg!

posted on Thursday, January 01, 2004 9:42 PM

Feedback

# re: HOWTO: Create a binary DHTML behavior with VS.NET 2003 2/23/2004 12:39 PM James Merrill

Thanks for sharing this. The documentation for behaviors does not, in my view, win an award for its entries in the "here is how you ..." category.

I wonder if there would be a way to make a version of this that gets parameterized in the <object> tag with -- for example -- the progid of a COM object that implements the equivalent of the DoMyCustomThing method, receiving as parameters the various objects being manipulated (pElem, pDisp, pDoc, pWindow).

Or some (any!) other way to avoid making a separate ATL-based project just to implement a single method.

# re: HOWTO: Create a binary DHTML behavior with VS.NET 2003 2/23/2004 4:24 PM Sean Chase

Yes, I believe you could. What I would do is put in a custom attribute on the object tag <object CallCLSID="whatever"...> and extend the binary behavior to do a passthrough.

# re: HOWTO: Create a binary DHTML behavior with VS.NET 2003 11/8/2004 2:35 PM Vorun

Can you make the project files available? thnxs

# re: HOWTO: Create a binary DHTML behavior with VS.NET 2003 11/9/2004 8:15 AM Sean Chase

Sure, try

http://www.unboxedsolutions.com/sean/examples/testbehavior.zip

# re: HOWTO: Create a binary DHTML behavior with VS.NET 2003 12/23/2004 6:43 PM gjguo

Thanks for sharing this.But you haven't mentioned how to implement the message connect and expose custom events.Would you please resolve the ploblems?I'm very looking forward to your solution.thanks

# re: HOWTO: Create a binary DHTML behavior with VS.NET 2003 12/23/2004 9:11 PM Sean Chase

Not sure what you mean by "custom events." Basically any event that the DHTML object model exposes for the item in question can be hooked. For more information, see http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/samples/internet/ie50/binarybehavior/default.asp

# re: HOWTO: Create a binary DHTML behavior with VS.NET 2003 12/23/2004 9:27 PM gjguo

thanks for your response.
Declare our own events(cutstom events) quite easily with script and XML code:
<PUBLIC:EVENT NAME="onResultChange" ID="rcID" />.but how to implement the thing in VS.net?

# Reflections on Programming Languages 10/20/2005 6:08 PM TheChaseMan's Frenetic SoapBox