2009-12-01 15 views
11

Estoy un poco oxidado, realmente muy oxidado con mi C++. No lo he tocado desde primer año de la universidad, así que ha pasado un tiempo.Administrado C++ para formar un puente entre C# y C++

De todos modos, estoy haciendo lo contrario de lo que hace la mayoría de las personas. Llamando al código C# de C++. Hice algunas investigaciones en línea y parece que necesito crear C++ administrado para formar un puente. Usa __declspec (dllexport) y luego crea un dll a partir de eso y utiliza todo como un contenedor.

Pero mi problema es - Realmente estoy teniendo dificultades para encontrar ejemplos. Encontré algunas cosas básicas donde alguien quería usar la versión de C# para String.ToUpper() pero eso era MUY básico y solo era un pequeño fragmento de código.

¿Alguien tiene alguna idea de dónde puedo buscar algo un poco más concreto? Tenga en cuenta que NO quiero usar COM. El objetivo es no tocar el código C# en absoluto.

+0

¿Cuán complicada es la interfaz que desea exponer a C/C++ no administrado? – CuppM

+0

Vea este enlace para una pregunta/respuesta similar: http://stackoverflow.com/questions/13293888/how-to-call-ac-sharp-library-from-native-c-using-c-cli-and-ijw . – amalgamate

Respuesta

11

Mientras Lain me pegaba a escribir un ejemplo, voy a publicar de todos modos por si acaso ...

El proceso de escribir un derivador para acceder a su propia biblioteca es lo mismo que acceder a una de las bibliotecas .Net estándar.

Ejemplo C# código de clase en un proyecto llamado CsharpProject:

using System; 

namespace CsharpProject { 
    public class CsharpClass { 
     public string Name { get; set; } 
     public int Value { get; set; } 

     public string GetDisplayString() { 
      return string.Format("{0}: {1}", this.Name, this.Value); 
     } 
    } 
} 

Para ello se crea un proyecto de biblioteca de clases C++ administrada (ejemplo es CsharpWrapper) y añadir su proyecto de C# como una referencia a él. Para usar el mismo archivo de encabezado para uso interno y en el proyecto de referencia, necesita una forma de usar la declspec correcta. Esto se puede hacer definiendo una directiva de preprocesador (CSHARPWRAPPER_EXPORTS en este caso) y usando un #ifdef para establecer la macro de exportación en su interfaz C/C++ en un archivo de encabezado. El archivo de encabezado de interfaz no administrado debe contener elementos no administrados (o debe haberlos filtrado por el preprocesador).

Archivo de encabezado de interfaz C++ no administrado (CppInterface.h):

#pragma once 

#include <string> 

// Sets the interface function's decoration as export or import 
#ifdef CSHARPWRAPPER_EXPORTS 
#define EXPORT_SPEC __declspec(dllexport) 
#else 
#define EXPORT_SPEC __declspec(dllimport) 
#endif 

// Unmanaged interface functions must use all unmanaged types 
EXPORT_SPEC std::string GetDisplayString(const char * pName, int iValue); 

continuación, puede crear un archivo de encabezado interno para poder incluir en sus archivos de biblioteca administrados. Esto agregará las declaraciones using namespace y puede incluir las funciones auxiliares que necesite.

archivo de C++ Header interfaz (CsharpInterface.h):

#pragma once 

#include <string> 

// .Net System Namespaces 
using namespace System; 
using namespace System::Runtime::InteropServices; 

// C# Projects 
using namespace CsharpProject; 


////////////////////////////////////////////////// 
// String Conversion Functions 

inline 
String^ToManagedString(const char * pString) { 
return Marshal::PtrToStringAnsi(IntPtr((char *) pString)); 
} 

inline 
const std::string ToStdString(String^strString) { 
IntPtr ptrString = IntPtr::Zero; 
std::string strStdString; 
try { 
    ptrString = Marshal::StringToHGlobalAnsi(strString); 
    strStdString = (char *) ptrString.ToPointer(); 
} 
finally { 
    if (ptrString != IntPtr::Zero) { 
    Marshal::FreeHGlobal(ptrString); 
    } 
} 
return strStdString; 
} 

