天天看點

alt 程序外com元件的連接配接事件

  1. 問題的提出

  類似于設計模式中Observer模式,在COM程式設計中,希望實作一種機制,使得對資料變化感興趣的若幹部分能夠接受到資料的變化通知。一個典型的應用:計算機監控程式在計算機狀态資料發生變化時通知系統管理者、系統日志程式、發送電子郵件等等,在com程式設計中連接配接點為我們友善的做到了這一點,在程序内com元件中我參考http://www.vckbase.com/document/viewdoc/?id=1538楊老師的文章很容易就參實作連接配接點的挂接,可是在程序外元件中去挂接失敗,如下面代碼所示: CSink sink; int _tmain(int argc, _TCHAR* argv[]) { ::CoInitialize( NULL ); IClassFactory *pCF=NULL; HRESULT hr = ::CoGetClassObject(__uuidof( FetionMsg),CLSCTX_LOCAL_SERVER, NULL, IID_IClassFactory, (void**)&pCF); if( SUCCEEDED( hr )){ IFetionMsg * sp = NULL; pCF->CreateInstance(NULL,__uuidof(IFetionMsg),(void **)&sp); if(sp){ _bstr_t bstrVar("test"); //CComBSTR bstrVar("test"); BSTR bstrValue = ::SysAllocString(L"程序外元件測試代碼。。。。"); // BSTR b=_com_util::ConvertStringToBSTR("資料");///使用前需要加上comutil.h和comsupp.lib sp->sendMsg(bstrVar); getchar(); sp->Release(); SysFreeString(bstrValue); } IConnectionPointContainerPtr spContainer; sp->QueryInterface(__uuidof(IConnectionPointContain er),(void**)&spContainer); CComQIPtr m_spCP; // 得到連接配接點接口 spContainer->FindConnectionPoint(__uuidof(_IFetionM sgEvents),&m_spCP ); if( !m_spCP ){ return 0; } DWORD m_dwCookie = 0; HRESULT hr = m_spCP->Advise(&sink, &m_dwCookie ); if( FAILED( hr ) ){ } pCF->Release(); } ::CoUninitialize(); return 0; } 這個問題我重試了很多次,開始以為是代理存根沒有注冊,後來注冊了也一樣,在網上搜尋了一下,原來也有很多人碰到了和我一樣的問題并且沒有找到很好的解決辦法,我納悶了好久,後來在微軟的管網上找到了相關介紹,網址:http://support.microsoft.com/kb/194179/zh-cn示例示範如何使用 ATL IDispEventImpl 和 IDispEventSimpleImpl 類來建立 ATL 接收器,但上面是在程序内元件中實作的,今天我這個例子是在程序外元件中實作,終于成功實作了程序外元件連接配接點事件的挂接,下面我們來看自己實作的程序外元件和接收器。

  2.實作部分

  一、程序外com元件執行個體

  用vs2008 atl 工程生成向導生成一個com元件工程

  元件類型選擇"可執行檔案EXE"

  建立一個atl簡單對象接口,注意要技術連接配接點

  

  添加一個OnMsg接口函數,在CProxy_ISrcObjEvents類中添加連接配接點的實作代碼 HRESULT Fire_OnMsg(BSTR p) { CComVariant varResult; T* pT = static_cast(this); int nConnectionIndex; CComVariant* pvars = new CComVariant[1]; int nConnections = m_vec.GetSize(); for (nConnectionIndex = 0; nConnectionIndex Lock(); CComPtr sp = m_vec.GetAt(nConnectionIndex); pT->Unlock(); IDispatch* pDispatch = reinterpret_cast(sp.p); if (pDispatch != NULL) { VariantClear(&varResult); pvars[0] = p; DISPPARAMS disp = { pvars, NULL, 1, 0 }; pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, &varResult, NULL, NULL); } } delete[] pvars; return varResult.scode; } 生成sys1.idl代碼如下: import "oaidl.idl"; import "ocidl.idl"; [ object, uuid(92AE347B-3623-4A3F-896E-DCE4C087CC92), dual, nonextensible, helpstring("ISrcObj 接口"), pointer_default(unique) ] interface ISrcObj : IDispatch{ [id(1), helpstring("方法test1")] HRESULT test1([in] BSTR p1, [in] BSTR p2); }; [ uuid(F43ABC61-454C-4EA3-B6D2-06C4C5584F9D), version(1.0), helpstring("sys1 1.0 類型庫") ] library sys1Lib { importlib("stdole2.tlb"); [ uuid(4AEE7640-CB24-419D-AA48-34DFB446548F), helpstring("_ISrcObjEvents 接口") ] dispinterface _ISrcObjEvents { properties: methods: [id(1), helpstring("方法OnMsg")] HRESULT OnMsg([in] BSTR p1); }; [ uuid(AF32FB41-38B7-42E7-85AA-6A447D755409), helpstring("SrcObj Class") ] coclass SrcObj { [default] interface ISrcObj; [default, source] dispinterface _ISrcObjEvents; }; }; OnMsg接口函數的實作 STDMETHODIMP CSrcObj::test1(BSTR p1, BSTR p2) { // TODO: 在此添加實作代碼 Fire_OnMsg(p1); return S_OK; } 這樣,程序内元件工程的實作就完成了,下面我們要實作在另一個程序裡調用OnMsg接口函數,并且能在接收器裡接收到事件。

  二、程序外元件事件連接配接

  建立一個win32工程,記得支援alt,加上頭檔案

  #include

  #include

  1、接收器的實作代碼 #pragma once #define IDC_SRCOBJ 103 #import "../sys1/Debug/sys1.tlb" no_namespace named_guids // /// // This file contains 4 different sink object all of which handle the 'Tick' // which is fired by the 'EventSrc' object // A COM object also acting as a sink. This class uses IDispEventImpl, with the // type library specified as a parameter. It use SINK_ENTRY_EX() to specify each // event form each source interface being handled. class CSinkObj1 :public IDispEventImpl { public: CSinkObj1() { } BEGIN_SINK_MAP(CSinkObj1) //Make sure the Event Handlers have __stdcall calling convention SINK_ENTRY_EX(IDC_SRCOBJ, DIID__ISrcObjEvents, 1, OnTick) END_SINK_MAP() // Event handler for 'Tick' - [id(1)] HRESULT Tick([in] long tckcnt); HRESULT __stdcall OnTick(long tickcnt) { // output string to list box printf( "Sink1 : Tick Event Received - %d\n", tickcnt); return S_OK; } }; // CSinkObj2 implements a sink object by deriving from IDispEventImpl but // not specifying the type library as a template argument. Instead the type // library and default source interface for the object are determined using // AtlGetObjectSourceInterface(). A SINK_ENTRY() macro is used for each // event from each source interface which is to be handled. class CSinkObj2 : public IDispEventImpl { public: CSinkObj2() { } BEGIN_SINK_MAP(CSinkObj2) //Make sure the Event Handlers have __stdcall calling convention SINK_ENTRY(IDC_SRCOBJ, 1, OnTick) END_SINK_MAP() // Event handler for 'Tick' - [id(1)] HRESULT Tick([in] long tckcnt); HRESULT __stdcall OnTick(BSTR p1) { // output string to list box char* lpszText2 = _com_util::ConvertBSTRToString(p1); printf( "Sink2 : Tick Event Received - %s\n", lpszText2); delete[] lpszText2; return S_OK; } }; // CSinkObj3 implements a sink object by deriving from IDispEventSimpleImpl. In // this case the type library is either not available or its more efficient not // to load the type library. The source interface ID is specified as an template // argument. A SINK_ENTRY_INFO() macro is used for each event from each source // interface which is to be handled. The last parameter to the macro is the // _ATL_FUNC_INFO structure which provides information about the event (source // interface method) since the type library is not available. static _ATL_FUNC_INFO OnTikcInfo = {CC_STDCALL, VT_BSTR, 1, {VT_BSTR}}; class CSinkObj3 : public IDispEventSimpleImpl { public: CSinkObj3() { } BEGIN_SINK_MAP(CSinkObj3) //Make sure the Event Handlers have __stdcall calling convention SINK_ENTRY_INFO(IDC_SRCOBJ, DIID__ISrcObjEvents, 1, OnTick, &OnTikcInfo) END_SINK_MAP() // Event handler for 'Tick' - [id(1)] HRESULT Tick([in] long tckcnt); HRESULT __stdcall OnTick(BSTR p1) { // output string to list box char* lpszText2 = _com_util::ConvertBSTRToString(p1); printf( "Sink3 : Tick Event Received - %s\n", lpszText2); delete[] lpszText2; return S_OK; } }; // CSinkObj4 is essentially same as CSinkObj3 except instead of providing the // _ATL_FUNC_INFO structure statically we can fill in the structure at run time. // This offers a little more flexibility and is a trade off of speed over size. class CSinkObj4 : public IDispEventSimpleImpl { public: CSinkObj4() { } BEGIN_SINK_MAP(CSinkObj4) //Make sure the Event Handlers have __stdcall calling convention SINK_ENTRY_EX(IDC_SRCOBJ, DIID__ISrcObjEvents, 1, OnMsg1) // equivalent to // SINK_ENTRY_INFO(IDC_SRCOBJ, DIID__EventSink, 1, OnTick, NULL) END_SINK_MAP() // fill in the _ATL_FUNC_INFO structured depending on DISPID HRESULT GetFuncInfoFromId(const IID& iid, DISPID dispidMember, LCID lcid, _ATL_FUNC_INFO& info) { if (InlineIsEqualGUID(iid, DIID__ISrcObjEvents)) { // fill in _ATL_FUNC_INFO with attributes of 'Tick' event info.cc = CC_STDCALL; switch(dispidMember) { case 1: info.vtReturn = VT_BSTR; info.nParams = 1; info.pVarTypes[0] = VT_BSTR; return S_OK; default: return E_FAIL; } } return E_FAIL; } // Event handler for 'Tick' - [id(1)] HRESULT Tick([in] long tckcnt); HRESULT __stdcall OnMsg1(BSTR p1) { // output string to list box char* lpszText2 = _com_util::ConvertBSTRToString(p1); printf( "Sink4 : OnMsg Event Received - %s\n", lpszText2); delete[] lpszText2; return S_OK; } }; 添加一個CSinkTest類,代碼如下: #pragma once #include "SinkObj.h" class CSinkTest { public: CSinkTest(void); ~CSinkTest(void); public: CComQIPtr m_spSrcObj; CSinkObj1* m_pSinkObj1; CSinkObj2* m_pSinkObj2; CSinkObj3* m_pSinkObj3; CSinkObj4* m_pSinkObj4; bool init(); LRESULT OnClickedSink1(); LRESULT OnClickedSink2(); LRESULT OnClickedSink3(); LRESULT OnClickedSink4(); }; #include "StdAfx.h" #include "SinkTest.h" CSinkTest::CSinkTest(void) { m_pSinkObj1 = NULL; m_pSinkObj2 = NULL; m_pSinkObj3 = NULL; m_pSinkObj4 = NULL; } CSinkTest::~CSinkTest(void) { if (m_pSinkObj1) { // disconnect from source if connected if (m_pSinkObj1->m_dwEventCookie != 0xFEFEFEFE) m_pSinkObj1->DispEventUnadvise(m_spSrcObj, &m_pSinkObj2->m_iid); delete m_pSinkObj1; } if (m_pSinkObj2) { // disconnect from source if connected if (m_pSinkObj2->m_dwEventCookie != 0xFEFEFEFE) m_pSinkObj2->DispEventUnadvise(m_spSrcObj, &m_pSinkObj2->m_iid); delete m_pSinkObj2; } if (m_pSinkObj3) { // disconnect from source if connected if (m_pSinkObj3->m_dwEventCookie != 0xFEFEFEFE) m_pSinkObj3->DispEventUnadvise(m_spSrcObj); delete m_pSinkObj3; } if (m_pSinkObj4) { // disconnect from source if connected if (m_pSinkObj4->m_dwEventCookie != 0xFEFEFEFE) m_pSinkObj4->DispEventUnadvise(m_spSrcObj); delete m_pSinkObj4; } } LRESULT CSinkTest::OnClickedSink1() { // Construct the sink object CSinkObj defined in SinkObj.h. // In this case the sink object is also another COM object // HRESULT hr = CComObject::CreateInstance(m_pSinkObj); // m_pSinkObj->AddRef(); // _ASSERTE(SUCCEEDED(hr)); // // // connect the sink and source, m_spSrcObj is the source COM object // hr = m_pSinkObj->DispEventAdvise(m_spSrcObj); // // // Error handling // if (FAILED(hr)) // { // TCHAR buf[1024]; // wsprintf(buf, "Connect Err-%x", hr); // } return 0; } LRESULT CSinkTest::OnClickedSink2() { // Make sure the COM object corresponding to pUnk implements IProvideClassInfo2 or // IPersist*. Call this method to extract info about source type library if you // specified only 2 parameters to IDispEventImpl m_pSinkObj2 = new CSinkObj2(); //m_pSinkObj2->AddRef(); HRESULT hr = AtlGetObjectSourceInterface(m_spSrcObj, &m_pSinkObj2->m_libid, &m_pSinkObj2->m_iid, &m_pSinkObj2->m_wMajorVerNum, &m_pSinkObj2->m_wMinorVerNum); _ASSERTE(SUCCEEDED(hr)); // connect the sink and source, m_spSrcObj is the source COM object hr = m_pSinkObj2->DispEventAdvise(m_spSrcObj, &m_pSinkObj2->m_iid); // Error handling if (FAILED(hr)) { TCHAR buf[1024]; wsprintf(buf, "Connect Err-%x", hr); } if(m_spSrcObj)m_spSrcObj->test1(_bstr_t("OnClickedS ink2_p1"),_bstr_t("OnClickedSink2_p2")); return 0; } LRESULT CSinkTest::OnClickedSink3() { m_pSinkObj3 = new CSinkObj3(); //m_pSinkObj3->AddRef(); // connect the sink and source, m_spSrcObj is the source COM object HRESULT hr = m_pSinkObj3->DispEventAdvise(m_spSrcObj); // Error handling if (FAILED(hr)) { TCHAR buf[1024]; wsprintf(buf, "Connect Err-%x", hr); } if(m_spSrcObj)m_spSrcObj->test1(_bstr_t("OnClickedS ink3_p1"),_bstr_t("OnClickedSink3_p2")); return 0; } LRESULT CSinkTest::OnClickedSink4() { m_pSinkObj4 = new CSinkObj4(); //m_pSinkObj4->AddRef(); // connect the sink and source, m_spSrcObj is the source COM object HRESULT hr = m_pSinkObj4->DispEventAdvise(m_spSrcObj); // Error handling if (FAILED(hr)) { TCHAR buf[1024]; wsprintf(buf, "Connect Err-%x", hr); } if(m_spSrcObj)m_spSrcObj->test1(_bstr_t("OnClickedS ink4_p1"),_bstr_t("OnClickedSink4_p2")); return 0; } bool CSinkTest::init() { ::CoInitialize( NULL ); IClassFactory *pCF=NULL; HRESULT hr = ::CoGetClassObject(__uuidof( SrcObj),CLSCTX_LOCAL_SERVER, NULL, IID_IClassFactory, (void**)&pCF); if( SUCCEEDED( hr )) { HRESULT hr1= pCF->CreateInstance(NULL,__uuidof(ISrcObj),(void**) &m_spSrcObj); if(m_spSrcObj){ return TRUE; } } return FALSE; } 調用部分代碼 #include "stdafx.h" #include "SinkTest.h" CSinkTest sink; int _tmain(int argc, _TCHAR* argv[]) { if(!sink.init()){ return 0; } //sink.OnClickedSink1(); //sink.OnClickedSink2(); sink.OnClickedSink3(); sink.OnClickedSink4(); getchar(); return 0; } 執行結果:

  

繼續閱讀