大家都知道,爲ActiveX控件添加事件處理函數是件容易的事情,IDE已經提供相應的Wizard,爲ActiveX控件添加事件處理函數和爲一般控件添加事件處理函數沒有什麼兩樣。而爲普通COM組件添加事件處理函數,就沒有這麼直觀了,必須手工編寫相關代碼。
如果完全手工去編寫這些代碼,可以說是相當的麻煩,實際上相當編寫另外一個COM組件給原組件調用,至少要實現IDispatch接口才行。不過在ATL的幫助下,事情簡化了很多。
另外一方面,一個組件的事件,只有對應的客戶端才能收到,如何把事件變爲廣播消息,讓所有的客戶端都知道呢?這個問題容易解決:組件端記錄所有的客戶端,把事件發給每一個客戶端就行了。下面我們看一個簡單的例子。
一、實現COM組件服務端
(COM組件作爲一個服務器在一個單獨的EXE內部運行。)
l 用VC6新建一個ATL項目server,服務器類型爲Executable。
l Insert à New ATL ObjectàObjectsàSimple Object
l 名稱爲Chat,在屬性中選中Support Connection Points(即支持事件)。
l 爲IChat增加接口函數:HRESULT Send(BSTR str);
l 爲_IchatEvents增加事件:HRESULT OnMessage(BSTR str);
l ReBuild All
l 右鍵點擊類Cchat, Implement Connection Points
l 爲Cchat增加一個靜態成員,記錄所有Cchat對象實例。static std::list<CChat*> s_AllInstances;
l 實現Send函數,該函數中對所有Cchat對象實例觸發OnMessage事件。
STDMETHODIMP CChat::Send(BSTR str) { // TODO: Add your implementation code here for(std::list<CChat*>::iterator iter = s_AllInstances.begin(); iter != s_AllInstances.end(); iter++) { CChat* obj = *iter;
obj->Fire_OnMessage(str); }
return S_OK; } |
二、實現客戶端
用VC6創建ATL/WTL項目,應用程序類型爲Dialog based。
讓CmainDlg繼承IdispEventImpl接口。
class CMainDlg : public CAxDialogImpl<CMainDlg>, public CUpdateUI<CMainDlg>, public CMessageFilter, public CIdleHandler, public IDispEventImpl<0, CMainDlg, &DIID__IChatEvents, &LIBID_SERVERLib> |
增加事件函數描述信息。
_ATL_FUNC_INFO g_OnMessageInfo = {CC_STDCALL, VT_EMPTY, 1, {VT_BSTR} }; |
實現消息處理函數,不要忘了加_stdcall修飾。
void _stdcall OnRecv(BSTR str) { USES_CONVERSION; CListBox lb(GetDlgItem(IDC_LIST_ALL_MESSAGE)); lb.InsertString(-1, OLE2A(str));
return ; } |
增加事件映射。SINK_ENTRY_INFO 的第一個參數要與IdispEventImpl的第一個參數一致,其取值沒有限制。
BEGIN_SINK_MAP(CMainDlg) SINK_ENTRY_INFO(0, DIID__IChatEvents, 1, OnRecv, &g_OnMessageInfo) END_SINK_MAP() |
調用組件函數。
LRESULT OnSend(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) { char szBuff[1024] = {0}; sprintf(szBuff, "(%d):", GetCurrentProcessId());
this->GetDlgItemText(IDC_EDIT_MESSAGE, szBuff+strlen(szBuff), 1000);
m_ichat->Send(CComBSTR(szBuff)); // TODO : Add Code for control notification handler. return 0; } |
在OnInitDialog中增加初始化代碼。
if(SUCCEEDED(m_ichat.CoCreateInstance(CComBSTR(L"Server.Chat")))) { if(FAILED(DispEventAdvise(m_ichat, &DIID__IChatEvents))) { MessageBox("DispEventAdvise failed!"); } } else { MessageBox("CoCreateInstance failed!"); } |
在CloseDialog中增加~初始化代碼。
if(FAILED(DispEventUnadvise(m_ichat, &DIID__IChatEvents))) { MessageBox("DispEventUnadvise failed!"); } |