2010-06-17 3 views
3

J'ai écrit une DLL C++ wrapper pour C# à appeler. La DLL a été testée et a bien fonctionné avec mon programme de test C++.C# & C++, erreur d'exécution lors de l'appel C++ dll à partir de C#

maintenant intégré avec C#, j'ai eu une erreur d'exécution et s'est écrasé. Impossible d'utiliser le débogueur pour afficher plus de détails.

Le côté C++ n'a qu'une seule méthode:

#ifdef DLLWRAPPERWIN32_EXPORTS 
#define DLLWRAPPERWIN32_API __declspec(dllexport) 
#else 
#define DLLWRAPPERWIN32_API __declspec(dllimport) 
#endif 

#include "NB_DPSM.h" 

extern "C" { 
DLLWRAPPERWIN32_API int WriteGenbenchDataWrapper(string fileNameToAnalyze, 
    string parameterFileName, 
    string baseNameToSaveData, 
    string logFileName, 
    string& message) ; 
} 

du côté C#, il y a une définition,

[DllImport("..\\..\\thirdParty\\cogs\\DLLWrapperWin32.dll")] 
public static extern int WriteGenbenchDataWrapper(string fileNameToAnalyze, 
       string parameterFileName, 
       string baseNameToSaveData, 
       string logFileName, 
       ref string message); 

et un appel:

string msg = ""; 
    int returnVal = WriteGenbenchDataWrapper(rawDataFileName, 
        parameterFileName, outputBaseName, logFileName, ref msg); 

Je suppose qu'il doit être quelque chose de mal avec le dernier paramètre de la fonction. string& en C++ devrait être ref string en C#?

EDIT: 

Avons-nous vraiment besoin du extern "C"?

EDIT 2: 

après que je retire la extern "C de la dll, je me suis le EntryPointNotFoundException. Lorsque je regarde la DLL en utilisant DLL Export Viewer, j'ai trouvé le nom de la fonction est "int __cdecl WriteGenbenchDataWrapper (class std :: ..." Dois-je inclure le "__cdecl"?

+8

Qu'est-ce qu'une 'chaîne' dans C? Un 'char *'? Si c'est la classe C++ String, je crois que le marshaller ne supporte pas le marshalling de/vers ce type. Aussi: Quelle exception obtenez-vous et où cela se produit-il? – dtb

+0

Pourquoi ne pas utiliser le débogueur? –

+0

@jeffamaphone: Probablement parce que le problème bloque le CLR, pas l'application de 5YrsLaterDBA. –

Répondre

3

Il y a un tas de règles pour marsheling avec PInvoke. pour référence Marsheling between managaed & unmanaged

Mettre l'accent sur le côté C# premier. Si vous saviez une taille raisonnable du message à l'avant, vous pouvez utiliser le type StringBuilder et de définir cette taille, quelque chose comme.

[DllImport("DLLWrapperWin32.dll")] 
public static extern int WriteGenbenchDataWrapper(string fileNameToAnalyze, 
                string parameterFileName, 
                string baseNameToSaveData, 
                string logFileName, 
                StringBuilder message 
                int messageLength); 

Impression à partir du nom du message (et d'autres messages) indi ciates vous ne connaissez pas la taille avant, et vous ne serez pas passer un message partiel à la fonction alors peut-être

[DllImport("DLLWrapperWin32.dll")] 
public static extern int WriteGenbenchDataWrapper(in string fileNameToAnalyze, 
                in string parameterFileName, 
                in string baseNameToSaveData, 
                in string logFileName, 
                out string message); 

maintenant sur le côté de C/C - pour correspondre à la seconde définition

extern "C" // if this is a C++ file to turn off name mangling for this function only 
int WriteGenbenchDataWrapper(char * fileNameToAnalyze, 
           char * parameterFileName, 
           char * baseNameToSaveData, 
           char * logFileName, 
           char ** message) { 
    string internalMessage; 
    SomeFunc(internalMessage); // these functions won't have extern "C" applied 
    * message = (char *)::CoTaskMemAlloc(internalMessage.length()+1); 
    strcpy(* message, internalMessage.c_str()); 
} 

Examen des unicode/cordes ansi est également important, reportez-vous à [MarshalAsAttribute (UnmanagedType.LPWSTR)]

pour le mode de sortie que vous souhaitez supprimer vos paramètres de chemin de développement ".. \ .. \ thirdParty \ de COGS"

+0

Ceci est un wrapper, je dois appeler une DLL de thirdparty et cette DLL est une DLL C++ avec des fonctions membres de la classe. On m'a dit que je ne pouvais pas utiliser "extern" C "" dans mon code. Est-ce correct? – 5YrsLaterDBA

+0

Vous pouvez utiliser extern "C" dans votre code pour votre interface, ne l'appliquez pas à l'interface C++. –

+0

comment puis-je supprimer l'espace alloué par CoTaskMemAlloc()? – 5YrsLaterDBA

1

Dans votre code C++:

J'ai toujours eu besoin de l'extern "C". C++ altère les noms de fonction si vous ne le faites pas (le mangling est nécessaire pour supporter la surcharge de fonction). L'extern "C" lui dit de ne pas le faire. Je vais aussi déclarer les fonctions comme __stdcall. Je crois que vous pouvez dire à C# quel type de convention d'appel utiliser, mais je pense que __stdcall est la valeur par défaut.En ce qui concerne le passage d'un objet string, je ne suis pas sûr de cela, je m'en tiens uniquement à l'utilisation de primitives pour le passage de paramètres, donc j'utiliserais constchar * et ajusterais en conséquence mon code C++.

En outre, j'essaie d'éviter de passer par référence. Au contraire, si j'ai besoin de retourner plusieurs valeurs, je vais mettre en place une série de getters pour gérer cela (un const char * retourne comme un IntPtr).

Dans votre code C#:

J'utilise pour la chaîne const char *, int pour int, et ainsi de suite. Je crois que Microsoft a un tableau quelque part pour vous dire ce qui devrait sous pour quoi. Lorsque vous traitez une chaîne renvoyée, vous devez la convertir en ANSI. Cela peut être fait avec un appel à Marshal.PtrToStringAnsi().

Exemple:

Dans mon code C++:

extern "C" __declspec(dllexport) const char* __stdcall GetCompany(const char *In) { 
    return MyGetCompany(In); // Calls the real implementation 
} 

Dans mon code C#:

[DllImport("TheDLL.dll", EntryPoint = "GetCompany")] 
private static extern IntPtr privGetCompany(String In); 

// Call this one, not the one above: 
public String GetProvince(String In) 
{ 
    return Marshal.PtrToStringAnsi(privGetCompany(In)); 
} 

Une note finale, si vous utilisez un 64- bit machine, la configuration 'Any CPU' fera un exécutable C# 64 bits, qui aura besoin d'une DLL 64 bits. Si vous avez seulement une DLL 32 bits, vous devrez ajouter une configuration (x86). Le message d'erreur que vous avez obtenu indique que votre programme C# trouve probablement la DLL correctement et la fonction aussi, donc le changement de nom n'est probablement pas le problème. Cela ressemble à appeler un problème de convention ou un problème avec le passage de paramètre.