【问题标题】:Cpp / Cli Fire an EventCpp / Cli 触发事件
【发布时间】:2012-03-29 11:46:48
【问题描述】:

我有一个 cpp 项目、一个 cpp cli 项目和一个 c# win forms 项目。我想从我的本机 cpp 代码中触发一个方法并在 c# 项目中捕获它。我该怎么做?

【问题讨论】:

  • 这是一个互操作性问题,我建议您将其标记为这样,请参阅here 了解有关 pinvoke 的信息

标签: c# .net events c++-cli


【解决方案1】:

可以有多种方法来回答这个问题,因为这些项目之间的依赖要求很重要。我将尝试回答最常见(我猜)的情况:您已经拥有一个本机 C++ 库,并且您想在 C# 应用程序中使用该库。在这种情况下,C# 项目依赖于本机库项目。在这种情况下,您可以利用 gateway cli/c++ 库 将原生 c++ 事件转换为 .NET 事件。

这是一个完整的代码示例,但在此之前,请注意:

  • 这可能不是最短的解决方案,但效果很好。此外,它还可以对将本机数据转换为 .net 类型提供更多控制。
  • 我在 VS 2005 中使用了这种方法。我不知道在新版本的 VS 中是否有更好的工具来实现特定的互操作性目的。
  • 如果您的本机事件是从 GUI 线程以外的线程触发的,请注意 that


原生库:

#ifndef _NATIVE_CODE_H_
#define _NATIVE_CODE_H_

//NativeCode.h
//A simple native library which emits only one event.

#include <stdlib.h>
#include <iostream>
using namespace std;

#define NATIVELIBRARY_API __declspec(dllexport)

//An argument class to wrap event parameters
class NativeEventArgs{
public:
    //a 32bit integer argument
    //any other primitives can be here, just be careful about the byte size
    int argInt32;

    //null terminated ascii string
    const char* argString;

    //null terminated wide/unicode string
    const wchar_t* argWString; 
};

//A simple mechanism to fire an event from native code.
//Your library may have a DIFFERENT triggering mechanism (e.g. function pointers)
class INativeListener
{
public:
    virtual void OnEvent(const NativeEventArgs& args)=0;
};

//The actual native library code, source of native events
class NATIVELIBRARY_API NativeCode
{
public:
    NativeCode()
        :theListener_(NULL)
    {}

    //Listener registration method
    void registerListener(INativeListener* listener) {
        theListener_ = listener;
    }

    //this is the very first source of the event
    //native code emits the event via the listener mechanism
    void eventSourceMethod() {
        //... other stuff

        //fire the native event to be catched
        if(theListener_){
            //prepare event parameters
            NativeEventArgs args;
            wstring wstr(L"A wide string");
            string str("A regular string");

            //build-up the argument object
            args.argInt32 = 15;
            args.argString = str.c_str();
            args.argWString = wstr.c_str();

            //fire the event using argument
            theListener_->OnEvent( args );
        }
    }

private:

    //native code uses a listener object to emit events
    INativeListener* theListener_;
};

#endif


网关库示例:

//GatewayCode.h
//GatewayLibrary is the tricky part,
//Here we listen events from the native library
//and propagate them to .net/clr world

#ifndef _GATEWAY_CODE_H_
#define _GATEWAY_CODE_H_

#include "../NativeLibrary/NativeCode.h" //include native library
#include <vcclr.h> //required for gcroot
using namespace System;
using namespace System::Runtime::InteropServices;

namespace GatewayLibrary{

    //.net equvelant of the argument class
    public ref class DotNetEventArg{
    internal:

        //contructor takes native version of argument to transform
        DotNetEventArg(const NativeEventArgs& args) {

            //assign primitives naturally
            argInt32 = args.argInt32;

            //convert wide string to CLR string
            argWString = Marshal::PtrToStringUni( IntPtr((void*)args.argWString) );

            //convert 8-bit native string to CLR string
            argString = Marshal::PtrToStringAnsi( IntPtr( (void*)args.argString) );

            //see Marshal class for rich set of conversion methods (e.g. buffers)
        }
    private:
        String^ argString;
        String^ argWString;
        Int32 argInt32;

    public:
        //define properties
        property String^ ArgString {
            String^ get() {
                return argString;
            }
        }

        property String^ ArgWString {
            String^ get() {
                return argWString;
            }
        }

        property Int32 ArgInt32 {
            Int32 get() {
                return argInt32;
            }
        }
    };

