程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> 匯編語言 >> Win32編程點滴 - 響應ActiveX控件的事件

Win32編程點滴 - 響應ActiveX控件的事件

編輯:匯編語言

在最近的一篇文章中說到了,如何創建ActiveX,這次我們來響應事件。這次,我們將創建一個類:CGeneralEventSink,它能夠響應任何Dispatch事件(事件的接口繼承與IDispatch)。

首先,我 們來回顧一下ConnectionPoint的概念。任何支持事件的對象(比如,ActiveX控件),都支持 IConnectionPointContainer接口,顧名思義就是一個IConnectionPoint的容器,包含了這個對象支持的 全部事件。IConnectionPoint代表了一組事件,調用IConnectionPoint::Advise並傳入我們想要接收事 件的對象指針。而IConnectionPoint::GetConnectionInterface返回的IID,是此接收事件的對象必須實 現的接口,否則Advise會失敗。來看一下AxAdviseAll的代碼:

//枚舉 IConnectionPointContainer中的每個IConnectionPoint,
//對於每個IConnectionPoint新建一個支持iid接口的CGeneralEventSink對象,並Advise。
//pUnk是控件的指針
HRESULT  AxAdviseAll(IUnknown * pUnk)
{
 HRESULT hr;
 IConnectionPointContainer  * pContainer = NULL;
 IConnectionPoint * pConnectionPoint=NULL;
  IEnumConnectionPoints * pEnum = NULL;
 hr = pUnk->QueryInterface (IID_IConnectionPointContainer,(void**)&pContainer);
 if (FAILED(hr)) goto  error1;
 hr = pContainer->EnumConnectionPoints(&pEnum);
 if (FAILED (hr)) goto error1;
 ULONG uFetched;
 while(S_OK == (pEnum->Next (1,&pConnectionPoint,&uFetched)) && uFetched>=1)
 {
 DWORD  dwCookie;
 IID iid;
 hr = pConnectionPoint->GetConnectionInterface (&iid);
 if (FAILED(hr)) iid = IID_NULL;
 //這裡傳入pUnk是為了通過pUnk 得到iid的ITypeInfo,進而判斷iid是否是Dispatch接口
 IUnknown * pSink = new  CGeneralEventSink(iid,pUnk);
 hr = pConnectionPoint->Advise (pSink,&dwCookie);
 pSink->Release();
 pConnectionPoint->Release ();
 pConnectionPoint = NULL;
 pSink = NULL;
 }
 hr =  S_OK;
error1: 
 if (pEnum)pEnum->Release();
 if (pContainer)  pContainer->Release();
 if (pConnectionPoint) pConnectionPoint->Release();
 return hr;
}

然後,來說一下Dispath事件。如果IConnectionPoint::GetConnectionInterface返回的IID代表的接 口是繼承於IDispatch的話,這個事件就是一個Dispath事件。當事件發生時,產生事件的對象不會直接 調用pObj->OnEvent(),而會調用pObj->Invoke(…)。例如,Flash控件的事件對象:(通過 vc的#import指令生成的)

struct __declspec(uuid("d27cdb6d-ae6d-11cf-96b8- 444553540000"))
_IShockwaveFlashEvents : IDispatch
{
  //
   // Wrapper methods for error-handling
  //
  // Methods:
   HRESULT OnReadyStateChange (
    long newState );
  HRESULT OnProgress  (
    long percentDone );
  HRESULT FSCommand (
    _bstr_t  command,
    _bstr_t args );
  HRESULT FlashCall (
     _bstr_t request );
};

當Flash控件產生事件時,它會調用IDispath::Invoke而 不是,OnReadyStateChange等方法。只有在vc,atl等框架裡面,這些框架會自動生成Invoke函數,在 Invoke函數中根據參數的不同(第一個參數memid代表代表哪個方法被調用),再調用 OnReadyStateChange等方法。

所以,雖然IConnectionPoint要求實現的接口是 _IShockwaveFlashEvents,但是我們的虛表中,只要存在IDispath的方法即可,虛表中之後 _IShockwaveFlashEvents的方法不會被直接調用(如果我們自己實現Invoke,也不會去調用的)。我們 告訴Flash控件,這是一個_IShockwaveFlashEvents接口,雖然它只實現了IDispath。就像 CGeneralEventSink中的代碼:

STDMETHOD(QueryInterface(REFIID riid,void **ppvObject))
{
   *ppvObject = NULL;
  if ( IID_IUnknown == riid)
  {
     *ppvObject = (IUnknown*)this;
  }
  else if (IID_IDispatch == riid ||  m_iid == riid)
  {
    *ppvObject = (IDispatch*)this;
  }
  else
  {
    return E_NOINTERFACE;
  }
  AddRef();
  return S_OK;
}

其中m_iid是IConnectionPoint要求實現的接口,通過 CGeneralEventSink構造函數參數傳入的。當然,m_iid一定要是一個dispatch接口(繼承於IDispatch) 。如果IConnectionPoint要求的接口不是繼承於IDispatch的,則m_iid會被設置為IID_NULL,然後 IConnectionPoint便得不到所要求的接口,Advise就會失敗,否則的話控件就會調用一個虛表中不存在 的方法(不是IDispatch事件的話,控件只能直接調用接口的方法,而不是Invoke),產生錯誤。

那麼,如何判斷m_iid是Dispatch接口呢?首先,得到代表此接口的ITypeInfo指針,這個請參考 代碼中的:

HRESULT CGeneralEventSink::GetIIDTypeInfo(IID iid,ITypeInfo **  ppInfo,IUnknown * pRelateObj);

簡單的說,就是控件的接口pRelateObj會實現 IProvideClassInfo接口,來提供它本身的ITypeInfo。再通過ITypeInfo::GetRefTypeInfo可以得到相關 事件的ITypeInfo。

接著,此ITypeInfo的TYPEATTR結構中的typekind表明了,此ITypeInfo是否 的Dispatch接口,代碼如下:

TYPEATTR *attr;
if (SUCCEEDED(pInfo- >GetTypeAttr(&attr)))
{
  if (attr->typekind == TKIND_DISPATCH)  isDispatch = true;
  pInfo->ReleaseTypeAttr(attr);
}

最後,CGeneralEventSink的IDispatch方法全部返回E_NOTIMPLE就可以了,畢竟控件只是通知我們事件發生了 ,而不關心我們有什麼反應。當然,為了讓提供的示例更有趣,代碼裡面的Invoke做了詳細的log(在得 到接口的ITypeInfo的同時,也枚舉了MEMID/DISPID對應的名字。還從注冊表中把iid變成了接口的名字 ,所有這一切參考CGeneralEventSink的構造函數)。

代碼下載:files.cnblogs.com/Greatest/TestActiveX2.zip

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved