Hallo zusammen,
ich melde mich auch mal wieder, diesmal mit einem für mich kniffligen Problem, für das ich nach zweitägiger Recherche immer noch keine Lösung gefunden habe.
Es geht darum, in einer C# (also .NET) Anwendung Funktionen in einer unmanaged DLL aufzurufen. Ziel ist es, die Klassen einer unmanaged C++-DLL in .NET verfügbar zu machen.
Hört sich erstmal nach einem Fall für P/Invoke an. Für mich als - wie ich dachte - alter Hase waren die ersten Schritte auch gar kein Problem. Auch, dass es um C++-DLLs ging, in denen auf Klassen und deren Methoden per P/Invoke zugegriffen werden sollte, war nicht wirklich problematisch. Wo ich jetzt seit zwei Tagen nicht weiterkomme, sind die Operatoren.
Vorweg: Der "Umweg" über ein C++/CLI Projekt kommt aus diversen Gründen nicht in Frage. Ohne das jetzt aufdröseln zu wollen, nur kurz zusammengefasst: Die unmanaged DLL liegt sowohl in 32 als auch in 64 Bit vor, gewünscht ist eine(!) Wrapper-DLL, die für "AnyCPU" kompiliert ist, also eine reine CLR-DLL sein soll.
Bisheriger Lösungsansatz ist dazu folgender:
Die gesamte Klasse, um die es geht, wurde mit
Ein kleines Beispiel einer solchen C++-Klasse:
Eine Hilfsklasse, der ich den Namen einer DLL und den Funktionsnamen übergebe, holt mit diesen Informationen per GetProcAddress einen Zeiger auf die Funktion, der in einem statischen Delegaten zwischengespeichert wird. Hiermit kann ich bequem unterscheiden, ob ein 32- oder ein 64-Prozess vorliegt und entsprechend unterschiedliche DLLs (32 oder 64 Bit) sowie Funktionsnamen (insbesondere bei Pointern als Funktionsparameter unterscheiden sich die Namensdekorationen) berücksichtigen. Zum Speichern der Delegaten verwende ich ebenfalls eine eigene (statische) Klasse:
Man beachte die Aufrufkonvention
Die eigentliche Managed-Klasse beginnt dann folgendermaßen:
Das funktioniert wunderbar.
Jetzt mein Problem: Ich finde keine Infos darüber und bekomme es nicht, den Delegaten für den im Beispiel vorhandenen +-Operator zu deklarieren und die Funktion in C# zu mappen.
Ja, ich weiß, was die Funktion in der unmanaged DLL macht und könnte sie in C# einfach "nachbauen". Es geht mir aber ums Prinzip an sich, weil ich es in diesem Zusammenhang mit mehreren zig Klassen und mehreren hundert Funktionen zu tun habe, die evtl. zu einem späteren Zeitpunkt modifiziert oder ggf. korrigiert werden und der CLR-Wrapper exakt dasselbe tun soll wie die unmanaged DLL ohne separat mit-angepasst werden zu müssen. Das evtl. nötige nachträgliche Löschen oder Einfügen von Funktionen muss hier das "Höchste der Gefühle" bleiben. Kurzum: Ein Nachbauen der Operatoren ist absolut unerwünscht und wenn möglich zu vermeiden.
tl;dr:
Wie muss der oben auskommentierte Delegat für den +-Operator aussehen und wie sieht sinnvollerweise der Aufruf gemäß dem Schema der Wrapper-Klasse aus?
ich melde mich auch mal wieder, diesmal mit einem für mich kniffligen Problem, für das ich nach zweitägiger Recherche immer noch keine Lösung gefunden habe.
Es geht darum, in einer C# (also .NET) Anwendung Funktionen in einer unmanaged DLL aufzurufen. Ziel ist es, die Klassen einer unmanaged C++-DLL in .NET verfügbar zu machen.
Hört sich erstmal nach einem Fall für P/Invoke an. Für mich als - wie ich dachte - alter Hase waren die ersten Schritte auch gar kein Problem. Auch, dass es um C++-DLLs ging, in denen auf Klassen und deren Methoden per P/Invoke zugegriffen werden sollte, war nicht wirklich problematisch. Wo ich jetzt seit zwei Tagen nicht weiterkomme, sind die Operatoren.
Vorweg: Der "Umweg" über ein C++/CLI Projekt kommt aus diversen Gründen nicht in Frage. Ohne das jetzt aufdröseln zu wollen, nur kurz zusammengefasst: Die unmanaged DLL liegt sowohl in 32 als auch in 64 Bit vor, gewünscht ist eine(!) Wrapper-DLL, die für "AnyCPU" kompiliert ist, also eine reine CLR-DLL sein soll.
Bisheriger Lösungsansatz ist dazu folgender:
Die gesamte Klasse, um die es geht, wurde mit
__declspec(dllexport)
ausgezeichnet. Somit werden alle Member-Funktionen exportiert. Mit dem "dekorierten" Namen zwar, aber das ist zweitrangig, eine DEF-Datei kommt hier nicht in Frage, auf die Gründe dafür geh ich auf Zeitgründen nicht ein. Selbst die Konstruktoren und der Destruktor sind extern verfügbar sowie auch - sic! - die zur Klasse gehörenden Operatoren.Ein kleines Beispiel einer solchen C++-Klasse:
C-Quellcode
Eine Hilfsklasse, der ich den Namen einer DLL und den Funktionsnamen übergebe, holt mit diesen Informationen per GetProcAddress einen Zeiger auf die Funktion, der in einem statischen Delegaten zwischengespeichert wird. Hiermit kann ich bequem unterscheiden, ob ein 32- oder ein 64-Prozess vorliegt und entsprechend unterschiedliche DLLs (32 oder 64 Bit) sowie Funktionsnamen (insbesondere bei Pointern als Funktionsparameter unterscheiden sich die Namensdekorationen) berücksichtigen. Zum Speichern der Delegaten verwende ich ebenfalls eine eigene (statische) Klasse:
C#-Quellcode
- [StructLayout(LayoutKind.Sequential, Pack = 8)] // Pack=8 ist korrekt, einfach ignorieren!
- internal unsafe struct __Punkt
- {
- public IntPtr* _vtable;
- public double x;
- public double y;
- }
- internal unsafe static class DLLCalls
- {
- [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
- internal delegate int _ctor_void(void* ths);
- [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
- internal delegate int _ctor_dbl_dbl(void* ths, double x, double y);
- [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
- internal delegate int _Destructor(void* ths);
- [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
- internal delegate void _void_ptr_dbl_dbl(void* ths, double d1, double d2);
- //[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
- //internal delegate void* _operator(void* a, void* b);
- //internal static _operator Punkt_OperatorPlus;
- internal static _ctor_void Punkt_Constructor_void;
- internal static _ctor_dbl_dbl Punkt_Constructor_dbl_dbl;
- internal static _Destructor Punkt_Destructor;
- internal static _void_ptr_dbl_dbl Punkt_Set;
- static DLLCalls()
- {
- if (!Environment.Is64BitProcess)
- {
- // 32-Bit
- }
- else
- {
- //Punkt_OperatorPlus = DLL.Call<_operator>("??HCPunkt@@QEBA?BV0@AEBV0@@Z");
- Punkt_Constructor_void = DLL.Call<_ctor_void>("??0CPunkt@@QEAA@XZ");
- Punkt_Constructor_dbl_dbl = DLL.Call<_ctor_dbl_dbl>("??0CPunkt@@QEAA@NN@Z");
- Punkt_Destructor = DLL.Call<_Destructor>("??1CPunkt@@UEAA@XZ");
- Punkt_Set = DLL.Call<_void_ptr_dbl_dbl>("?Set@CPunkt@@QEAAXNN@Z");
- }
- }
- }
Man beachte die Aufrufkonvention
ThisCall
für die Delegaten, die es ermöglicht, auf Objekt-Member zuzugreifen, in dem der Zeiger auf das Objekt als erster Parameter übergeben wird.Die eigentliche Managed-Klasse beginnt dann folgendermaßen:
C#-Quellcode
- public unsafe class MPunkt : IDisposable
- {
- private __Punkt* _ptr;
- private MPunkt(__Punkt* ptr)
- {
- _ptr = ptr;
- }
- public MPunkt()
- {
- _ptr = (__Punkt*)Memory.Alloc(sizeof(__Punkt));
- DLLCalls.Punkt_Constructor_void(_ptr);
- }
- public MPunkt(double x, double y)
- {
- _ptr = (__Punkt*)Memory.Alloc(sizeof(__Punkt));
- DLLCalls.Punkt_Constructor_dbl_dbl(_ptr, x, y);
- }
- public MPunkt(MPunkt other)
- {
- _ptr = (__Punkt*)Memory.Alloc(sizeof(__Punkt));
- Memory.Copy(other._ptr, _ptr, sizeof(__Punkt));
- }
- public double x
- {
- get { return _ptr->x; }
- set { _ptr->x = value; }
- }
- public double y
- {
- get { return _ptr->y; }
- set { _ptr->y = value; }
- }
- public void Set(double x, double y)
- {
- DLLCalls.TPunkt_Set(_ptr, x, y);
- }
- #region IDisposable Member
- ~MPunkt()
- {
- Dispose(false);
- }
- public void Dispose()
- {
- Dispose(true);
- }
- private void Dispose(bool bDisposing)
- {
- if (_ptr != null)
- {
- DLLCalls.Punkt_Destructor(_ptr);
- Memory.Free(_ptr);
- _ptr = null;
- }
- if (bDisposing)
- {
- GC.SuppressFinalize(this);
- }
- }
- #endregion
- }
Das funktioniert wunderbar.
Jetzt mein Problem: Ich finde keine Infos darüber und bekomme es nicht, den Delegaten für den im Beispiel vorhandenen +-Operator zu deklarieren und die Funktion in C# zu mappen.
Ja, ich weiß, was die Funktion in der unmanaged DLL macht und könnte sie in C# einfach "nachbauen". Es geht mir aber ums Prinzip an sich, weil ich es in diesem Zusammenhang mit mehreren zig Klassen und mehreren hundert Funktionen zu tun habe, die evtl. zu einem späteren Zeitpunkt modifiziert oder ggf. korrigiert werden und der CLR-Wrapper exakt dasselbe tun soll wie die unmanaged DLL ohne separat mit-angepasst werden zu müssen. Das evtl. nötige nachträgliche Löschen oder Einfügen von Funktionen muss hier das "Höchste der Gefühle" bleiben. Kurzum: Ein Nachbauen der Operatoren ist absolut unerwünscht und wenn möglich zu vermeiden.
tl;dr:
Wie muss der oben auskommentierte Delegat für den +-Operator aussehen und wie sieht sinnvollerweise der Aufruf gemäß dem Schema der Wrapper-Klasse aus?
Weltherrschaft erlangen: 1%
Ist dein Problem erledigt? -> Dann markiere das Thema bitte entsprechend.
Waren Beiträge dieser Diskussion dabei hilfreich? -> Dann klick dort jeweils auf den Hilfreich-Button.
Danke.
Waren Beiträge dieser Diskussion dabei hilfreich? -> Dann klick dort jeweils auf den Hilfreich-Button.
Danke.