How To Set DHTML Event Properties from C++ (253915)



The information in this article applies to:

  • Microsoft Internet Explorer (Programming) 4.0
  • Microsoft Internet Explorer (Programming) 4.01
  • Microsoft Internet Explorer (Programming) 4.01 SP1
  • Microsoft Internet Explorer (Programming) 4.01 SP2
  • Microsoft Internet Explorer (Programming) 5
  • Microsoft Internet Explorer (Programming) 5.01
  • Microsoft Internet Explorer (Programming) 5.5

This article was previously published under Q253915

SUMMARY

This article describes how to set C++ event handlers on the DHTML event properties, such as put_onmousedown, from a WebBrowser control host, ActiveX control, or other C++ object.

MORE INFORMATION

The Dynamic HTML object model allows script to assign functions directly to event handlers using event properties:
<SCRIPT LANGUAGE="jscript">
function mousedownhandler()
{
   // do stuff;
}

function afterPageLoads()
{
   someElement.onmousedown = mousedownhandler;
}
</SCRIPT>
				
To duplicate this form of event handling from C++, implement a simple COM object for each function object that implements only the IUnknown and IDispatch interfaces. Pass a reference to this object inside a variant to the appropriate event property. When the event fires, the IDispatch::Invoke function will be executed with a DISPID of 0 to signal the event.

The following example header file implements a template class that can be used to easily create these "function objects" in C++.

FUNCTIONOBJECT.H:
// FunctionObject.h: interface for the CFunctionObject class.
// 
// USAGE:
// The user of this class is a C++ class (CYourClass) that must:
//		
// 1. Implement a member function on CYourClass of the form:
//				
// void MyEventCallback(DISPID id, VARIANT* pVarResult);
// 
// This function will be called with the dispid set in
// CreateEventFunctionObject. One event handler function can service
// multiple event function objects.
// 
// 2. When setting function object event handlers on MSHTML, use code of
// this form:
// 
// _variant_t varFO =
//    CFunctionObject<CYourClass>::CreateEventFunctionObject(this,
//       MyEventCallback, DISPID_MOUSEDOWN);
// [HTML object interface]->put_onmousedown(varFO);
// 
// This syntax requires VC++ COM compiler support for the _variant_t class. 
// Otherwise, pass into the put_mousedown (or other MSHTML event prop)
// a VARIANT containing the LPDISPATCH returned from
// CreateEventFunctionObject.
// 
// 3. Set all MSHTML function object event handlers to NULL before the
// MSHTML document is destroyed:
// 
//   VARIANT varNull; varNull.vt = VT_NULL;
//   [HTML object interface]->put_onmousedown(varNull);
// 
// At minimum, do this in an event handler for the HTML window object's 
// "onbeforeunload" event or WebBrowser control BeforeNavigate2. The 
// function objects will be stranded in memory if this isn't done before
// the HTML document is unloaded.If you set the handlers to NULL in 
// BeforeNavigate2 then you should also account for the last case when 
// the application is being shutdown since BeforeNavigate2 will not be
// called at that time. 


////////////////////////////////////////////////////////////////////// 

template <class T> class CFunctionObject : public IDispatch  
{
typedef void (T::*EVENTFUNCTIONCALLBACK)(DISPID id, VARIANT* pVarResult);

public:
   CFunctionObject() { m_cRef = 0; }
	
   ~CFunctionObject() {}

   HRESULT __stdcall QueryInterface(REFIID riid, void** ppvObject)
   {
      *ppvObject = NULL;

      if (IsEqualGUID(riid, IID_IUnknown))
         *ppvObject = reinterpret_cast<void **> (this);

      if (IsEqualGUID(riid, IID_IDispatch))
         *ppvObject = reinterpret_cast<void **> (this);

      if (*ppvObject)
      {
         ((IUnknown*)*ppvObject)->AddRef();
         return S_OK;
      }
      else return E_NOINTERFACE;
   }

   DWORD __stdcall AddRef()
   {
      return InterlockedIncrement(&m_cRef);
   }

   DWORD __stdcall Release()
   {
      if (InterlockedDecrement(&m_cRef) == 0)
      {
         delete this;
         return 0;
      }
      return m_cRef;
   }

   STDMETHOD(GetTypeInfoCount)(unsigned int FAR* pctinfo)
   { return E_NOTIMPL; }
   STDMETHOD(GetTypeInfo)(unsigned int iTInfo, LCID  lcid,
      ITypeInfo FAR* FAR*  ppTInfo)
   { return E_NOTIMPL; }
   STDMETHOD(GetIDsOfNames)(REFIID riid, OLECHAR FAR* FAR* rgszNames,
      unsigned int cNames, LCID lcid, DISPID FAR* rgDispId)
   { return S_OK; }
   STDMETHOD(Invoke)(DISPID dispIdMember, REFIID riid, LCID lcid,
      WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult,
      EXCEPINFO * pExcepInfo, UINT * puArgErr)
   {
      if (DISPID_VALUE == dispIdMember)
         (m_pT->*m_pFunc)(m_id, pVarResult);
      else
         TRACE(_T("CFO Invoke dispid = %d\n"), dispIdMember);
	
      return S_OK;
   }

public:
   static LPDISPATCH CreateEventFunctionObject(T* pT,
                        EVENTFUNCTIONCALLBACK pFunc, DISPID id)
   {
      CFunctionObject<T>* pFO = new CFunctionObject<T>;
      pFO->m_pT = pT;
      pFO->m_pFunc = pFunc;
      pFO->m_id = id;
      return reinterpret_cast<LPDISPATCH> (pFO);
   }

protected:
   T* m_pT;
   EVENTFUNCTIONCALLBACK m_pFunc;
   DISPID m_id;
   long m_cRef;
	};
				

Modification Type:MinorLast Reviewed:7/13/2004
Keywords:kbDHTML kbhowto KB253915