zoukankan      html  css  js  c++  java
  • C#的COM事件在C++和JAVA中触发和响应的实现

    在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 "..OutputComEvent.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
    ", 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 <stdio.h>
    #include <tchar.h>
    
    #include <atlbase.h> //增加
    extern CComModule _Module;//增加
    #include <atlcom.h>//增加
    
    // 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


  • 相关阅读:
    大气散射 Aerial Perspective
    Efficient GPU Screen-Space Ray Tracing
    screen space shadowmap unity
    Scriptable render pipeline unity
    winform学习之----重新绘制边框方法延伸
    winform学习之----将多个控件的click方法绑定到同一click方法中
    winform学习之----进程和线程
    winform学习之----打开文件对话框并将文件内容放入文本框
    winform学习之----Directory
    Winform窗体事件发生顺序
  • 原文地址:https://www.cnblogs.com/sparkleDai/p/7605057.html
Copyright © 2011-2022 走看看