Autor Beitrag
pyro123
Hält's aus hier
Beiträge: 4



BeitragVerfasst: Di 11.09.07 15:05 
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:

ausblenden 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:
ausblenden 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.
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 20451
Erhaltene Danke: 2264

Win 10
C# (VS 2019)
BeitragVerfasst: 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

_________________
Zwei Worte werden Dir im Leben viele Türen öffnen - "ziehen" und "drücken".
pyro123 Threadstarter
Hält's aus hier
Beiträge: 4



BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3803
Erhaltene Danke: 176

Arch Linux
Python, C, C++ (vim)
BeitragVerfasst: Di 11.09.07 21:14 
user profile iconpyro123 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 Threadstarter
Hält's aus hier
Beiträge: 4



BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 1097
Erhaltene Danke: 2



BeitragVerfasst: 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 Threadstarter
Hält's aus hier
Beiträge: 4



BeitragVerfasst: Mi 12.09.07 21:07 
Hm, das versteh ich nich so ganz..

Muss ich jetzt Einfach so machen:

ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3803
Erhaltene Danke: 176

Arch Linux
Python, C, C++ (vim)
BeitragVerfasst: Mi 12.09.07 23:00 
user profile iconpyro123 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 ;) ?
user profile iconChryzler 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 416


Delphi32 (D2005 PE); Chrome/C# (VS2003 E/A, VS2005)
BeitragVerfasst: Do 13.09.07 07:11 
Probier's mal so:
ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2931

XP Prof, Vista Business
D6, D2k5-D2k7 je Prof
BeitragVerfasst: Mi 26.09.07 16:27 
user profile iconKhabarakh 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.

_________________
gringo pussy cats - eef i see you i will pull your tail out by eets roots!