Strings versenden

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Antworten
siro
Beiträge: 730
Registriert: Di 23. Aug 2016, 14:25
OS, Lazarus, FPC: Windows 11
CPU-Target: 64Bit
Wohnort: Berlin

Strings versenden

Beitrag von siro »

Strings versenden

Einen schönen guten Tag, ich hab noch ein "kleines" Problemchen,
bevor ich ins Wochenende starte und da könnt Ihr mir vermutlich helfen:

Ich möchte über meine Serielle Schnittstelle Strings übertragen,
Aber die Strings sind auch nicht mehr das, was sie bei Turbo Pascal mal waren... :?

Es gibt 16 verschiedene Varianten: :shock:

Doch nun zum Problem:
In meiner Komponente habe ich folgendes:

Code: Alles auswählen

 
Procedure WriteString(s:string);
var written:cardinal;
bein
  WriteFile(fHandle,s,length(s),written,@Foverlapped);
end;
 


Die Windows API sieht ja so aus:

Code: Alles auswählen

BOOL WINAPI WriteFile(
  _In_        HANDLE       hFile,
  _In_        LPCVOID      lpBuffer,
  _In_        DWORD        nNumberOfBytesToWrite,
  _Out_opt_   LPDWORD      lpNumberOfBytesWritten,
  _Inout_opt_ LPOVERLAPPED lpOverlapped
);
 


die FPC Datei "redef.inc" sieht das so aus:

Code: Alles auswählen

function WriteFile(hFile: THandle; 
                   const Buffer;
                   nNumberOfBytesToWrite: DWORD;
                   var lpNumberOfBytesWritten: DWORD;
                   lpOverlapped: POverlapped
                  ): BOOL; external 'kernel32' name 'WriteFile';
 


Wenn ich jetzt folgenden Code habe:

var s:string;

s:='Hallo';
WriteString(s);


Bei einem ShortString würden jetzt "vermutlich" 5 Bytes gesendet werden.
Das Längenbyte und die ersten 4 Buchstaben "Hall"
Das letzte "o" würde also fehlen,
Weil im ersten Byte befindet sich ja die Längenangabe und dann folgen die Asciizeichen.
demnach müste ich schreiben:
WriteFile(fHandle,s[1],length(s),written,@Foverlapped);

Aber was, wenn es kein ShortString ist ?

Bei einem Nullterminierten String muss ich s[0] verwenden.
Stellt sich mir grad die Frage woher die Funktion length eigentlich weis, wie die Länge des Strings ermittelt wird.

Also erscheint mir meine SendeRoutine für Strings relativ unkontrolliert
und daher meine Frage: wie mache ich das am Sinnvollsten.


In der Unit Synaser sieht das so aus:

Code: Alles auswählen

procedure TBlockSerial.SendString(data: AnsiString);
begin
  SendBuffer(Pointer(Data), Length(Data));
end;
 


AnsiString ist 0 terminiert und hat einen RefCount und Length,
Laut http://wiki.freepascal.org/Character_an ... AnsiString
hat dann Refcount 4 Bytes und Length auch 4 Bytes, sofern ich das richtig interprtiert habe :oops:
was aber dem wiederspricht:
-- Ansistrings are strings that have no length limit --

Irgendwie verstehe ich die Stringverarbeitung nicht mehr. :roll:

Wird bei FPC Pointer(Data) automatisch der Zeiger 8 Bytes hinter den Speicher gesetzt
um Refcount und Length zu überbrücken und auf das erste Zeichen zu zeigen ?

Ich danke euch schonmal für jegliche Hinweise:

Siro

Nachtrag: ich könnte mit der Compiler Direktive {$H-} generell auf ShortString umstellen ?
Dann muss ich vermutlich meine Funktion wie folgt aufrufen:
WriteFile(fHandle,s[1],length(s),written,@Foverlapped);
Grüße von Siro
Bevor ich "C" ertragen muß, nehm ich lieber Lazarus...

