Cpp/Cli触发事件

本文关键字:事件 Cli Cpp | 更新日期: 2023-09-27 18:29:18

我有一个cpp项目、一个cpp-cli项目和一个c#win表单项目。我想从我的原生cpp代码中激发一个方法,并在c#项目中捕获它。我该怎么做?

Cpp/Cli触发事件

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

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

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


本地图书馆:

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