Über P/Invoke eine C Funktion aus einer DLL aufrufen
-
Hallo zusammen,
Ich habe den Titel bewusst etwas allgemeiner gehalten, da ich wahrscheinlich mehrere Fragen hier noch stellen werde. Aktuell probiere ich gerade ein .Net Wrapper für eine C DLL zu erstellen. Mit dem ganzen P/Invoke Zeug habe ich aber durchaus so meine Probleme.
Derzeit habe ich 2 Fragen, später werden es sicher noch mehr. Die erste ist mehr eine Prüffrage, ob ich es auch richtig verstanden habe.
Zuerst aber zu den C Funktionen:
int xxx_create(/*const*/ char* filename, xxx** obj); int xxx_destroy(xxx* obj);
Und nun zu den Fragen:
1. Diese Funktionen habe ich nun so eingebunden (scheint auch richtig zu funktionieren):[DllImport(@"C:\dev\test\xxx.dll", EntryPoint = "xxx_create")] static extern int xxxCreate(string filename, out IntPtr obj); [DllImport(@"C:\dev\test\xxx.dll", EntryPoint = "xxx_destroy")] static extern int xxxDestroy(IntPtr obj); // Aufruf: IntPtr obj = IntPtr.Zero; xxxCreate("test.xxx", out obj); // ... xxxDestroy(obj);
Ist das so richtig?
2. Der zu übergebene String
filename
MUSS in UTF-8 kodiert sein. Wie kann ich das erreichen?Grüssli
-
Zu 2. könnte dies eine Hilfe sein: http://msdn.microsoft.com/de-de/library/system.text(VS.80).aspx
Edit
Ausserdem könnte der P/Invoke Interop Assistent eine Hife sein:
http://www.codeplex.com/clrinterop
-
theta schrieb:
Zu 2. könnte dies eine Hilfe sein: http://msdn.microsoft.com/de-de/library/system.text(VS.80).aspx
Ja, daran hatte ich auch schon gedacht, nur sind mir zwei Dinge nicht so ganz klar:
1. Kann man die Übergabe nicht irgendwie automatisieren. Also das immer automatisch der String in UTF-8 kodiert und übergeben wird?
2. Wenn ich überEncoding.UTF8.GetBytes(...)
gehe, wie übergebe ich das sinnvoll an die Funktion?theta schrieb:
Edit
Ausserdem könnte der P/Invoke Interop Assistent eine Hife sein:
http://www.codeplex.com/clrinteropEine grosse Hilfe, danke!
Dank diesem Programm und nochmals einigem lesen von Dokumentationen, stehe ich aktuell mit dieser Lösung da, welche zu funktionieren scheint, aber ich hätte da trotzdem noch eine Expertenmeinung dazu gehört
C Funktionen wie vorhin, C# sieht so aus:
[DllImport(@"C:\dev\test\xxx.dll", EntryPoint = "xxx_create")] private static extern int xxxCreate([MarshalAs(UnmanagedType.LPArray)] byte[] filename, out IntPtr obj); [DllImport(@"C:\dev\test\xxx.dll", EntryPoint = "xxx_destroy")] private static extern int xxxDestroy(IntPtr obj); // Auführung: IntPtr obj = IntPtr.Zero; byte[] bytes = Encoding.UTF8.GetBytes(@"C:\dev\test\test.xxx"); xxxCreate(bytes, out obj); // ... xxxDestroy(obj);
Grüssli
-
Der Thread ist ziemlich weit in der Versenkung verschwunden, aber ich hole ihn mal wieder hervor. Bin leider länger nicht mehr dazu gekommen, an dem Zeug weiterzuarbeiten. Inzwischen hat sich aber wieder eine neue Frage ergeben. Zuerst mal ein wenig kurzer dummy code:
int count; char const* saveArray; void saveArray(char const* array, int cnt) { count = cnt; saveArray = array; } void doSomethingWithArray() { FILE* file = fopen("test.dat", "w"); fwrite(saveArray, 1, cnt, file); fclose(file); }
[DllImport("test.dll", EntryPoint = "saveArray")] public static extern void SaveArray([MarshalAs(UnmanagedType.LPArray)] byte[] arr, int cnt); [DllImport("test.dll", EntryPoint = "doSomethingWithArray")] public static extern void DoSomethingWithArray(); // ... byte[] arr = Encoding.UTF8.GetBytes("Hello World!"); SaveArray(arr, arr.Length); // ... DoSomethingWithArray();
Geht das? Ich habe nämlich irgendwie das Gefühl, dass dies nicht geht, wegen verschiedenen Hinweisen in der MSDN. Aber so ganz sicher bin ich nicht, weil ich es noch nicht ganz verstanden habe, was genau mit den Daten passiert, wenn sie vom managed code in den unmanaged code gelangen.
Müsste ich da allenfalls über
Marshal.AllocHGlobal(...)
gehen?Grüssli
-
Ich bin nicht sicher, aber...
Ich würde eher davon ausgehen, dass die gemarshallten Daten nur im Funktionsaufruf gültig sind. D.h also also ein Kopie der Daten anlegen.Simon
-
Hmmm, naja, die DLL ist halt nicht von mir und ich möchte nicht unbedingt in dem Code rumpfuschen gehen und die DLL dann neu bauen. An den meisten Orten kann ich angeben, dass die Kopie gemacht werden soll, ist halt nur etwas unperformanter, wobei ich allerdings noch nicht überprüft habe, ob das überhaupt ins Gewicht fallen würde. Werden wahrscheinlich sowieso stellen sein, wo gespeichert oder geladen wird, also wo der User allenfalls ruhig etwas warten darf
Und an den Stellen wo man halt nicht kopieren kann, würde ich es dann so lösen, wenn ich es muss:
public class GlobalBuffer : IDisposable { public int Size { get; private set; } public IntPtr Handle { get; private set; } public GlobalBuffer(int size) { this.Size = size; this.Handle = Marshal.AllocHGlobal(size); } private ~GlobalBuffer() { Dispose(); } public void Dispose() { this.Size = 0; Marshal.FreeHGlobal(this.Handle); this.Handle = IntPtr.Zero; } }; // und dann halt: byte[] utf8 = Encoding.UTF8.GetBytes("Hello World!"); using(GlobalBuffer buffer = new GlobalBuffer(utf8.Length)) { Marshal.Copy(utf8, 0, buffer.Handle, utf8.Length); SaveArray(buffer.Handle, buffer.Size); // ... DoSomethingWithArray(); }
Grüssli