siro
Beiträge: 730
Registriert: Di 23. Aug 2016, 14:25
OS, Lazarus, FPC: Windows 11
CPU-Target: 64Bit
Wohnort: Berlin

Re: Strings versenden

Beitrag von siro »

Ich habe eben mal etwas probiert
Und es hat sich bestätigt, das mit {$H-} meine Strings so im Speicher liegen wie damals mit Turbo Pascal.
Längenbyte und dann die Ascii Character.
string_1.jpg

Dann habe ich mit {$H+} compiliert.
Wie und wo ich da an meine Character komme habe ich leider noch nicht verstanden.
Hier der gleiche Speicheruszug.
string_2.jpg

Wo landen denn da meine Asccizeichen ?
Grüße von Siro
Bevor ich "C" ertragen muß, nehm ich lieber Lazarus...

Mathias
Beiträge: 6162
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Strings versenden

Beitrag von Mathias »

Ich habe mir die Unit synaser genauer angeguckt, und dort den Code von SendBuffer entschlüsselt, und somit müsste es so gehen.
Natürlich muss der FHandle noch initialisiert werden.
Was es mit den 2 Parametern Result und Overlapped auf sich hat, weis ich nicht, aber so wie es scheint, hast du diese schon gebraucht.
Getestet habe ich es nicht, aber es wird fehlerfrei kompiliert.

Code: Alles auswählen

var Fhandle: THandle;
 
procedure WriteString(s:String);
 
  procedure WriteBuffer(buffer: pointer; length: integer);
  begin
     WriteFile(FHandle, Buffer^, Length, DWord(Result), @Overlapped);
  end;
 
begin
  WriteBuffer(Pointer(s), Length(s));
end;   


Wen dieser Code mal läuft, kann er sicher noch optimiert werden, so das die procedure WriteBuffer entfällt. :wink:
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

siro
Beiträge: 730
Registriert: Di 23. Aug 2016, 14:25
OS, Lazarus, FPC: Windows 11
CPU-Target: 64Bit
Wohnort: Berlin

Re: Strings versenden

Beitrag von siro »

Danke Dir Mathias, ich werde das testen.

Meine serielle Komponente ist ja soweit fertig, ich wollte Ihr halt noch das String senden beibringen.
Das sollte möglichst unabhängig von dem verwendeten Stringtyp sein. Deshalb bin ich mir da noch nicht sicher.
FHandle Overlapped... ist alles schon "verpackt" und läuft.

Sobald ich Erfolge habe, melde ich mich zurück.
Grüße von Siro
Bevor ich "C" ertragen muß, nehm ich lieber Lazarus...

Mathias
Beiträge: 6162
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Strings versenden

Beitrag von Mathias »

ich wollte Ihr halt noch das String senden beibringen.

Denke daran, das der Gegenstelle auch die Länge des Stringes bekannt sein soll.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

siro
Beiträge: 730
Registriert: Di 23. Aug 2016, 14:25
OS, Lazarus, FPC: Windows 11
CPU-Target: 64Bit
Wohnort: Berlin

Re: Strings versenden

Beitrag von siro »

Das sieht "SEHR" gut aus Mathias.
Ich habe das etwas umwegig grad geprüft mit AnsiString und ShortString gemischt.
Das Ergebnis scheint immer richtig. Jedoch nur wenn ich auf {H+} compiliere.
Das ist aber völlig okay so.
Hab nochmals vielen Dank.

bei {H-} bekomme ich einen Type conversion error bei:
WriteBuffer(Pointer(s), Length(s)); ==> Illegal type conversion ShortString to Pointer


Siro
Grüße von Siro
Bevor ich "C" ertragen muß, nehm ich lieber Lazarus...

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: Strings versenden

Beitrag von mse »

Wenn ich jetzt folgenden Code habe:

var s:string;

s:='Hallo';
WriteString(s);


