JavaScript中external與active host之間的事件調用機制

http://discuss.develop.com/archives/wa.exe?A2=ind0212b&L=atl&P=6237

 

 > Hello,
>       I have an ActiveX control  hosted in IE browser which raises
> events and also provides Callback procedures. Is it possible to sink
> those events in either JScript or JavaScript.

JScript does not have any kind of built-in support for event handlers.

The usual approach (the one taken by the IE DOM model itself) is using
simple properties to get/set the event handlers.  You expose a property
from your object like this:

[ propget ] HRESULT eventHandler( [out, retval] VARIANT * handler );
[ propput ] HRESULT eventHandler( [in] VARIANT handler );

And implement them to simply store a VARIANT in your COM object.  Then,
at the script side, you just write the script function which is supposed
to do the event handling, and assign it as the event handler like this:

function onEvent( ) { blah; }
function OnLoadPage()
{
// ...
window.external  // your COM object
  .eventHandler = onEvent;  // assign the event handler
}

This will make a call to put_eventHandler( ).  JScript passes a
VT_DISPATCH variable to your COM object, which you store for later use.
When you're going to fire the event in the COM code, you should invoke
the dispatch passed to you from the script source with DISPID_VALUE,
which triggers a call to the JScript function.

The major work on this is easily encapsulated in a C++ class, which is
what I have done.  Here is the class which you can use in your
application:

#include <map>

/**

  This class provides support for MSHTML style event handler properties.
These are
  properties of VARIANT type accepting IDispatch pointers to the event
handler
  objects.  When an event is fired, the default method of these
IDispatch items
  (with the DISPID DISPID_VALUE) is invoked.  Only a single handler can
be
  associated with a single event and it will be called when the event is
fired.

  The client must derive from CEventHandlerManager.

**/

class CEventHandler
{
private:
typedef CAdapt< CComPtr< IDispatch > > CEventHandlerItem;

CEventHandler()
  : m_mapHandler()
{
}

protected:
HRESULT get_EventHandler(DISPID dispid, VARIANT * pVal) throw()
{
  const HRESULT hr = Utility::CheckParam( pVal );
  if (FAILED(hr))
   return hr;

  try
  {
   const std::map< DISPID, CEventHandlerItem >::const_iterator it =
    m_mapHandler.find( dispid );
   if (it != m_mapHandler.end())
   {
    V_VT( pVal ) = VT_DISPATCH;
    (V_DISPATCH( pVal ) = (*it).second.m_T)->AddRef();
   }
  }
  catch (...)
  {
   V_VT( pVal ) = VT_DISPATCH;
   V_DISPATCH( pVal ) = NULL;
   return E_FAIL;
  }

  return S_OK;
}

HRESULT put_EventHandler(DISPID dispid, VARIANT const & newVal) throw()
{
  IDispatch * pDisp = NULL;

  switch (V_VT( &newVal ))
  {
  case VT_UNKNOWN:
   if (FAILED( V_UNKNOWN( &newVal )->QueryInterface( &pDisp ) ))
    return E_INVALIDARG;
   break;
  case VT_DISPATCH:
   pDisp = V_DISPATCH( &newVal );
   break;
  default:
   return E_INVALIDARG;
  }

  ATLASSERT(pDisp);

  try
  {
   m_mapHandler.insert( std::make_pair( dispid,
    CEventHandlerItem( pDisp ) ) );
  }
  catch (...)
  {
   return E_FAIL;
  }

  return S_OK;
}

HRESULT Fire_Event(DISPID dispid)
{
  CComVariant val;
  const HRESULT hr = get_EventHandler( dispid, &val );
  if (FAILED(hr))
   return hr;

  if (V_VT( &val ) != VT_DISPATCH)
   return hr;

  return ATL::CComDispatchDriver( V_DISPATCH( &val ) ).
   Invoke0( DISPID( DISPID_VALUE ) );
}

template< class T1 >
HRESULT Fire_Event(DISPID dispid, T1 const & p)
{
  CComVariant val;
  const HRESULT hr = get_EventHandler( dispid, &val );
  if (FAILED(hr))
   return hr;

  if (V_VT( &val ) != VT_DISPATCH)
   return hr;

  const CComVariant param( p );
  return ATL::CComDispatchDriver( V_DISPATCH( &val ) ).
   Invoke1( DISPID( DISPID_VALUE ),
   const_cast< CComVariant * > (&param) );
}

template< class T1, class T2 >
HRESULT Fire_Event(DISPID dispid, T1 const & p1, T2 const & p2)
{
  CComVariant val;
  const HRESULT hr = get_EventHandler( dispid, &val );
  if (FAILED(hr))
   return hr;

  if (V_VT( &val ) != VT_DISPATCH)
   return hr;

  const CComVariant param[ 2 ] = { CComVariant( p1 ), CComVariant( p2 )
};
  return ATL::CComDispatchDriver( V_DISPATCH( &val ) ).
   Invoke2( DISPID( DISPID_VALUE ),
   const_cast< CComVariant * > (&param[ 0 ]),
   const_cast< CComVariant * > (&param[ 1 ]) );
}

template< class T1, class T2, class T3 >
HRESULT Fire_Event(DISPID dispid, T1 const & p1, T2 const & p2,
  T3 const & p3)
{
  CComVariant val;
  const HRESULT hr = get_EventHandler( dispid, &val );
  if (FAILED(hr))
   return hr;

  if (V_VT( &val ) != VT_DISPATCH)
   return hr;

  const CComVariant param[ 3 ] = { CComVariant( p1 ), CComVariant( p2 ),
   CComVariant( p3 ) };
  return ATL::CComDispatchDriver( V_DISPATCH( &val ) ).
   InvokeN( DISPID( DISPID_VALUE ),
   const_cast< CComVariant * > (param),
   sizeof(param) / sizeof(param[ 0 ]) );
}

private:
std::map< DISPID, CEventHandlerItem > m_mapHandler;

template < class ConnPoint >
friend class CEventHandlerManager;
};

template < class ConnPoint >
class __declspec(novtable) CEventHandlerManager :
public ConnPoint,
public CEventHandler
{
public:
CEventHandlerManager()
{
}
};

You just derive from CEventHandlerManager and pass the connection point
class as the template parameter (this is just to separate instances of
this class when using multiple connection points), and call the
following methods:

get_EventHandler( ) - Call this method to retrieve the event handler
associated with the event specified by its DISPID.  Useful for
get_eventHandler( ) implementation.
put_EventHandler( ) - Call this method to set the event handler
associated with the event specified by its DISPID.  Useful for
put_eventHandler( ) implementation.
Fire_Event( ) - Call this method to fire an event specified by its
DISPID. This method invokes the event handler's dispatch using
DISPID_VALUE.  It takes up to maximum three parameters which should be
convertible to ATL::CComVariant.


If you need any help with this implementation, let us know.

-------------
Ehsan Akhgari

List Owner: [email protected]

[ Email: [email protected]; [email protected] ]
[ WWW: http://www.beginthread.com/Ehsan ]

And if great things have been a failure with you, have you yourselves
therefore- been a failure? And if you yourselves have been a failure,
has man therefore- been a failure? If man, however, has been a failure:
well then! never mind! -Thus Spoke Zarathustra, F. W. Nietzsche

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章