Aufruf-Konventionen ändern - wie geht das?
-
(Anm. vorab: Dies ist kein Cross-Posting, da es sich hier tatsächlich um eine neue Problematik handelt)
Hallo zusammen,
wie ja schon aus dem Beitrag C++ Dll-in C# verwenden zu ersehen war, kämpfe ich mit den Tücken der beiden Systeme. Insbesondere bei einer Callback-Funktion, die aus einer C++-DLL heraus aufgerufen wird, aber in C# geschrieben ist.
Insbesonder geht es darum, dass die Aufrufkonventionen beider Systeme unterscheidlich sind. So werden in C++ standardmäßig die Übergabeparameter nach einem Funktionsaufruf vom Aufrufer wieder vom Stack entfernt. In C# dagegen macht es jedoch standardmäßig der Aufgerufene.
Um das Problem zu lösen, gibt es für die Implementierung von C++-DLLs in C# Aufrufkonventionen (wie z.B. CallingConvention.Cdecl oder CallingConvention.StdCall), die bei der Import-Deklaration der einzelnen Funktionen angegeben werden müsen.Soweit so gut. Doch leider gelten diese Aufruf-Konentionen ebend nur für den Import von Funktionen aus einer DLL. Wie schaut das aber nun in meinem Fall aus? Wie sage ich einer in C# programmierten Methode, dass sie von einer C++-Komponente als Callback-Funktion aufgerufen wird und deshalb die Parameter nicht selbst vom Stack zu nehmen hat?
Ich würde mich freuen, wenn sich jemand damit auskennt und mir einen Tipp geben könnte.
Wes
-
Hier nun die Lösung:
Es gibt keinen direkten Weg, um die Aufrufkonventionen unter C# für diesen Fall zu ändern. Aber es existieren trotzdem noch 2 Wege, die möglich sind.
Der 1. Weg ist eine nachträgliche Manipulation des IL-Codes der C#-Applikation, die die DLL verwenden will, und wird hier beschrieben:
http://www.codeproject.com/csharp/cdeclcallback.asp?df=100&forumid=248333&exp=0&select=1317814Allerdings ist das eine äußerst fragwürdige Methode, da sie während der Entwicklung einen erheblichen Mehraufwand bedeutet.
Die Alternative ist folgende:
Die Callback-Funktion wird in der C++-DLL als __stdcall deklariert.Dll-Header-Datei
#pragma once #ifdef DLLCSTEST_EXPORTS #define DLLCSTEST_API extern "C" __declspec(dllexport) #else #define DLLCSTEST_API __declspec(dllimport) #endif // Callback-Funktionstyp deklarieren typedef void __stdcall tCallback (int Wert1); // als StandardCall festlegen typedef tCallback* tpCallback; namespace MyNamespace { // exportierte Funktionen DLLCSTEST_API int fnDllCsTest(int a); DLLCSTEST_API void SetCallback(tpCallback Callback); }
Diese Deklaration bewirkt, dass die Übergabeparameter IMMER von der aufgerufenen Funktion selbst entfernt werden. C++-Aufrufe können das so berücksichtigen.
Zu beachten ist dann lediglich (für C++-Applikationen, die diese DLL verwenden wollen), dass auch Callback-Funktionen, die unter C++ angelegt und eingebnden werden, ebenfalls so zu deklarieren sind. Aber das ist kaum eine Mühe und bringt auch keine Nachteile.
So kann die Callback-Möglichkeit sowohl von C++- als auch von C#-Anwendungen ohne Änderung in der DLL genutzt werden.
Gruß,
Wes