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; } 执行结果:
  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值