2009-12-01 4 views
11

Sono un po 'arrugginito, in realtà davvero arrugginito con il mio C++. Non l'ho toccato da quando sono tornato all'università di Freshman, quindi è passato un po 'di tempo.C++ gestito per formare un ponte tra C# e C++

In ogni caso, sto facendo il contrario di quello che fa la maggior parte delle persone. Chiamare il codice C# da C++. Ho fatto qualche ricerca online e sembra che ho bisogno di creare un C++ gestito per formare un ponte. Usa __declspec (dllexport) e poi crea una dll da quella e usa il tutto come un wrapper.

Ma il mio problema è - sono davvero avendo un momento difficile trovare esempi. Ho trovato alcune cose di base in cui qualcuno voleva usare la versione C# su String.ToUpper(), ma era MOLTO di base ed era solo un piccolo snippet di codice.

Qualcuno ha qualche idea di dove posso cercare qualcosa di un po 'più concreto? Nota, NON voglio usare COM. L'obiettivo è di non toccare affatto il codice C#.

+0

Quanto è complicata l'interfaccia che si desidera esporre a C/C++ non gestito? – CuppM

+0

Vedere questo link per una simile domanda/risposta: http://stackoverflow.com/questions/13293888/how-to-call-ac-sharp-library-from-native-c-using-c-cli-and-ijw . – amalgamate

risposta

11

Mentre Lain mi ha battuto a scrivere un esempio, vi posterò che comunque nel caso in cui ...

Il processo di scrittura un wrapper per accedere la propria libreria è uguale all'accesso a una delle librerie .Net standard.

Esempio C# codice di classe in un progetto chiamato 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); 
     } 
    } 
} 

Si potrebbe creare un progetto di libreria di classi C++ gestito (esempio è CsharpWrapper) e aggiungere il progetto C# come un riferimento ad esso. Per utilizzare lo stesso file di intestazione per uso interno e nel progetto di riferimento, è necessario un modo per utilizzare il declspec giusto. Questo può essere fatto definendo una direttiva preprocessore (CSHARPWRAPPER_EXPORTS in questo caso) e usando un #ifdef per impostare la macro di esportazione nell'interfaccia C/C++ in un file di intestazione. Il file di intestazione dell'interfaccia non gestito deve contenere elementi non gestiti (o essere filtrato dal preprocessore).

di file non gestito C++ Interface Header (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); 

Quindi è possibile creare un file di intestazione interno per poter includere nei file di libreria gestiti. Ciò aggiungerà le istruzioni using namespace e può includere le funzioni di supporto di cui hai bisogno.

Managed C++ file di interfaccia di intestazione (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; 
} 

Poi basta scrivere il codice di interfaccia che fa l'involucro.

file di Managed C++ Interface Source (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()); 
} 

Poi basta includere l'intestazione non gestito nel progetto non gestito, dire al linker di utilizzare il file lib generato durante il collegamento, e assicurarsi che il .Net e wrapper DLL si trovano nella stessa cartella dell'applicazione non gestita.

#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

Hai spiegato un po 'di più le cose - vorrei aver selezionato entrambe le risposte. Grazie! –

+0

Le stringhe sono un buon esempio, come le funzioni di conversione sono richiesti non appena è necessario marshall una stringa da nativo gestito (che è normalmente molto presto nella maggior interoperabilità!) – Iain

+0

Per il "No such file or directory", è necessario o: 1) Aggiungi 'CppInterface.h' all'app non gestita. Non lo farei se tutti i progetti vivono nella stessa posizione/repository. Come hai quindi i file duplicati da mantenere. 2) Aggiungere il percorso ai file del progetto wrapper nella cartella di compilazione della compilazione dell'app non gestita. Per Visual Studio, fare clic con il tasto destro del mouse sul progetto e selezionare "Proprietà". Poi sotto "Proprietà di configurazione" -> "C/C++" -> "Generali" -> "Directory di inclusione aggiuntive" aggiungere il percorso della posizione di 'CppInterface.h'. – CuppM

10

Creare un nuovo progetto C++/CLI in Visual Studio e aggiungere un riferimento al C# dll. Supponiamo di avere un C# dll chiamato DotNetLib.dll con questa classe in:

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

Ora aggiungere una classe CLR C++ al progetto 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; 
     } 
    }; 
} 

Questo chiamerà C# da C++.

Ora, se necessario, è possibile aggiungere una classe C++ nativo al progetto C++/CLI che può parlare con la classe CLR C++:

// Native.h 
#pragma once 

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

e:

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

si dovrebbe essere in grado di chiama la classe Native da qualsiasi altra dll C++ nativa come al solito.

Si noti inoltre che Managed C++ è diverso da ed è stato superceeded da C++/CLI. Wikipedia spiega meglio: