Strings versenden

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut

Strings versenden

Beitragvon siro » 17. Feb 2017, 13:24 Strings versenden

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
"C" verCehnfacht die Entwicklungszeit...
siro
 
Beiträge: 187
Registriert: 23. Aug 2016, 13:25
Wohnort: Berlin
OS, Lazarus, FPC: Windows 7 Windows 8.1 Windows 10 | 
CPU-Target: 64Bit
Nach oben

Beitragvon siro » 17. Feb 2017, 19:01 Re: Strings versenden

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 ?
Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.
Grüße von Siro
"C" verCehnfacht die Entwicklungszeit...
siro
 
Beiträge: 187
Registriert: 23. Aug 2016, 13:25
Wohnort: Berlin
OS, Lazarus, FPC: Windows 7 Windows 8.1 Windows 10 | 
CPU-Target: 64Bit
Nach oben

Beitragvon Mathias » 17. Feb 2017, 19:03 Re: Strings versenden

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 gün
Mit Java und C/C++ sehe ich rot
Mathias
 
Beiträge: 2877
Registriert: 2. Jan 2014, 17:21
Wohnort: Schweiz
OS, Lazarus, FPC: Linux (die neusten Trunc) | 
CPU-Target: 64Bit
Nach oben

Beitragvon siro » 17. Feb 2017, 19:15 Re: Strings versenden

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
"C" verCehnfacht die Entwicklungszeit...
siro
 
Beiträge: 187
Registriert: 23. Aug 2016, 13:25
Wohnort: Berlin
OS, Lazarus, FPC: Windows 7 Windows 8.1 Windows 10 | 
CPU-Target: 64Bit
Nach oben

Beitragvon Mathias » 17. Feb 2017, 19:17 Re: Strings versenden

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 gün
Mit Java und C/C++ sehe ich rot
Mathias
 
Beiträge: 2877
Registriert: 2. Jan 2014, 17:21
Wohnort: Schweiz
OS, Lazarus, FPC: Linux (die neusten Trunc) | 
CPU-Target: 64Bit
Nach oben

Beitragvon siro » 17. Feb 2017, 22:54 Re: Strings versenden

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
"C" verCehnfacht die Entwicklungszeit...
siro
 
Beiträge: 187
Registriert: 23. Aug 2016, 13:25
Wohnort: Berlin
OS, Lazarus, FPC: Windows 7 Windows 8.1 Windows 10 | 
CPU-Target: 64Bit
Nach oben

Beitragvon mse » 18. Feb 2017, 06:00 Re: Strings versenden

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.
mse
 
Beiträge: 1608
Registriert: 16. Okt 2008, 09:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.4.2,git master FPC 3.0,fixes_3_0) | 
CPU-Target: x86,x64,ARM
Nach oben

Beitragvon Mathias » 18. Feb 2017, 08:20 Re: Strings versenden

Vor dem schreiben aber pruefen, das der String groesser 0 ist.
Mit Lazarus sehe ich gün
Mit Java und C/C++ sehe ich rot
Mathias
 
Beiträge: 2877
Registriert: 2. Jan 2014, 17:21
Wohnort: Schweiz
OS, Lazarus, FPC: Linux (die neusten Trunc) | 
CPU-Target: 64Bit
Nach oben

Beitragvon mse » 18. Feb 2017, 08:26 Re: Strings versenden

Ich glaube nicht, dass das notwendig ist. Der Datenpointer ist dann zwar ungültig, da nNumberOfBytesToWrite 0 ist, schadet es nicht.
mse
 
Beiträge: 1608
Registriert: 16. Okt 2008, 09:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.4.2,git master FPC 3.0,fixes_3_0) | 
CPU-Target: x86,x64,ARM
Nach oben

Beitragvon siro » 18. Feb 2017, 08:36 Re: Strings versenden

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
"C" verCehnfacht die Entwicklungszeit...
siro
 
Beiträge: 187
Registriert: 23. Aug 2016, 13:25
Wohnort: Berlin
OS, Lazarus, FPC: Windows 7 Windows 8.1 Windows 10 | 
CPU-Target: 64Bit
Nach oben

Beitragvon Mathias » 18. Feb 2017, 20:48 Re: Strings versenden

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 gün
Mit Java und C/C++ sehe ich rot
Mathias
 
Beiträge: 2877
Registriert: 2. Jan 2014, 17:21
Wohnort: Schweiz
OS, Lazarus, FPC: Linux (die neusten Trunc) | 
CPU-Target: 64Bit
Nach oben

• Themenende •

Zurück zu Freepascal



Wer ist online?

Mitglieder in diesem Forum: Google [Bot] und 2 Gäste

porpoises-institution
accuracy-worried