Bei einem ShortString würden jetzt "vermutlich" 5 Bytes gesendet werden.
Das Längenbyte und die ersten 4 Buchstaben "Hall"
Das letzte "o" würde also fehlen,
Weil im ersten Byte befindet sich ja die Längenangabe und dann folgen die Asciizeichen.
demnach müste ich schreiben:
WriteFile(fHandle,s[1],length(s),written,@Foverlapped);

Korrekt.
Aber was, wenn es kein ShortString ist ?

Bei einem Nullterminierten String muss ich s[0] verwenden.

Nein, das erste Zeichen ist immer Index 1.
Stellt sich mir grad die Frage woher die Funktion length eigentlich weis, wie die Länge des Strings ermittelt wird.

Ein langer String ist ein Pointer auf das erste Zeichen (= Adresse von s[1]) des strings. Davorgestellt ist eine Speicherblock mit diesem Aufbau (aus rtl/inc/astrings.inc):

Code: Alles auswählen

 
{
  This file contains the implementation of the AnsiString type,
  and all things that are needed for it.
  AnsiString is defined as a 'silent' pchar :
  a pchar that points to :
 
  @-16 : Code page indicator.
  @-12 : Character size (2 bytes)
  @-8  : SizeInt for reference count;
  @-4  : SizeInt for size;
  @    : String + Terminating #0;
  Pchar(Ansistring) is a valid typecast.
  So AS[i] is converted to the address @AS+i-1.
 
  Constants should be assigned a reference count of -1
  Meaning that they can't be disposed of.
}

 
Type
  PAnsiRec = ^TAnsiRec;
  TAnsiRec = Record
    CodePage    : TSystemCodePage;
    ElementSize : Word;
{$ifdef CPU64}   
    { align fields  }
   Dummy       : DWord;
{$endif CPU64}
    Ref         : SizeInt;
    Len         : SizeInt;
  end;
 

Die Offsets sind für 32 Bit, auf 64Bit Systemen ist SizeInt 8Byte breit. Ein leerer string ('') ist ein NIL-Pointer.
Also erscheint mir meine SendeRoutine für Strings relativ unkontrolliert
und daher meine Frage: wie mache ich das am Sinnvollsten.

Code: Alles auswählen

 
Procedure WriteString(s:string);
var written:cardinal;
bein
  WriteFile(fHandle,s[1],length(s),written,@Foverlapped);
end;
 

sollte immer funktionieren.

Mathias
Beiträge: 6162
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Strings versenden

Beitrag von Mathias »

Vor dem schreiben aber pruefen, das der String groesser 0 ist.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: Strings versenden

Beitrag von mse »

Ich glaube nicht, dass das notwendig ist. Der Datenpointer ist dann zwar ungültig, da nNumberOfBytesToWrite 0 ist, schadet es nicht.

siro
Beiträge: 730
Registriert: Di 23. Aug 2016, 14:25
OS, Lazarus, FPC: Windows 11
CPU-Target: 64Bit
Wohnort: Berlin

Re: Strings versenden

Beitrag von siro »

Guten Morgen,
so früh schon so gute Info:
Supi, ich Danke Dir "mse"

Die entscheidenden Bytes liegen also "davor" im Speicher und s[1] ist immer das erste Zeichen.
So macht das jetzt auch für mich einen Sinn. @-xx das (MINUS) löst mein Rätsel.
Das ist natürlich sehr gut gelöst und damit auch SEHR kompatibel.

Ihr seid Spitze, geniesst das Wochenende.

Siro
Grüße von Siro
Bevor ich "C" ertragen muß, nehm ich lieber Lazarus...

Mathias
Beiträge: 6162
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Strings versenden

Beitrag von Mathias »

mse hat geschrieben:Ich glaube nicht, dass das notwendig ist. Der Datenpointer ist dann zwar ungültig, da nNumberOfBytesToWrite 0 ist, schadet es nicht.

Wen man die Bereichsprüfung aktiviert, dann kommt es zu einem Fehler.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Antworten