Entwickler-Ecke
Dateizugriff - Array of byte aus dll an Hauptprogramm übergeben
rushifell - So 18.09.11 15:11
Titel: Array of byte aus dll an Hauptprogramm übergeben
Hallo,
ich versuche mich gerade an der Programmierung von dll's und möchte ein Array of Byte von der Dll an mein Hauptprogramm übergeben. Ich denke, ich muss vermutlich den Umweg über Pchar gehen. Das Array of Byte wird in der dll mit Daten gefüllt.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23:
| function setdata(var buf:Pchar):boolean;stdcall; tmpbuf:Array [0..919] of byte; begin ... For i:=0 to 919 do buf[i]:=chr(tmpbuf[i]); end;
Function Readdata(var buf:Array of Byte):Boolean; var tmpbuf:Pchar; begin ... GetMem(TmpBuf, (920 + 1) * SizeOf(Char)); try getdata(TmpBuf); Move(TmpBuf[0], Buf[0], 920); finally FreeMem(TmpBuf); end; ... end; |
Es funktioniert anscheinend. Ist das so in Ordnung, oder muss es anders gelöst werden?
Gruß
Blup - Mo 19.09.11 10:05
Das Prinzip ist richtig, Speicher wird im Hauptprogramm reserviert, der Speicherinhalt kann in der DLL verändert werden.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23:
| type TByteArray = packed array of Byte; const DATASIZE = 220; function getdata(const buf: TByteArray): Boolean; stdcall; var tmpbuf: Array [0..(DATASIZE - 1)] of byte; i: Integer; begin for i:= 0 to DATASIZE - 1 do buf[i]:= tmpbuf[i]; end; function Readdata(var buf: TByteArray):Boolean; begin SetLength(buf, DATASIZE); getdata(buf); end; |
rushifell - Mo 19.09.11 20:00
Danke für Deine Antwort. Es ist schwer, etwas über das Thema zu finden. Hab am Sonntag den halben Tag gesucht und heute 2-3 Stunden und nicht viel gefunden, nur ein Beispiel über Strings und dll's von Luckie und ein Einsteigertutorial beim Delphi-Treff. Vielleicht such ich ja falsch ;-)
Zu meinem Beispiel hätte ich eine Frage: Ich habe irgendwo im Netz gelesen, dass man in Zeile 15 bei GetMem die Länge mit Sizeof(Char) multiplizieren muss/sollte. Kannst Du mir dazu was sagen?
Zu Deinem Beispiel. Vielen Dank. Bietet ByteArray Vorteile gegenüber Pchar? Muss ich keinen Speicher anfordern? Meine Buffergröße ist konstant 920 Bytes. Das ist wohl kein Problem?
Was ich nicht verstehe ist, dass das ganze doch gar nicht so extrem kompliziert aussieht, ich aber trotzdem keine Beispiele dafür gefunden habe.
Blup - Di 20.09.11 11:03
rushifell hat folgendes geschrieben : |
Zu meinem Beispiel hätte ich eine Frage: Ich habe irgendwo im Netz gelesen, dass man in Zeile 15 bei GetMem die Länge mit Sizeof(Char) multiplizieren muss/sollte. Kannst Du mir dazu was sagen?
|
AnsiChar und AnsiString sind Typen die für jedes Zeichen genau ein Byte benötigen.
In älteren Delphiversionen gilt:
Char = AnsiChar;
String = AnsiString;
WideChar und WideString benötigen für jedes Zeichen genau zwei Byte.
Ab Delphi 2009 gilt:
Char = WideChar;
String = WideString;
Du solltest dir überlegen welcher Datentyp notwendig ist.
Sollen tatsächlich Zeichenketten übergeben werden, dann definiere den Parameter z.B. als PAnsiChar oder PWideChar.
rushifell hat folgendes geschrieben : |
Bietet ByteArray Vorteile gegenüber Pchar? Muss ich keinen Speicher anfordern? Meine Buffergröße ist konstant 920 Bytes. Das ist wohl kein Problem?
|
Speicher muss immer in irgendeiner Form angefordert werden.
Wie die Anforderung erfolgt, hängt vom Datentyp ab.
In meinem Beispiel über SetLength.
Wenn dein Parameter immer eine feste Größe hat, ist es vieleicht sinnvoll dafür einen eigenen Datentyp zu deklarieren.
Dies kann ein Array fester Größe oder ein Record sein oder ein bischen von beidem.
z.B.:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43:
| type PMyParam = ^TMyParam; TMyParam = record Count: Longint; Item: array[0..915] of AnsiChar; end; procedure getDaten1(MyParam: PMyParam); begin MyParam^.Item[0] := 'T'; MyParam^.Item[1] := 'e'; MyParam^.Item[2] := 'x'; MyParam^.Item[3] := 't'; MyParam^.Count := 4; end; procedure getDaten2(var MyParam: TMyParam); begin MyParam.Item[0] := 'T'; MyParam.Item[1] := 'e'; MyParam.Item[2] := 'x'; MyParam.Item[3] := 't'; MyParam.Count := 4; end; procedure holeDaten; var myParam1: TMyParam; myParam2: PMyParam; begin getDaten1(@myParam1); getDaten2(myParam1); New(myParam2); try getDaten1(myParam2); getDaten2(myParam2^); finally Dispose(myParam2); end; |
rushifell hat folgendes geschrieben : |
Was ich nicht verstehe ist, dass das ganze doch gar nicht so extrem kompliziert aussieht, ich aber trotzdem keine Beispiele dafür gefunden habe. |
Ist nicht kompliziert, erfordert aber Grundlagenwissen über Datentypen, Speicherverwaltung und Paramterübergabe.
rushifell - Di 20.09.11 22:50
Danke :beer:
Ich beschreibe mal, wofür ich das ganze brauche:
Ich programmiere ein Spiel, in dem die Level in einer Datei gespeichert sind. Ungepackt hat ein Level die Größe von genau 920 Bytes (deshalb die konstante Array-Größe). Es gibt die Möglichkeit, mit einem Editor eigene Levelsets zu erstellen und in einer Datei zu speichern. Im Spiel kann dann eines dieser Levelsets geladen werden. Nun möchte ich eine dll schreiben, in der Levelsets anderer Dateiformate (von Fremdanwendungen) in einen Memorystream geladen, konvertiert und in den Puffer geschrieben werden. Die einzelnen Level landen dann im Hauptprogramm, dem Spiel. Ursprünglich hatte ich die Unterstützung anderer Dateiformate in Units gesteckt. Eine dll bietet jedoch den Vorteil, dass beliebig viele zusätzliche Dateiformate eingebunden werden können und bei Bedarf recht einfach auch von anderen Leuten programmiert werden können. Die dll's werden einfach beim Programmstart aus dem Unterordner "plugins" eingelesen.
Ich habe den Code von Dir in meine dll eingebaut:
Die Variable Count brauche ich nicht, da die Puffergröße immer konstant ist.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33:
| type PMyParam = ^TMyParam; TMyParam = record item: array[0..919] of AnsiChar; end;
function setdata(var buffer :PMyParam):boolean;stdcall; tmpbuf:Array [0..919] of byte; Begin ... ... For i:=0 to 920-1 do buffer^.item[i]:=Chr(tmpbuf[i]); End;
Function Readdata(var buf:Array of Byte):Boolean; Var TmpBuf:PMyParam; Begin ... New(TmpBuf); try getdata(TmpBuf); For i:=0 to 919 do buf[i]:=Ord(TmpBuf^.item[i]); finally Dispose(TmpBuf); end; ... End; |
Eine Fragen hätte ich noch: Kann ich statt AnsiChar auch Byte als Typ für die Items benutzen, dann würde ich mir das Umwandeln von Byte in Char und zurück sparen. Ich brauche lediglich die Byte-Werte. Kann ich auch zusätzlich eine Variable vom Typ String in den Record packen?
Gruß
Blup - Mi 21.09.11 09:53
rushifell hat folgendes geschrieben : |
Kann ich statt AnsiChar auch Byte als Typ für die Items benutzen, dann würde ich mir das Umwandeln von Byte in Char und zurück sparen. |
Klar gehen Byte, die sind sowieso die Basis von allem.
rushifell hat folgendes geschrieben : |
Kann ich auch zusätzlich eine Variable vom Typ String in den Record packen? |
Strings sind problematisch, zum einen müsste man sich zwischen AnsiString oder WideString entscheiden.
Das Hauptproblem ist aber, String-Variablen sind Pointer. Diese verweisen auf einen weiteren Speicherbereich, wo ein Art Record mit der eigentliche Zeichenkette, die Länge der Zeichenkette, ein Referenzzähler und die Größe des dafür reservierten Speicherbereichs abgelegt ist. Wird dieser String in der DLL zugewiesen, wird der String vom Speichermanager der DLL verwaltet, das Programm selbst hat aber einen eigenen Speichermanager. Was dabei alles schief gehen kann, würde jetzt zu weit führen. Abgesehen davon könnte man so eine DLL nur schwer in anderen Programmiersprachen erstellen.
http://docwiki.embarcadero.com/RADStudio/XE2/de/String-Typen
Ich würde einen nullterminierte Zeichenkette fester Größe innerhalb des Records vereinbaren z.B.
Delphi-Quelltext
1: 2: 3: 4:
| TMyGameRecord = record Name: array[0..15] of AnsiChar; Playfield: array[0..39, 0..22] of Byte; end; |
rushifell - Mi 21.09.11 17:28
Super :D
So ist es viel einfacher. Ein Level besteht genau gesagt aus einem 40 Byte großen Levelheader und einer 880 Byte großen Map. Mit 0..39 liegst Du jedoch genau richtig ;-)
Blup hat folgendes geschrieben: |
Ich würde einen nullterminierte Zeichenkette fester Größe innerhalb des Records vereinbaren |
So werd ich's auch machen. :zustimm:
Vielen dank nochmal für alles, auch für den guten Link.
Gruß
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!