A continuación, sólo escribir el código de interfaz que hace la envoltura.

Gestionado archivo C++ Source Interface (CppInterface.cpp):

#include "CppInterface.h" 
#include "CsharpInterface.h" 

std::string GetDisplayString(const char * pName, int iValue) { 
CsharpClass^oCsharpObject = gcnew CsharpClass(); 

oCsharpObject->Name = ToManagedString(pName); 
oCsharpObject->Value = iValue; 

return ToStdString(oCsharpObject->GetDisplayString()); 
} 

A continuación, sólo incluyen la cabecera no administrado en su proyecto no administrado, decirle al enlazador a utilizar el archivo .lib generados al vincular, y asegurarse de que la Las DLL .Net y wrapper están en la misma carpeta que su aplicación no administrada.

#include <stdlib.h> 

// Include the wrapper header 
#include "CppInterface.h" 

void main() { 
// Call the unmanaged wrapper function 
std::string strDisplayString = GetDisplayString("Test", 123); 

// Do something with it 
printf("%s\n", strDisplayString.c_str()); 
} 
+0

Usted explicó las cosas un poco más, ojalá pudiera haber seleccionado ambas respuestas. ¡Gracias! –

+0

Las cadenas son un buen ejemplo ya que las funciones de conversión son necesarias tan pronto como necesite ordenar una cadena de nativa a administrada (que normalmente es muy pronto en la interoperabilidad!) – Iain

+0

Para el "No existe ese archivo o directorio", necesita o bien: 1) Agregue 'CppInterface.h' a la aplicación no administrada. No haría esto si todos los proyectos viven en la misma ubicación/repositorio. Como usted tiene archivos duplicados para mantener. 2) Agregue la ruta a los archivos del proyecto del contenedor en la carpeta de inclusión de compilación de la aplicación no administrada. Para Visual Studio, haga clic derecho en el proyecto y seleccione "Propiedades". Luego, en "Propiedades de configuración" -> "C/C++" -> "General" -> "Incluir directorios adicionales", agregue la ruta a la ubicación de 'CppInterface.h'. – CuppM

10

Cree un nuevo proyecto C++/CLI en Visual Studio y agregue una referencia a su C# dll. Supongamos que tenemos un C# DLL llamada DotNetLib.dll con esta clase en:

namespace DotNetLib 
{ 
    public class Calc 
    { 
     public int Add(int a, int b) 
     { 
      return a + b; 
     } 
    } 
} 

Ahora agregar una clase CLR C++ para su proyecto de C++/CLI:

// TestCPlusPlus.h 

#pragma once 

using namespace System; 
using namespace DotNetLib; 

namespace TestCPlusPlus { 

    public ref class ManagedCPlusPlus 
    { 
    public: 
     int Add(int a, int b) 
     { 
      Calc^ c = gcnew Calc(); 
      int result = c->Add(a, b); 
      return result; 
     } 
    }; 
} 

Para ello, será C# en C++.

Ahora bien, si es necesario, puede agregar una clase nativa C++ para su proyecto ++/CLI C que puede hablar a la clase CLR C++:

// Native.h 
#pragma once 

class Native 
{ 
public: 
    Native(void); 
    int Add(int a, int b); 
    ~Native(void); 
}; 

y:

// Native.cpp 
#include "StdAfx.h" 
#include "Native.h" 
#include "TestCPlusPlus.h" 

Native::Native(void) 
{ 
} 

Native::~Native(void) 
{ 
} 

int Native::Add(int a, int b) 
{ 
    TestCPlusPlus::ManagedCPlusPlus^ c = gcnew TestCPlusPlus::ManagedCPlusPlus(); 
    return c->Add(a, b); 
} 

usted debería ser capaz de llama a la clase Native desde cualquier otro dll nativo de C++ como siempre.

Tenga en cuenta también que C++ administrado es diferente y fue superado por C++/CLI. Wikipedia lo explica mejor:

Cuestiones relacionadas