Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Length zerstört AnsiString (https://www.delphipraxis.net/140296-length-zerstoert-ansistring.html)

OlafSt 15. Sep 2009 15:41


Length zerstört AnsiString
 
Folgende Klasse ist definiert:

Delphi-Quellcode:
type
  TFlexTransportSDS = class(TObject)
  strict private
     FTargetSSI: integer;
     FMessage: AnsiString; //Nix Unicode
     FHandle: integer;
     FIsStatus: Boolean;
     FStatus: Word;
  protected
  public
     //Die anderen Konstruktoren habe ich hier auskommentiert, sie spielen keine Rolle
     constructor Create(ATargetSSI: integer; const AMessage: AnsiString; AHandle: integer); //overload;
     //constructor Create(ATargetSSI: integer; AStatusValue: integer); overload;
     //constructor Create; overload;

     property TargetSSI: integer read FTargetSSI write FTargetSSI;
     property TheMessage: AnsiString read FMessage write FMessage;
     property SDSHandle: integer read FHandle write FHandle;
     property IsStatus: boolean read FIsStatus write FIsStatus;
     property StatusValue: Word read FStatus write FStatus;
  end;
Der Konstruktor sieht wie folgt aus:
Delphi-Quellcode:
constructor TFlexTransportSDS.Create(ATargetSSI: integer; const AMessage: AnsiString; AHandle: integer);
var
   i: integer;
begin
     FTargetSSI:=ATargetSSI;

     //Originalcode
     //FMessage:=AMessage;
     //funktioniert ebenfalls nicht

     for i := 1 to Length(AMessage) do
         FMessage:=FMessage+AMessage[i];
     FHandle:=AHandle;
end;
Nun erzeugen wir eine Instanz dieses neuen Objekts:

Delphi-Quellcode:
var
   TF: TFLexTransportSDS;
   s: AnsiString;
begin
     s:=#3#0'$PSCOCM,21';
     TF:=TFlexTransportSDS.Create(104, s, 114);
end;
Debuggen wir nun durch den Konstruktor hindurch, wird AMessage zerfetzt, es bleibt "#3?????" und eine Menge Nullbytes davon übrig.
Ich hab das ganze dann mit dem Disassember verfolgt und sehe, das Length nur noch mit Unicode-Strings arbeiten kann, ergo erzeugt der Compiler einen Aufruf nach @InternalLStrToUStr, um aus AMessage erstmal Unicode zu machen und dann die Länge zu ermitteln.
Offensichtlich ist @InternalLStrToUStr mit einem Problem behaftet: Das erste Zeichen (#3) wird noch erkannt, die folgende #0 aber nicht mehr und die Routine flippt aus ;) Mein Originalstring (AMessage !!) ist anschließend zerstört.

Kann das wer nachvollziehen ?
Gibt es eine Möglichkeit, Length zu umgehen (StrLen geht wegen des Nullbytes NICHT !) ?

Wenn dies ein tatsächlicher Fehler in Delphi ist, verschrotte ich D2009 - wer weiß, was noch für Granaten da schlummern.

Compiler ist Delphi 2009 Professional, Update 3 plus Update 4 (Database Pack Update) plus Help Update 3 sind installiert.

DeddyH 15. Sep 2009 15:46

Re: Length zerstört AnsiString
 
Das "Zerstören" kann ich unter Delphi 2007 wohl nicht nachvollziehen, aber auch ein AnsiString hört beim #0 auf. Wäre Shortstring eine Option?

Bernhard Geyer 15. Sep 2009 15:55

Re: Length zerstört AnsiString
 
Verwendung von #0 als Teil eines Strings ist eine ganz schlechte Idee. Unter C/C++ würdest du damit keine 3 Quellcodezeilen weit kommen. Falls es sich um ein Transportprotokoll handelt würde ich hier eher mit Streams arbeiten. Du kannst von Glück reden das dein Code bei alten Delphi-Versionen halbwegs funktioniert hat.

Namenloser 15. Sep 2009 16:06

Re: Length zerstört AnsiString
 
Ich hatte mal ein ähnliches Problem mit Sonderzeichen, war ziemlich schwer aufzuspüren, weil die Fehler ganz woanders (und nicht immer an der gleichen Stelle) auftraten. Hach, das war eine herrliche Debugging-Session... :love:

Probier mal statt #0 chr(0) zu schreiben (gilt für die anderen Sonderzeichen auch).

p80286 15. Sep 2009 16:15

Re: Length zerstört AnsiString
 
Mal so aus der Hüfte gedacht:
wie wäre es mit einem array of char?

das ist kompatibel zu "string" aber die interpretation des Inhalts (#0) fällt weg?

Gruß
K-H

himitsu 15. Sep 2009 16:30

Re: Length zerstört AnsiString
 
Der obere Code sollte funktionieren, da hier zufällig keine "Funktionen" verwendet werden, welche die #0 nicht mögen.

String-Konstante mit #0 ist möglich
Concat (String := String + String) ist möglich
'ne einfache Zuweisung (String := String) ist möglich
und Übergabe als Parameter geht auch

Der Delphi-String benutzt ein LängenenByte (Integer) und ließt nicht die länge anhand einer abschließenden #0 aus ... es gibt aber leider viele Funktionen, welche nach dem C-Standard als PChar arbeiten und auch die oft genutzen WinAPIs arbeiten so C-mäßig.

Also wenn man weiß, was man tut, kann man in einem String alle möglichen Steuerzeichen (auch #0) verwenden.
(mach ich sehr gern, da ich so das automatische Aufräumen mit ausnutzen kann :angel2: )


Das Problem ist hier vermutlich DELPHI und seine kranke Unicode-Unterstützung und wegen der #0 an 2. Stelle denkt es wäre ein UnicodeString.

Schalte das String-Checking ab und gut ist, außerdem wird dadurch das Programm etwas schneller/schlanker.

http://www.delphipraxis.net/internal...076095#1076095
http://www.delphipraxis.net/internal...075246#1075246


Delphi-Quellcode:
     FMessage: AnsiString; //Nix Unicode
das interessiert Delphi2009/2010 nicht, prüft es ständig nach und ändert es dann einfach

implementation 15. Sep 2009 18:00

Re: Length zerstört AnsiString
 
Zitat:

Zitat von himitsu
Der Delphi-String benutzt ein LängenenByte (Integer) und ließt nicht die länge anhand einer abschließenden #0 aus ... es gibt aber leider viele Funktionen, welche nach dem C-Standard als PChar arbeiten und auch die oft genutzen WinAPIs arbeiten so C-mäßig.

Das war mal so - bevor auch in Delphi die nullterminierten "langen Strings" eingeführt wurden.
Um das Phänomen zu umgehen, kannst du einen ShortString verwenden, der benutzt tatsächlich noch das erste Byte als Längenangabe.

himitsu 15. Sep 2009 18:06

Re: Length zerstört AnsiString
 
nicht ganz ... der AnsiString und UnicodeString (seit D2009) haben intern 2 Zusätzliche Integer

- Längenangabe
- Referenzzählung

und, damit man es schön einfach als PChar verwenden kann, eine #0 hintendran,
aber von der Stringverwaltung wird nur die Längenangabe verwendet.

Allerdings gibt es halt Delphifunktionen, welche c-typisch nur auf #0 reagieren,
oder wie z.B. StringReplace, welche zwar selber die Längenangabe auswertet, aber durch Verwendung von Delphi-Referenz durchsuchenAnsiPos dann doch nicht richtig arbeitet :wall:


Alle Zeitangaben in WEZ +1. Es ist jetzt 04:40 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz