Manière correcte d’appeler une méthode DLL C à partir de C #

J’essaie d’exécuter certaines méthodes (dans ce cas particulier, rdOnAllDone) à partir d’une DLL tierce, écrite en C, et en parcourant les fichiers d’en-tête, j’ai trouvé ceci:

#ifndef TDECLSDONE #ifdef STDCALL #define CCON __stdcall #else #define CCON __cdecl #endif #define TDECLSDONE #endif #define DLLIMP __declspec (dllimport) DLLIMP int CCON rdOnAllDone (void(CCON *)(int)); 

Après avoir cherché un moyen d’appeler cette méthode, j’ai fait ceci:

 [DllImport("sb6lib.dll", CallingConvention = CallingConvention.Cdecl)] public static extern int rdOnAllDone(Delegate d); public delegate void rdOnAllDoneCallbackDelegate(); private static void rdOnAllDoneCallback() { Console.WriteLine("rdOnAllDoneCallback invoked"); } 

La méthode a été appelée correctement sauf que je n’ai pas pu obtenir le paramètre int. J’ai donc essayé d’append le paramètre d’entrée int comme ceci

 [DllImport("sb6lib.dll", CallingConvention = CallingConvention.Cdecl)] public static extern int rdOnAllDone(Delegate d); public delegate void rdOnAllDoneCallbackDelegate(int number); private static void rdOnAllDoneCallback(int number) { Console.WriteLine("rdOnAllDoneCallback invoked " + number); } 

Mais maintenant, délégué est appelé deux fois et le programme se bloque avec le message d’erreur suivant: “vshosts32.exe a cessé de fonctionner”

Quelle est la bonne façon d’appeler cette méthode DLL?

EDIT: J’ai oublié d’append la méthode Main:

 public static void Main() { rdOnAllDoneCallbackDelegate del3 = new rdOnAllDoneCallbackDelegate(rdOnAllDoneCallback); rdOnAllDone(del3); while (true) { Thread.Sleep(1000); } } 

Trois choses à faire pour que cela fonctionne correctement:

  • vous devez informer le commanditaire de pinvoke du type de délégué réel , utiliser Déléguer ne suffit pas. Cela créera le mauvais thunk qui ne permettra pas de bien préparer l’argument. Quel est ce que vous avez vu se produire.
  • vous devez informer le marshaller de la convention d’appel si ce n’est pas __stdcall avec l’atsortingbut [UnmanagedFunctionPointer]. Obtenir ce mauvais déséquilibre la stack avec de bonnes chances pour un crash dur.
  • vous devez stocker une référence à l’object délégué afin que le garbage collector ne la collecte pas. Il ne peut pas voir les références détenues par le code natif. Si vous vous trompez, le code natif échoue avec un crash brutal après la prochaine collecte de place.

Donc, cela devrait mieux fonctionner, modifiez si nécessaire:

 [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void rdOnAllDoneCallbackDelegate(int parameter); [DllImport("sb6lib.dll", CallingConvention = CallingConvention.Cdecl)] public static extern int rdOnAllDone(rdOnAllDoneCallbackDelegate d); class Foo { private static rdOnAllDoneCallbackDelegate callback; // Keeps it referenced public static void SetupCallback() { callback = new rdOnAllDoneCallbackDelegate(rdOnAllDoneCallback); rdOnAllDone(callback); } private static void rdOnAllDoneCallback(int parameter) { Console.WriteLine("rdOnAllDoneCallback invoked, parameter={0}", parameter); } } 

La signature de votre délégué doit correspondre à celle du rappel natif et également définir correctement UnmanagedFunctionPointerAtsortingbute .

Dans votre cas comme ça:

 [UnmanagedFunctionPointerAtsortingbute(CallingConvention.Cdecl)] public delegate void rdOnAllDoneCallbackDelegate(int parameter); [DllImport("sb6lib.dll", CallingConvention = CallingConvention.Cdecl)] public static extern int rdOnAllDone(rdOnAllDoneCallbackDelegate callback); 

Usage:

 { rdOnAllDone(rdOnAllDoneCallback); } private static void rdOnAllDoneCallback(int parameter) { Console.WriteLine("rdOnAllDoneCallback invoked, parameter={0}", parameter); }