在C++中調用C#開發COM組件時,一般的接口調用都比較容易實現,但是對於COM組件中的事件,C++中要去響應卻不好實現。因為C#中事件是采用委托機制,而C++中卻沒有委托的機制,這樣就無法實現對應。那要怎麼辦呢?
在C++中雖然沒有委托的類型來對應,不過C++卻可以開發ATL組件,同時裡面有用到事件的映射,那麼我們是不是可以應用這種機制去實現呢?進過不斷的查找資料和一番努力,總算是達成了目標,請看效果圖。
Trigger Event是由C#封裝的COM組件內部輸出的,而Event Reponse : 10000是由COM組件觸發C++的事件後輸出的。那麼這個具體要如何實現呢?我們先看C#的COM組件代碼:
IPaint接口
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; namespace ComEvent { [Guid("7EEDF2D8-836C-4294-90A0-7A144ADC93F9")] [InterfaceType(ComInterfaceType.InterfaceIsDual)] public interface IPaint { [DispId(1)] void Draw(int count); } }
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; namespace ComEvent { [Guid("7FE32A1D-F239-45ad-8188-89738C6EDB6F")] [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] public interface IEvent { [DispId(20)] void DrawEvent(int count); } }
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; namespace ComEvent { [Guid("76BBA445-7554-4308-8487-322BAE955527"), ClassInterface(ClassInterfaceType.None), ComDefaultInterface(typeof(IPaint)), ComSourceInterfaces(typeof(IEvent)), ComVisible(true)] public class Paint : IPaint { public delegate void DrawDelegate(int count); //注意事件的名稱必須和IEvent定義的名字一致,而且必須public public event DrawDelegate DrawEvent; #region IPaint 成員 public void Draw(int count) { Console.WriteLine("Trigger Event"); OnDraw(count); } public void OnDraw(int count) { try { if (DrawEvent == null) { Console.WriteLine("Event is NULL!"); } else { DrawEvent(count); } } catch (Exception ex) { Console.WriteLine(ex.Message); } } #endregion } }說明
1.代碼中的甩有GUID都必須不一樣,可以使用GUID生成器來生成。其中要特別注意的是IEvent接口中的DsidpId的值,我在實現時就是這裡吃了很大的虧。
2.事件接口IEvent的接口類型一般是InterfaceIsIDispatch。
3.在實現的類中Paint需要添加對所要暴露的COM接口加以引用,即 ComDefaultInterface(typeof(IPaint))和ComSourceInterfaces(typeof(IEvent)),注意typeof後面的是我們自定義的接口。
4.由於Paint中沒有繼承IEvent接口,但在Paint卻要有相應的DrawEvent事件觸發,所以我們需要在Paint中定義一個相同於IEvent中DrawEvent的委托來對應,即public delegate void DrawDelegate(int count)和public event DrawDelegate DrawEvent;
5.為了使用COM組件能夠使用,需要對項目的屬性作一些配置。見下面的圖
編譯COM組件,這時Output會生成ComEvent.dll、ComEvent.pdb、ComEvent.tlb三個文件,其中ComEvent.tlb是要在C++調用COM組件時使用的。
下面是C++調用的實現代碼
ComCall_CPlusPlus.cpp
// ComCall_CPlusPlus.cpp : 定義控制台應用程序的入口點。 // #include "stdafx.h" #import "..\Output\ComEvent.tlb" using namespace ComEvent; ATL::CComModule _Module; class EventReceiver : public IDispEventImpl<0, EventReceiver, &(__uuidof(ComEvent::IEvent)), &(__uuidof(ComEvent::__ComEvent)), 1, 0> { public: STDMETHOD(DrawEventResponse)(int count); BEGIN_SINK_MAP(EventReceiver) SINK_ENTRY_EX(0, (__uuidof(ComEvent::IEvent)), 20, DrawEventResponse) END_SINK_MAP() }; STDMETHODIMP EventReceiver::DrawEventResponse(int count) { printf("Event Reponse : %d\n", count); return S_OK; } int _tmain(int argc, _TCHAR* argv[]) { CoInitialize(NULL); ComEvent::IPaintPtr pPaint(__uuidof(ComEvent::Paint)); _Module.Init(NULL, (HINSTANCE)GetModuleHandle(NULL)); EventReceiver * pReceiver = new EventReceiver; HRESULT hresult=pReceiver->DispEventAdvise(pPaint); pPaint->Draw(10000); pReceiver->DispEventUnadvise(pPaint); _Module.Term(); CoUninitialize(); return 0; }
// stdafx.h : 標准系統包含文件的包含文件, // 或是經常使用但不常更改的 // 特定於項目的包含文件 // #pragma once #include "targetver.h" #include說明#include #include //增加 extern CComModule _Module;//增加 #include //增加 // TODO: 在此處引用程序需要的其他頭文件
1.ATL模塊引用:需要在stdafx.h中增加atlbase.h、extern CComModule _Module和atlcom.h,在ComCall_CPlusPlus.CPP中增加ATL::CComModule _Module;
2.定義繼承自ATL模板接口IDispEventImpl的事件接收類EventReceiver。IDispEventImpl的模板參數第一個是0,第二個是事件接收類的名字EventReceiver,第三個參數是事件接口ID的指針(使用 &(__uuidof(ComEvent::IEvent))來計算),第四個參數是類ID的指針(使用 &(__uuidof(ComEvent::__ComEvent))來計算),第五個參數是1,第六個參數是0.
3.要注意添加對ComEvent命名空間的引用
4.事件映射DrawEventResponse。BEGIN_SINK_MAP、SINK_ENTRY_EX、END_SINK_MAP三個必須要一組,才能實現對事件的映射。
BEGIN_SINK_MAP的參數是EventReceiver
SINK_ENTRY_EX第一個參數是0,第二個參數是事件接口的ID(使用__uuidof(ComEvent::IEvent)來計算,該值必須與IDispEventImpl使用的事件ID一致),第四個參數是事件的DispId(就是在COM組件定義時定義的值20,一定要是這個值,不然會出錯),第五個參數是DrawEventResponse的具體實現。
5.初始化COM和釋放實例:CoInitialize(NULL)和 CoUninitialize()必須要配對
6.ATL實始化_Module.Init
7.ATL掛接和取消事件pReceiver->DispEventAdvise(pPaint)和pReceiver->DispEventUnadvise(pPaint)。
下面是JAVA的實現代碼
package com.event; import com.jacob.activeX.ActiveXComponent; import com.jacob.com.Dispatch; import com.jacob.com.DispatchEvents; import com.jacob.com.Variant; public class ComEventExample { public static void main(String[] args) { ActiveXComponent dotnetCom = new ActiveXComponent("ComEvent.Paint"); Dispatch test = (Dispatch) dotnetCom.getObject(); SensorEvents se = new SensorEvents(); DispatchEvents de = new DispatchEvents(test, se); Dispatch.call(test, "Draw", new Variant(10000)); } }
package com.event; import com.jacob.com.Variant; public class SensorEvents { public void DrawEvent(Variant[] i) { System.out.println("this is java event executed "+i[0]); } }一定要注意jacob.jar包的引用
這是JAVA的運行結果
下載http://download.csdn.net/detail/xxdddail/6710307