    //EventGateway fires .net event when a native event happens.
    //It is the actual gateway class between Native C++ and .NET world.
    //In other words, It RECEIVES NATIVE events, TRANSFORMS/SENDS them into CLR.
    public ref class EventGateway {
    public:

        //ctor, its implementation placed below
        EventGateway();

        //required to clean native objects
        ~EventGateway();
        !EventGateway();

        //the SENDER part
        //.net event stuff defined here
        delegate void DotNetEventHandler(DotNetEventArg^ arg);
        event DotNetEventHandler^ OnEvent;

    private:
        //our native library code
        //notice you can have pointers to native objects in ref classes.
        NativeCode* nativeCode_; 

        //the required device to listen events from the native library
        INativeListener* nativeListener_; 

    internal: //hide from .net assembly

        //the RECEIVER part, called when a native event received
        void OnNativeEvent(const NativeEventArgs& args){
            //you can make necessary transformation between native types and .net types

            //create .net argument using native argument
            //required conversion is done by DotNetEventArg class
            DotNetEventArg^ dotNetArgs = gcnew DotNetEventArg(args);

            //fire .net event
            OnEvent( dotNetArgs );
        }

    };
}

//A concrete listener class. we need this class to register native library events.
//Its our second gateway class which connects Native C++ and CLI/C++
//It basically gets events from NativeLibary and sends them to EventGateway
class NativeListenerImp : public INativeListener {
public:
    NativeListenerImp(gcroot<GatewayLibrary::EventGateway^> gatewayObj ){
        dotNetGateway_ = gatewayObj;
    }

    //this is the first place we know that a native event has happened
    virtual void OnEvent(const NativeEventArgs& args) {

        //inform the .net gateway which is responsible of transforming native event to .net event
        dotNetGateway_->OnNativeEvent(args);
    }

private:
    //class member to trigger .net gateway.
    //gcroot is required to declare a CLR type as a member of native class.
    gcroot<GatewayLibrary::EventGateway^> dotNetGateway_;
};

////ctor and dtors of EventGateway class
GatewayLibrary::EventGateway::EventGateway()
{
    nativeCode_ = new NativeCode();

    //note; using 'this' in ctor is not a good practice
    nativeListener_ = new NativeListenerImp(this);

    //register native listener
    nativeCode_->registerListener(nativeListener_);
}

GatewayLibrary::EventGateway::~EventGateway()
{
    //call the non-deterministic destructor
    this->!EventGateway();
}

GatewayLibrary::EventGateway::!EventGateway()
{
    //clean up native objects
    delete nativeCode_;
    delete nativeListener_;
}

#endif


以及 C#(或任何其他 .net 语言)的最终应用程序:

//Program.cs
//C# the final evet consumer application

using System;
using System.Collections.Generic;
using System.Text;
using GatewayLibrary;

namespace SharpClient
{
    class Program
    {
        static void Main(string[] args)
        {
            //create the gateway
            EventGateway gateway = new EventGateway();

            //listen on .net events using the gateway
            gateway.OnEvent += new EventGateway.DotNetEventHandler(gateway_OnEvent);

        }

        static void gateway_OnEvent( DotNetEventArg args )
        {
            //use the argument class
            Console.WriteLine("On Native Event");
            Console.WriteLine(args.ArgInt32);
            Console.WriteLine(args.ArgString);
            Console.WriteLine(args.ArgWString);
        }
    }
}

【讨论】:

  • 感谢您的回答。什么是 GatewayListenerImp? Visual Studio 不知道。 (VS2010)
  • 好的,我已经编辑了我的答案,应该是NativeListenerImp。格式化时好像出错了。
  • 我还想问一件事。如何将参数发送到 gateway_OnEvent() 函数?
  • @seckin 我添加了参数传递示例。
  • @xaero99 我可以知道“eventSourceMethod”是做什么的吗?我只是尝试这段代码作为第一天学习它。不知何故,这种方法让我感到困惑。我的 c# 也不会触发 gateway_OnEvent ......我可以知道我错过了哪一部分吗?
猜你喜欢
  • 2021-05-28
  • 2020-10-25
  • 1970-01-01
  • 1970-01-01
  • 2020-09-24
  • 1970-01-01
  • 2015-07-08
  • 2010-10-27
  • 1970-01-01
相关资源
最近更新 更多