程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C#的COM事件在C++和JAVA中觸發和響應的實現

C#的COM事件在C++和JAVA中觸發和響應的實現

編輯:C#入門知識

在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);
    }

}


IEvent接口

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);
    }

}

Paint實現

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

// 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的運行結果

n塊ズ…?http://www.Bkjia.com/soft下載http://download.csdn.net/detail/xxdddail/6710307


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