en utilisant une classe définie dans une DLL c ++ dans le code c #

J’ai une dll qui a été écrite en c ++, je dois utiliser cette dll dans mon code c #. Après la recherche, j’ai trouvé que l’utilisation de P / Invoke me permettait d’accéder à la fonction dont j’avais besoin, mais ces fonctions sont définies dans une classe et utilisent des variables de membre privées non statiques. J’ai donc besoin de pouvoir créer une instance de cette classe pour utiliser correctement les fonctions. Comment puis-je accéder à cette classe pour pouvoir créer une instance? J’ai été incapable de trouver un moyen de le faire.

Je suppose que je devrais noter que le dll c ++ n’est pas mon code.

Il n’y a aucun moyen d’utiliser directement une classe C ++ en code C #. Vous pouvez utiliser PInvoke de manière indirecte pour accéder à votre type.

Le modèle de base est que pour chaque fonction membre de la classe Foo, créez une fonction non membre associée qui appelle la fonction membre.

class Foo { public: int Bar(); }; extern "C" Foo* Foo_Create() { return new Foo(); } extern "C" int Foo_Bar(Foo* pFoo) { return pFoo->Bar(); } extern "C" void Foo_Delete(Foo* pFoo) { delete pFoo; } 

Maintenant, il suffit d’invoquer ces méthodes dans votre code C #

 [DllImport("Foo.dll")] public static extern IntPtr Foo_Create(); [DllImport("Foo.dll")] public static extern int Foo_Bar(IntPtr value); [DllImport("Foo.dll")] public static extern void Foo_Delete(IntPtr value); 

L’inconvénient est que vous aurez un IntPtr gênant à transmettre, mais il est assez simple de créer une classe d’encapsulation C # autour de ce pointeur pour créer un modèle plus utilisable.

Même si vous ne possédez pas ce code, vous pouvez créer une autre DLL qui encapsule la DLL d’origine et fournit une petite couche PInvoke.

Marshal C ++ Class et utiliser le PInvoke

Code C ++, NomClasse.h

 class __declspec(dllexport) CClassName { public: CClassName(); ~CClassName(); void function(); }; 

Code C ++, ClassName.cpp

 CClassName::CClassName() { } CClassName::~CClassName() { } void CClassName::function() { std::cout << "Bla bla bla" << std::endl; } 

Code C ++, fichier ClassNameCaller.h pour la fonction d'appelant

 #include "ClassName.h" #ifdef __cplusplus extern "C" { #endif extern __declspec(dllexport) CClassName* CreateClassName(); extern __declspec(dllexport) void DisposeClassName(CClassName* a_pObject); extern __declspec(dllexport) void function(CClassName* a_pObject); #ifdef __cplusplus } #endif 

Code C ++, fichier ClassNameCaller.cpp pour la fonction d'appelant

 #include "ClassNameCaller.h" CClassName* CreateClassName() { return new CClassName(); } void DisposeClassName(CClassName* a_pObject) { if(a_pObject!= NULL) { delete a_pObject; a_pObject= NULL; } } void function(CClassName* a_pObject) { if(a_pObject!= NULL) { a_pObject->function(); } } 

Code C #

 [DllImport("ClassNameDll.dll")] static public extern IntPtr CreateClassName(); [DllImport("ClassNameDll.dll")] static public extern void DisposeClassName(IntPtr pClassNameObject); [DllImport("ClassNameDll.dll")] static public extern void CallFunction(IntPtr pClassNameObject); //use the functions IntPtr pClassName = CreateClassName(); CallFunction(pClassName); DisposeClassName(pClassName); pClassName = IntPtr.Zero; 

Voici un exemple comment appeler la méthode de classe C ++ à partir de VB – pour C #, il suffit de réécrire le programme exemple à l’étape 4.

myfile.i

 %module learnaboutswig class A { public: void boringfunction(char *charstr); }; 

télécharger le swig de swig.org

swig -c ++ -csharp myfile.i

regardez le résultat, voyez si cela fonctionnera pour vous.

Vous devrez peut-être écrire une DLL intermédiaire (en C ++, peut-être) qui gère cela pour vous et expose l’interface dont vous avez besoin. Votre DLL serait chargée de charger la DLL tierce, de créer une instance de cet object C ++ et d’exposer ses fonctions membres selon les besoins via l’API que vous concevez. Vous utiliseriez alors P / Invoke pour accéder à votre API et manipuler proprement l’object.

Remarque: Pour l’API de votre DLL, essayez de limiter les types de données aux primitives (long, int, char *, etc.) pour éviter les problèmes de limites de module.

Je suis d’accord avec JaredPar. La création d’instances de classes non managées dans le code managé ne devrait pas être possible.

Autre chose: si vous pouviez recomstackr la DLL en C ++ géré ou en faire un composant COM, ce serait beaucoup plus facile /

La façon dont je l’ai fait est en créant un wrapper Managed C ++ fin autour de ma DLL C ++ non gérée. Le wrapper géré contient des classes “proxy” qui entourent le code non géré en exposant l’interface requirejse par l’application .NET. C’est un peu un double travail mais cela permet un fonctionnement assez transparent dans des environnements normaux. Les choses deviennent plus compliquées avec des dépendances dans certaines circonstances (telles que ASP.NET), mais vous ne rencontrerez probablement pas cela.