2010-04-09 12 views
7

Entonces, lo que tengo es una API C++ contenida dentro de * .dll y quiero usar una aplicación C# para llamar a métodos dentro de la API.Exponer una API de C++ a C#

Hasta ahora han creado un proyecto/CLR C++ que incluye el C++ nativo API y logrado crear una clase de "puente" que se ve un poco como el siguiente:

// ManagedBridge.h 
#include <CoreAPI.h> 
using namespace __CORE_API; 

namespace ManagedAPIWrapper 
{ 
    public ref class Bridge 
    { 
     public: 
      int    bridge_test(void); 
      int    bridge_test2(api_struct* temp); 
    } 
} 

.

// ManagedBridge.cpp 
#include <ManagedBridge.h> 

int Bridge::bridge_test(void) 
{ 
    return test(); 
} 

int Bridge::bridge_test2(api_struct* temp) 
{ 
    return test2(temp); 
} 

I también tienen una aplicación # C que tiene una referencia a la C++/CLR "bridge.dll" y luego utiliza los métodos contenidos dentro. Tengo una serie de problemas con esto:

  1. no puedo encontrar la manera de llamar bridge_test2 dentro del programa de C#, ya que no tiene conocimiento de lo que es un api_struct realmente es. Sé que tengo que ordenar el objeto en algún lugar, pero ¿lo hago en el programa C# o en el puente C++/CLR?
  2. Esto parece un muy manera prolija de exponer todos los métodos en la API, ¿no hay una manera más fácil de que me esté perdiendo? (Que no utiliza P/Invoke!)

EDIT: Ok, así que tengo los conceptos básicos de trabajo ahora gracias a las respuestas a continuación, sin embargo, mi estructura (lo llaman "api_struct2" para este ejemplo) tiene tanto una enumeración nativa y la unión en el código C++ nativo, como el siguiente:

typedef struct 
{ 
    enum_type1 eEnumExample; 
    union 
    { 
      long  lData; 
      int  iData; 
      unsigned char ucArray[128]; 
      char  *cString; 
      void  *pvoid; 
    } uData; 
} api_struct2; 

Creo que he encontrado la manera de conseguir el funcionamiento de enumeración; Lo he vuelto a declarar en el código administrado y estoy realizando un "test nativo_entero = static_cast (eEnumExample)" para cambiar la versión administrada a original.

Sin embargo, la unión me tiene perplejo, no estoy seguro de cómo atacarlo ... ¿Alguien tiene ideas?

Respuesta

3

Sí, está pasando una estructura no administrada por referencia. Eso es un problema para un programa C#, los punteros son bastante incompatibles con la recolección de basura.Sin contar el hecho de que probablemente tampoco tenga la declaración para la estructura.

se puede resolver declarando una versión administrada de la estructura:

public value struct managed_api_struct { 
    // Members... 
}; 

ya se puede declarar el método como

int bridge_test2(managed_api_struct temp); // pass by value 

o

int bridge_test2(managed_api_struct% temp); // pass by reference 

escoger el último si la estructura tiene más de 4 campos (~ 16 bytes). El método necesita copiar los miembros de la estructura, uno por uno, en una api_struct no administrada y llamar al método de la clase no administrada. Desafortunadamente, esto es necesario porque el diseño de la memoria de una estructura administrada no es predecible.

Todo esto es bastante mecánico, es posible que obtenga ayuda from SWIG. No lo he usado yo mismo, no estoy seguro de si es lo suficientemente inteligente como para manejar una estructura aprobada.

Un enfoque completamente diferente es hacer que la clase contenedora sea más limpia dándole un constructor y/o propiedades que le permitan construir el contenido de una api_struct. O bien, podría declarar una clase ref de contenedor para la estructura, como lo haría en el código administrado.

+0

¿Significaría esto que creo el managed_api_struct en C++/CLI * .dll o en el código C# mismo? Además, pensé que podría pasar una estructura administrada al código nativo siempre que utilizara el atributo StructLayout. – Siyfion

+0

No importa, pero C++/CLI tiene sentido para evitar dependencias circulares. Sí, [StructLayout] funciona pero usted * debe * utilizar una llamada Marshal :: StructureToPtr(). El diseño de una estructura administrada no es predecible. –

+0

Ok, gracias, voy a dar una oportunidad ahora, veo dónde me encuentro. – Siyfion

2

ya que no tiene conocimiento de lo que en realidad es un api_struct

Es necesario definir una versión administrada en un ensamblado de .NET, que utiliza atributos (como StructLayoutAttribute) para asegurar que calcula las referencias correctamente.

Este parece ser un muy largo aliento [...]

El otro enfoque es crear un contenedor COM (por ejemplo, utilizando ATL) alrededor de su API. Esto podría requerir más esfuerzo, pero al menos evitará la doble codificación de las definiciones de estructuras y funciones necesarias para P/Invoke.

Corrección: Se ha creado un proyecto de C++/CLI: tan sólo tiene que añadir correcta '#pragma' para indicar al compilador este es el código de .NET, y entonces la salida es una asamblea el proyecto de C# sólo puede referenciar .

+0

¿Qué me ayudará a agregar el #pragma? ¿Y hay alguna forma de que pueda poner todas las referencias en el proyecto C++/CLI, de modo que cada proyecto que hace referencia a él no tenga que volver a declarar las clases/estructuras equivalentes de .NET? – Siyfion

+0

@Siyfion: '#pragma managed' permite el ingreso y la salida del código administrado. Ver http://msdn.microsoft.com/en-us/library/0adb9zxe(VS.100).aspx – Richard

+0

Entiendo eso, pero funciona actualmente, ¡así que no puedo ver qué agregar eso me permitirá hacerlo! ? – Siyfion

2

Yu están tratando de hacerlo de esta manera más complicado de lo que realmente es. Lo que quiere es dos estructuras diferentes. Uno administrado y otro no administrado. Expone la versión administrada externamente (a su aplicación C#). Será todo ".Net-ish" sin conceptos de uniones más o menos.

En su puente, recibe la versión administrada de la estructura, crea manualmente la estructura no administrada y escribe el código para mover tus datos, campo por campo, a la estructura no administrada. Luego llame a su código no administrado y finalmente mueva los datos a la estructura administrada.

Lo bueno de C++/CLI es que el código administrado también puede funcionar con código y datos no administrados (e incluye los archivos .h no administrados).