2012-03-29 26 views
5

Tengo un proyecto cpp, un proyecto cpp cli y un proyecto de formularios C# win. Quiero disparar un método desde mi código cpp nativo y atraparlo en el proyecto C#. ¿Cómo puedo hacer esto?Cpp/Cli Incendio de un evento

+0

esta es una pregunta de interoperabilidad, sugiero que la etiquete como tal, consulte [aquí] (http://msdn.microsoft.com/en-us/library/2x8kf7zx (v = vs.80) .aspx) para información sobre pinvoke – ldgorman

Respuesta

7

Puede haber múltiples enfoques para responder esta pregunta, porque el requisito de dependencia entre esos proyectos es importante. Trataré de responder por el caso más común (supongo): en el que ya tienes una biblioteca C++ nativa y quieres usar esa biblioteca en una aplicación C#. En ese escenario, el proyecto C# depende del proyecto de la biblioteca nativa. En tal caso, puede utilizar gateway cli/C++ library para transformar eventos C++ nativos en eventos .NET.

Aquí es un ejemplo de código completo, pero antes de eso, tenga en cuenta:

  • puede no ser la solución más corta, pero funciona bien. También puede proporcionar más control en la transformación de datos nativos a tipos de .NET.
  • Utilicé este enfoque en VS 2005. No sé si hay un instrumento mejor en las versiones más nuevas de VS para ese propósito específico de interoperabilidad.
  • Si su evento nativo se desencadena a partir de un hilo que no sea el hilo de la GUI, tenga cuidado con that.


La biblioteca nativa:

#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 


puerta de enlace Biblioteca de la muestra:

//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 


Y la aplicación final en C# (o en cualquier otro lenguaje .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); 
     } 
    } 
} 
+0

Gracias por su respuesta. ¿Qué es GatewayListenerImp? Visual Studio no lo sabe. (VS2010) –

+1

OK, he editado mi respuesta. Debería ser 'NativeListenerImp'. Parece que cometí un error al formatear. – xaero99

+0

Muchas gracias ... Ha funcionado –

Cuestiones relacionadas