2009-10-07 15 views
8

Espérons que c'est une question sans intelligence, mais cela montre mon manque d'expertise en C++. Je suis un programmeur C#, et j'ai déjà beaucoup travaillé avec P/Invoke dans le passé avec les dll C++/C d'autres personnes. Cependant, cette fois j'ai décidé d'écrire un wrapper C++ dll (non géré) moi-même, et ensuite j'appelle mon wrapper dll de C#.Comment configurer une fonction C++ pour qu'elle puisse être utilisée par p/invoke?

Le problème que je rencontre immédiatement est que je suis incapable de définir une fonction C++ qui peut être trouvée par p/invoke. Je ne sais pas ce que la syntaxe est, mais voici ce que je suis en train jusqu'à présent:

extern bool __cdecl TestFunc() 
{ 
    return true; 
} 

Au départ, je simplement eu, mais il ne fonctionne pas non plus:

bool TestFunc() 
{ 
    return true; 
} 

Et puis sur le côté C#, je:

public const string InterfaceLibrary = @"Plugins\TestDLL.dll"; 

    [DllImport(InterfaceLibrary, CallingConvention = CallingConvention.Cdecl, 
     EntryPoint = "TestFunc"), SuppressUnmanagedCodeSecurity] 
    internal static extern bool TestFunc(); 

Tout compile, mais quand j'exécute cette C# p/Invoke appel, je reçois un System.EntryPointNotFoundException: Impossible de trouver un point d'entrée nommé « TestFunc » dans DLL " Plugins \ TestDLL.dll '.

Sûrement cela doit être quelque chose d'incroyablement simple à l'extrémité C++ dont je ne connais pas la syntaxe.

Répondre

12

Vous voulez utiliser extern "C" ainsi que __declspec(export), comme ceci:

extern "C" _declspec(dllexport) bool TestFunc() 
{ 
    return true; 
} 

Pour plus de détails, voir MSDN on Marshalling Types.

+0

Parfait, ça l'a fait! J'avais aussi essayé d'avoir extern "C" dans le passé, mais cela n'a pas fonctionné. Il échoue jusqu'à ce que le _declspec (dllexport) soit ajouté. – x4000

1

Vous devez exposer cette fonction avec extern "C" sinon le nom est tronqué.

1

Le compilateur C++ modifie les noms de vos fonctions pour incorporer des informations sur les paramètres et les types de retour. Ceci est appelé mangling de nom. D'un autre côté, le compilateur C ne déforme pas les noms de vos fonctions.

Vous pouvez dire au compilateur C++ pour travailler comme un compilateur C en utilisant extern "C":

extern "C" __declspec(dllexport) bool TestFunc { return true; } 

Pour appeler des fonctions de C# en utilisant P/Invoke, vos noms ne doivent pas être mutilée. Par conséquent, vous pouvez réellement exporter des fonctions C vers C#. Si vous souhaitez que la fonctionnalité soit implémentée en C++, vous pouvez écrire une fonction C qui appelle simplement la fonction C++ implémentant la fonctionnalité.

6

Extension de la bonne réponse de Reed.

Un autre problème que vous pouvez rencontrer lors de l'exposition d'une fonction C++ via PInvoke est l'utilisation de types invalides. PInvoke ne peut réellement prendre en charge que le regroupement de types primitifs et d'anciens types struct/class de données.

Par exemple, supposons que TestFunc avait la signature suivante

void TestFunc(std::string input); 

Même en ajoutant extern "C" et __declspec(dllexport) ne serait pas suffisant pour exposer la fonction C++. Au lieu de cela, vous devez créer une fonction d'assistance qui n'expose que les types compatibles PInvoke, puis appelée dans la fonction principale.Par exemple

void TestFunc(const std::string& input) { ... } 

extern "C" _declspec(dllexport) void TestFuncWrapper(char* pInput) { 
    std::string input(pInput); 
    TestFunc(input); 
} 
+0

Ouais, c'est exactement ce genre de raison que j'écris un wrapper C++ dll, plutôt que d'essayer d'appeler toutes les fonctions dont j'ai besoin directement. Mon but est de créer une interface plus simple pour P/Invoke, tout en laissant toute la complexité nécessaire avec les callbacks et les types complexes, etc., du coté C++. Bons points! – x4000

+0

@ x4000, j'ai pris exactement la même approche avant. Sauf au lieu d'une nouvelle DLL, j'ai simplement ajouté les fonctions wrapper à la même DLL. Plus de DLL aurait été plus propre. – JaredPar

+0

La DLL initiale que j'emballe est une tierce partie, donc ce n'était pas une option pour moi. Mais vous avez raison, je pense qu'il est plus propre à bien des égards de séparer les enveloppes du contenu enveloppé lorsque cela est possible. – x4000

1

Faites quelque chose aime ceci:

#define EXPORT extern "C" __declspec(dllexport) 

Et puis déclarer toute fonction avec le mot-clé EXPORT, par exemple une fonction C++

BOOL getString(TCHAR* string, DWORD size); 

deviendrait

EXPORT BOOL getString(TCHAR* string, DWORD size); 

alors le plaisir partie: Allez à votre console VS et tapez:

dumpbin /EXPORTS <PATH_TO_GENERATED_DLL> 

et you'l voir le nom mutilée et ordinal de toutes vos fonctions facilement exportées, alors il est juste une question o les pInvoking

0

Construire tous les projets avec Plate-forme Win32 et bit approprié (par ex. x86 ou x64) option de construction.