Entwickler-Ecke
C# - Die Sprache - Problem mit "packed record" bei Delphi-DLL-Import
pyro123 - Di 11.09.07 15:05
Titel: Problem mit "packed record" bei Delphi-DLL-Import
Hallo,
ich hab eine Delphi-DLL, die ich gerne einbinden möchte. Das Einbinden funktioniert auch. Nur bei einer Funktion nicht. Die gibt als Rückgabewert eben jenes "packed record" zurück:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8:
| type TUploadProgress = packed record BytesTransferred : Integer; ElapsedTime : Integer; EstimatedTimeLeft : Integer; PercentsDone : Integer; TransferRate : Single; end; |
In C# habe ich versucht das so zu lösen:
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
| public struct UploadProgress { public int BytesTransferred; public int ElapsedTime; public int EstimatedTimeLeft; public int PercentsDone; public double TransferRate; } [DllImport("upload.dll", EntryPoint = "ReadProgress")] public static extern UploadProgress ReadProgress();
public static void Test() { UploadProgress prog = ReadProgress(); } |
Beim Testen gibt es aber eine "AccessViolationException". Ich habe leider keine Ahnung an was das liegt..
Weiß jemand, wie man das beheben kann?
Danke schon mal, gruß pyro123
Christian S. - Di 11.09.07 15:48
Hallo!
Zuerstmal stimmen die Typen bei TransferRate nicht überein (Delphi: Single <> C#: Double).
Außerdem kannst Du mal schauen, ob Du beim StructLayout was machen musst. Dazu in der Hilfe mal unter "StructLayoutAttribute" und "LayoutKind" schauen und mit den Einstellungen rumprobieren.
Grüße
Christian
pyro123 - Di 11.09.07 17:56
Hallo,
ich habe LayoutKind mal auf Auto gesetzt.
Nun kommt folgende Fehlermeldung:
Zitat: |
Die Typensignatur der Methode ist nicht PInvoke-kompatibel. |
Google und MSDN meinte irgendwas von Marshaling oder sowas. Was ist das und wie hilft es mir, das Problem zu beheben?
Kha - Di 11.09.07 21:14
pyro123 hat folgendes geschrieben: |
ich habe LayoutKind mal auf Auto gesetzt. |
Autsch! Diese Aussage lässt mich daran zweifeln, ob du das SDK überhaupt nur berührt hast.
Sei's drum, hier die Vermutung eines absoluten P/Invoke-Laien: Habe noch nie einen komplexen Typen als Rückgabewert einer DllImport-Funktion gesehen, versuche es also lieber einmal auf der Delphi-Seite mit einem out-Parameter (btw, calling-Convention stimmt?).
pyro123 - Di 11.09.07 21:22
Tut mir leid, aber ich hab echt nicht kapiert, was die MSDN mir vermitteln wollte :\
Auf den Delphi-Source habe ich leider keinen Zugriff.
Chryzler - Mi 12.09.07 19:59
packed record ist ja praktisch auch nur ein record (hab den Unterschied nie kapiert..). 4 mal Integer (4-Byte) und 1 mal Single (4-Byte) sind 20 Byte. D.h. du könntest UploadProgress als byte[20] deklarieren. Dann per BitConverter.ToInt32 und BitConverter.ToSingle wieder die einzelnen Integer- und Single-Wert auslesen. So könnte es gehen. Denn irgendwie ist ja klar, dass es nicht geht: ein Record mit 5 Feldern ist ja nicht gleich eine Struktur, die 5 Felder hat. Oder?
pyro123 - Mi 12.09.07 21:07
Hm, das versteh ich nich so ganz..
Muss ich jetzt Einfach so machen:
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8:
| [DllImport("upload.dll", EntryPoint = "ReadProgress")] public static extern byte[] ReadProgress();
public static void Test() { byte[] test = new byte[20]; test = ReadProgress(); } |
Das funktioniert nicht :(
Kha - Mi 12.09.07 23:00
pyro123 hat folgendes geschrieben: |
Tut mir leid, aber ich hab echt nicht kapiert, was die MSDN mir vermitteln wollte :\ |
Sind die zwei letzten Sätze bei LayoutKind.Auto wirklich
so schwer zu verstehen ;) ?
Chryzler hat folgendes geschrieben: |
(hab den Unterschied nie kapiert..) |
Na dann wäre es spätestens bei diesem Thema höchste Zeit... Lasse dir einmal Sizeof eines unpacked und eines packed Records, der ein Byte und danach ein beliebiges anderes Feld besitzt, ausgeben. Zum Glück sind hier wohl alle Felder so oder so aligned (bei Delphi muss man ja nicht lange herumraten, ob es ein Compiler für x86 oder x64 war ;) ), also macht es in diesem Fall wirklich einmal keinen Unterschied.
Zitat: |
D.h. du könntest UploadProgress als byte[20] deklarieren. |
Ein Record ist ein Wertetyp, ein Array ein Referenztyp - sicher, dass
das irgendwie funktionieren kann :gruebel: ?
Zitat: |
ein Record mit 5 Feldern ist ja nicht gleich eine Struktur, die 5 Felder hat. |
Gegenfrage: Weshalb nicht :D ?
Nachdem ich kurz von einer BadImageFormatException aufgehalten wurde (habe ich schon erwähnt, dass Delphi beim Thema x64 noch nicht so ganz weit fortgeschritten ist :mrgreen: ? ), hat sich meine Vermutung bestätigt: Mit einem out-Parameter funktioniert alles problemlos, mit einem Rückgabewert werden die Werte - öhm... höchstinteressant (allerdings keine Exception). Wenn niemand die Lösung oder einen Workaround kennt, solltest du vielleicht einmal versuchen, eine neue Delphi-Dll zu erstellen, die die Funktion in eine out-Prozedur wrappt.
Robert_G - Do 13.09.07 07:11
Probier's mal so:
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9:
| [StructLayout(LayoutKind.Sequencial)] public struct UploadProgress { public int BytesTransferred; public int ElapsedTime; public int EstimatedTimeLeft; public int PercentsDone; public single TransferRate; } |
Ein packed record ist einer, bei dem die Felder nicht für Optimierte Zugriffe ausgerichtet sondern direkt, lückenlos, hintereinander "gepackt" werden.
Motzi - Mi 26.09.07 16:27
Khabarakh hat folgendes geschrieben: |
[...]hat sich meine Vermutung bestätigt: Mit einem out-Parameter funktioniert alles problemlos, mit einem Rückgabewert werden die Werte - öhm... höchstinteressant (allerdings keine Exception). Wenn niemand die Lösung oder einen Workaround kennt, solltest du vielleicht einmal versuchen, eine neue Delphi-Dll zu erstellen, die die Funktion in eine out-Prozedur wrappt. |
Diese Erfahrung hab ich auch schon gemacht. Wenn der Rückgabewert einer Funktion von einem bestimmten Typ ist, dann baut Delphi den Stack falsch auf. Genau genommen tritt das Problem bei mehreren Typen auf - ich konnte es bisher bei Interfaces und WideStrings beobachten, bei Integer oder Pointer hingegen funktioniert alles.
Ich hab lange nach der Ursache gesucht und hab sie schließlich beim Debuggen des ASM-Codes gefunden. Wenn der Rückgabewert vom Typ Integer oder Pointer ist, so wird das Ergebnis
hinter den anderen Parametern am Stack abgelegt - ganz genauso wie wenn man statt einer Funktion eine Prozedur mit out-Parameter verwendet. Bei anderen Typen wie Interfaces und WideStrings hingegen, wird das Ergebnis
vor den anderen Parametern am Stack abgelegt.
Ich hab keine Ahnung warum der Delphi Compiler da so einen Murks zusammendreht, ich halte es jedenfalls für einen Compilerfehler (außer es kann mir jemand einen trifftigen Grund für dieses Verhalten nennen). Das Problem lässt sich jedoch leicht umgehen, indem man out-Parameter verwendet.
Gruß, Motzi
PS: das Problem besteht übrigens nicht nur bei Funktionen die über DllImport importiert werden, sondern auch bei COM-Interfaces die keine safecall-Konvention verwenden. Bei der safecall-Konvention verschwindet das Problem, weil ja ohnehin ein HRESULT als Funktionswert zurückgegeben wird und das eigentliche Ergebnis intern als out-Parameter behandelt wird.
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!