Cpp / Cli开火活动

时间:2012-03-29 11:46:48

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

我有一个cpp项目,一个c ++ cli项目和一个c#win表单项目。我想从我的本机cpp代码中激活一个方法并在c#项目中捕获它。我怎么能这样做?

1 个答案:

答案 0 :(得分:7)

可以有多种方法来回答这个问题,因为这些项目之间的依赖性要求很重要。我将尝试回答最常见的(我猜)案例:你已经拥有一个本机C ++库,并且你想在C#应用程序中使用该库。在那种情况下,C#项目依赖于本机库项目。在这种情况下,您可以使用网关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);
        }
    }
}