FAQ: __cdecl calling convention bei callbacks


  • Administrator

    Ich möchte eine Korrektur eines FAQ Beitrages vorschlagen:
    http://www.c-plusplus.net/forum/viewtopic-var-t-is-192725.html

    Darin wird über die Möglichkeiten von Callbacks geredet, unteranderem dass Callbacks über die __cdecl Konvention nicht möglich seien. Das ist aber Unfug, wie ich heute nach langem knübeln und grübeln rausgefunden habe.

    Problemstellung
    Wir haben eine C++ DLL mit ca. folgendem Code:

    typedef void(*theCallback)(int, int);
    
    extern "C" __declspec(dllexport) void callMeBack(theCallback callback)
    {
      int a, b;
      a = 0;
      b = 0;
    
      callback(a, b);
      callback(a, b);
    }
    

    Wir wollen diese Funktion nun per P/Invoke aufrufen:

    internal delegate void theCallback(Int32 a, Int32 b);
    
    [DllImport("the.dll")]
    internal static void callMeBack(theCallback callback);
    
    // ...
    private void myCallback(int a, int b)
    {
    }
    
    public void foo()
    {
      callMeBack(this.myCallback);
    }
    

    Mit sehr hoher Wahrscheinlichkeit wird dies eine AccessViolationException auslösen, mit der Begründung dass protected Speicher beschädigt wurde. Um genau zu sein ist es der ESP (Extended Stack Pointer). Das Problem hier ist nämlich die Calling Convention. Eine C++ DLL verwendet standardmässig __cdecl , während DllImport standardmässig CallingConvention.Winapi verwendet. Für die WinAPI ist diese Einstellung auch absolut sinnvoll, für die C++ DLL allerdings weniger.

    Lösung
    Erster Schritt ist diese Korrektur:

    [DllImport("the.dll", CallingConvention = CallingConvention.Cdecl)]
    internal static void callMeBack(theCallback callback);
    

    Jetzt wird immerhin schon mal die Funktion korrekt aufgerufen. Wir haben aber immer noch ein Problem, nämlich die Callback-Funktion wird falsch aufgerufen. Um dies zu lösen müssen wir die delegate Deklaration mit einem Attribute ausstatten.

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    internal delegate void theCallback(Int32 a, Int32 b);
    

    Nun sollte alles einwandfrei funktionieren. Zumindest hat es bei mir dann endlich geklappt 🙂

    Referenzen:
    MSDN - DllImportAttribute
    MSDN - CallingConvention
    MSDN - UnmanagedFunctionPointerAttribute

    Grüssli


  • Administrator

    *push*
    Wollte nur mal nachfragen, was nun damit eigentlich ist? Wird das korrigiert oder nicht? Oder liegt sonst irgendein Problem damit vor?

    Grüssli


Anmelden zum Antworten