Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Delphi 7, String PChar Problem

31 views
Skip to first unread message

Wolfgang

unread,
Apr 9, 2020, 3:45:24 AM4/9/20
to
Liebe Delphigemeinde!

Ich sehe scheinbar den Wald vor lauter Bäumen nicht mehr.
Ihr könnt mir da sicher weiterhelfen.

In einem kleinen (~1000 Zeilen) Testprogramm funktioniert das,
in einem großen Projekt stürzt das Programm so heftig, dass
nicht einmal der try except Block greift. Vermute mal dass
die StringList Rueck nicht genug Platz hat um die ~10KB
aus p_Rueck auszunehmen. Wie bekomme ich die Daten auf die
p_Rueck zeigt in die Stringlist kopiert.
Ich danke Euch!

Das ist das relevante Codeschnippsel:

private
...
BufferLen : Integer; // wird mit 40 * 1024 initialisiert
Counter : Integer;
p_Rueck : PChar;

...

Procedure Holen;
var ...
Rueck : TStrings;
...
begin
...
Counter := 0;
// Dient nur dazu wenn der Buffer zu klein ist mit einem
// groesseren Buffer weiter zu machen
repeat
inc(Counter);
p_Rueck := nil;
p_Rueck := StrAlloc((Counter * BufferLen) + 1);
// ShowMessage(IntToStr(StrBufSize(p_Rueck))); // Wert ok
// Antwort aus einer DLL
errno := GetAnswer(HDL,p_Rueck, StrBufSize(p_Rueck) );
if (errno = BUFFERTOSMALL) then
StrDispose(p_Rueck);
Sleep(500);
until (Counter > 3) or (errno <> BUFFERTOSMALL);

if errno = SUCCESS then
begin
Rueck := TStringList.Create;
//StrLCopy(PChar(Rueckm.Text),p_rm,StrLen(p_rm)-1); // strlen ok
//Rueck.Text := String(p_Rueck);
Rueck.Text := StrPas(p_Rueck); // Absturz
StrDispose(p_Rueck);
Rueck.Free;
end;

...
end;

mfg Wolfgang

Wolfgang

unread,
Apr 9, 2020, 3:52:54 AM4/9/20
to
Am 09.04.2020 um 09:45 schrieb Wolfgang:
> Liebe Delphigemeinde!
>
> Ich sehe scheinbar den Wald vor lauter Bäumen nicht mehr.
> Ihr könnt mir da sicher weiterhelfen.
>
> In einem kleinen (~1000 Zeilen) Testprogramm funktioniert das,
> in einem großen Projekt stürzt das Programm so heftig, dass
> nicht einmal der try except Block greift. Vermute mal dass
> die StringList Rueck nicht genug Platz hat um die ~10KB
> aus p_Rueck auszunehmen. Wie bekomme ich die Daten auf die
> p_Rueck zeigt in die Stringlist kopiert.
> Ich danke Euch!
>
> Das ist das relevante Codeschnippsel:
>
> private
>   ...
>   BufferLen : Integer;  // wird mit 40 * 1024 initialisiert
>   Counter   : Integer;
>   p_Rueck   : PChar;
>
> ....
Sorry habe vergessen zu erwähnen dass es sich um D7 auf
XP und Windows 7 x86 mit allen Updates handelt.

Christian Schmitt

unread,
Apr 10, 2020, 12:04:46 PM4/10/20
to
Am Donnerstag, 9. April 2020 09:45:24 UTC+2 schrieb Wolfgang:

> In einem kleinen (~1000 Zeilen) Testprogramm funktioniert das,
> in einem großen Projekt stürzt das Programm so heftig, dass
> nicht einmal der try except Block greift.

Bist du mit dem Debugger mal in die Zuweisungsfunktion von TStringList.Text hineingesprungen, um mal zu sehen bei welcher Anweisung genau der Crash passiert?


> Vermute mal dass
> die StringList Rueck nicht genug Platz hat um die ~10KB
> aus p_Rueck auszunehmen. Wie bekomme ich die Daten auf die
> p_Rueck zeigt in die Stringlist kopiert.

Ich habe nur Delphi XE, hier ist die TStringList intern als Array aufgebaut, daher - vorausgesetzt dein Arbeitsspeicher reicht :-) - sollte das kein Problem sein.

Mir kommt noch fix folgender Gedanke: D7 ist doch noch nicht Unicode basiert, bist du sicher, dass die DLL einen Zeiger auf einen PAnsiChar zurückliefert? Nicht dass du hier Unicode zurück bekommst, dann könnte die Größenberechnung für deinen PChar falsch sein...

Zweiter Ansatz: StrBufSize gibt die Zeichenanzahl inklusive des Nullterminators zurück. Sicher dass es hier kein Problem gibt? Nicht dass die DLL den Nullterminator überschreibt (geht das überhaupt?) und die .Text-Funktion dann quasi das Ende nicht findet...?

Was enthält dein PChar denn, hast du da mal reingeschaut?

Alles gerade nur so kurze Gedanken...

Happy Easter!

Gruß

Wolfgang

unread,
Apr 13, 2020, 4:08:22 AM4/13/20
to
Danke für deine Antwort!

>> Das ist das relevante Codeschnippsel:
>>
>> private
>> ...
>> BufferLen : Integer; // wird mit 40 * 1024 initialisiert
>> Counter : Integer;
>> p_Rueck : PChar;
>>
>>....
>>
function GetAnswer (h:hwnd;pRD:Pchar;nMaxRDLen:Integer):Integer;
stdcall; external 'ext.dll';
Am 10.04.2020 um 18:04 schrieb Christian Schmitt:
> Am Donnerstag, 9. April 2020 09:45:24 UTC+2 schrieb Wolfgang:
>
>> In einem kleinen (~1000 Zeilen) Testprogramm funktioniert das,
>> in einem großen Projekt stürzt das Programm so heftig, dass
>> nicht einmal der try except Block greift.
>
> Bist du mit dem Debugger mal in die Zuweisungsfunktion von TStringList.Text hineingesprungen, um mal zu sehen bei welcher Anweisung genau der Crash passiert?
>
>
>> Vermute mal dass
>> die StringList Rueck nicht genug Platz hat um die ~10KB
>> aus p_Rueck auszunehmen. Wie bekomme ich die Daten auf die
>> p_Rueck zeigt in die Stringlist kopiert.
>
> Ich habe nur Delphi XE, hier ist die TStringList intern als Array aufgebaut, daher - vorausgesetzt dein Arbeitsspeicher reicht :-) - sollte das kein Problem sein.
>
Sollte vom Speicher kein Problem sein. Maximum war mal 70KB. Das ist
aber genau so viel, dass ich mit diesem funktionierenden Konstrukt mit
array of char nicht mehr arbeiten kann.

function GetAnswer(h:hwnd;var RD:Array of
Char;nMaxRDLen:Integer):Integer; stdcall; external 'ext.dll';

Rueck : Array[0..60000] of Char; // Kann halt dann nicht 70KB

errno := GetAnswer(HDL,Rueck,length(Rueck));

Ist StrPas zum kopieren von einem PChar überhaupt die richtige Funktion?

> Mir kommt noch fix folgender Gedanke: D7 ist doch noch nicht Unicode basiert, bist du sicher, dass die DLL einen Zeiger auf einen PAnsiChar zurückliefert? Nicht dass du hier Unicode zurück bekommst, dann könnte die Größenberechnung für deinen PChar falsch sein...
>
Ist sicher kein Unicode String.
DLL C : int GetAnswer(HANDLE h, LPSTR pRD, int nMaxRDLen);

> Zweiter Ansatz: StrBufSize gibt die Zeichenanzahl inklusive des Nullterminators zurück. Sicher dass es hier kein Problem gibt? Nicht dass die DLL den Nullterminator überschreibt (geht das überhaupt?) und die .Text-Funktion dann quasi das Ende nicht findet...?
>
> Was enthält dein PChar denn, hast du da mal reingeschaut?

Die DLL sollte eigentlich einen Nullterminierten String zurückliefern.
Sieht vom Debugger aus soweit gut aus aber werde da nochmal genauer
wegen des #0 reinschauen.

> Alles gerade nur so kurze Gedanken...
>
> Happy Easter!
>
> Gruß
>
lg Wolfgang

Christian Schmitt

unread,
Apr 20, 2020, 4:09:36 AM4/20/20
to
Am Montag, 13. April 2020 10:08:22 UTC+2 schrieb Wolfgang:

> Sollte vom Speicher kein Problem sein. Maximum war mal 70KB. Das ist
> aber genau so viel, dass ich mit diesem funktionierenden Konstrukt mit
> array of char nicht mehr arbeiten kann.
>
> function GetAnswer(h:hwnd;var RD:Array of
> Char;nMaxRDLen:Integer):Integer; stdcall; external 'ext.dll';
>
> Rueck : Array[0..60000] of Char; // Kann halt dann nicht 70KB
>
> errno := GetAnswer(HDL,Rueck,length(Rueck));

Stehe ich hier auf dem Schlauch oder warum kannst du die 60000 nicht in 71680 (70kB) ändern?

> Ist StrPas zum kopieren von einem PChar überhaupt die richtige Funktion?

Ja, wobei ich die explizit eigentlich nie genutzt habe. Ich habe immer eine direkte Zuweisung gemacht, in deinem Fall wäre das dann

Rueck.Text := p_Rueck;

Hatte damit noch nie Probleme.

>> Bist du mit dem Debugger mal in die Zuweisungsfunktion von TStringList.Text hineingesprungen, um mal zu sehen bei welcher Anweisung genau der Crash passiert?

Haste mal geschaut?

Gruß


Wolfgang

unread,
Apr 20, 2020, 7:53:04 AM4/20/20
to
Wenn ich > 65535 (Int) komme dann kommt in einer Funktion einer Form wo
diese Variable verwendet wird: [Fehler] : Zuviele lokale Konstanten.
Verwenden Sie kürzere Prozeduren.

lg Wolfgang

Christian Schmitt

unread,
Apr 21, 2020, 3:21:00 AM4/21/20
to
Am Montag, 20. April 2020 13:53:04 UTC+2 schrieb Wolfgang:

> Wenn ich > 65535 (Int) komme dann kommt in einer Funktion einer Form wo
> diese Variable verwendet wird: [Fehler] : Zuviele lokale Konstanten.
> Verwenden Sie kürzere Prozeduren.

Ist bei D7 Int <> LongInt? Ansonsten geht ein Integer eigentlich bis 2^32/2, daher wundert mich das gerade.
Und mit einer Stringlist würde es funktionieren? Imho ist das auch nix anderes wie ein array.

Haste mal versucht das StrPas wegzulassen, also den Compiler selbsttätig die Umwandlung vornehmen zu lassen?

Rueck.Text := p_Rueck;

Gruß

Wolfgang

unread,
Apr 21, 2020, 8:40:14 AM4/21/20
to
Hi!

sysutils:

function StrPas(const Str: PChar): string;
begin
Result := Str;
end;

StrPas() macht genau das.

lg Wolfgang

Alfred Gemsa

unread,
Apr 21, 2020, 12:14:39 PM4/21/20
to
Am 09.04.2020 um 09:45 schrieb Wolfgang:

> In einem kleinen (~1000 Zeilen) Testprogramm funktioniert das,
> in einem großen Projekt stürzt das Programm so heftig, dass
> nicht einmal der try except Block greift. Vermute mal dass
> die StringList Rueck nicht genug Platz hat um die ~10KB
> aus p_Rueck auszunehmen. Wie bekomme ich die Daten auf die
> p_Rueck zeigt in die Stringlist kopiert.
> Ich danke Euch!

Ich habe mal was gebastelt:

Auf dem Formular ist eint Editfeld, wo die Länge eingetragen wird, ein
Memo, was dann die erzeugte Stringliste anzeigt und ein Button zum Start:

type
TForm1 = class(TForm)
Edit1: TEdit;
Memo1: TMemo;
Button2: TButton;
procedure FormCreate(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
sl: TStringList;
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
sl := TStringList.create;
end;

procedure TForm1.Button2Click(Sender: TObject);
var i, len: integer;
pc: pChar;
s, s1: string;
begin
len := strtoint(Edit1.Text);
//
// langen String zusammenbasteln
//
for i:=1 to len do
s := s + inttostr(i mod 10);
//
// Terminierende Null
//
s := s + #0;
//
// pChar erzeugen
//
pc := nil;
pc := StrAlloc(len + 1);
//
// String auf pChar übertragen, mit Terminator
//
move(s[1], pc^, len + 1);
//
// pChar auf anderen String kopieren (StrPas)
//
s1 := strpas(pc);
//
// Anderen String in Stringliste eintragen und anzeigen
//
sl.Clear;
sl.Text := s1;
Memo1.Lines.Assign(sl);
end;

end.

Der Unterschied zu deinem Code ist, dass ich mit StrPas zuerst einen
normalen String fülle (s1) und den dann in die Stringliste schreibe.

Ich habe es bis 100.000 getestet, strpas geht damit.

HTH, Alfred;

Alfred Gemsa

unread,
Apr 21, 2020, 12:15:21 PM4/21/20
to
Ich vergaß: Windows 10, Delphi 7.

Christian Schmitt

unread,
Apr 22, 2020, 1:55:31 AM4/22/20
to
Am Dienstag, 21. April 2020 18:14:39 UTC+2 schrieb Alfred Gemsa:

> Der Unterschied zu deinem Code ist, dass ich mit StrPas zuerst einen
> normalen String fülle (s1) und den dann in die Stringliste schreibe.

Und wenn du das nicht machst kracht es? Kannst du den Fehler von Wolfgang reproduzieren?

Gruß

Alfred Gemsa

unread,
Apr 22, 2020, 5:04:02 AM4/22/20
to
Am 22.04.2020 um 07:55 schrieb Christian Schmitt:

>> Der Unterschied zu deinem Code ist, dass ich mit StrPas zuerst einen
>> normalen String fülle (s1) und den dann in die Stringliste schreibe.
>
> Und wenn du das nicht machst kracht es? Kannst du den Fehler von Wolfgang reproduzieren?

//
// pChar erzeugen
//
pc := StrAlloc(len + 1);
move(s[1], pc^, len + 1);
//
// pChar-Inhalt direkt in Stringliste eintragen und anzeigen
//
sl.Text := strpas(pc);
Memo1.Lines.Assign(sl);
//
//
StrDispose(pc);

Geht auch.

Wolfgang nimmt ein TStrings:

var ...
Rueck : TStrings;
...

Ich hingegen trage das Zeug in eine Stringliste ein:

private
sl: TStringList;

Nehme ich TStrings, krachts auch bei mir. Dabei wird ein "AbstractError"
ausgelöst, was ich nicht verstehe, im Netz finde ich dazu folgendes:

> von TStrings wird nur abgeleitet. Die Funktion Count ist dort nur als
> abstract deklariert und muss in den abgeleiteten Klassen angegeben
> werden.
> TStringList ist von TStrings abgeleitet und stellt alle Methoden zur
> Verfügung. Nimm also nicht TStrings sondern TStringList (TStrings
kann > als Typ bei den Übergabeparametern verwendet werden).

Die Delphi-Hilfe sagt:

Beschreibung

> Von TStrings abgeleitete Objekte ermöglichen die Speicherung und
> Bearbeitung von String-Listen. TStrings enthalten abstrakte bzw. rein
> virtuelle (C++ Terminologie) Methoden und sollten nicht direkt
> instantiiert werden.

> Nachkommen von TStrings können entweder unterschiedliche
Zeichenketten > (wie z.B. die einzelnen Zeilen eines Listenfeldes) oder
eine einzige
> lange Texteinheit repräsentieren, deren Elemente dann separat
> bearbeitet werden können. Die Eigenschaften und Methoden von TStrings
> bieten zahlreiche Werkzeuge zur String-Listenbearbeitung an.

Alfred

Wolfgang

unread,
Apr 22, 2020, 9:37:02 AM4/22/20
to
Hallo!

Super danke das war die Lösung.
TStrings war auch bei mir der Schuldige. Nehme ich TStringList klappt
das ohne Absturz auch ohne den Umweg über die Zuweisung an einen String.

So sieht es jetzt aus:

private
...
BufferLen : Integer; // wird mit 40 * 1024 initialisiert
Counter : Integer;
p_Rueck : PChar;

....
function GetAnswer(h:hwnd;RD:PChar;nMaxRDLen:Integer):Integer; stdcall;
external 'ext.dll';
...
Procedure Holen;
var ...
Rueck : TStringList;
...
begin
...
Counter := 0;
// Dient nur dazu wenn der Buffer zu klein ist mit einem
// groesseren Buffer weiter zu machen
repeat
inc(Counter);
p_Rueck := nil;
p_Rueck := StrAlloc((Counter * BufferLen) + 1);
errno := GetAnswer(HDL,p_Rueck, Counter * BufferLen);
if (errno = BUFFERTOSMALL) then
StrDispose(p_Rueck);
Sleep(500);
until (Counter > 3) or (errno <> BUFFERTOSMALL);

if errno = SUCCESS then
begin
Rueck := TStringList.Create;
Rueck.Text := StrPas(p_Rueck);
StrDispose(p_Rueck);
Rueck.Free;
end;
...
end;

Danke für Eure Hilfe!

lg Wolfgang

Christian Schmitt

unread,
Apr 23, 2020, 6:08:03 AM4/23/20
to
Am Mittwoch, 22. April 2020 11:04:02 UTC+2 schrieb Alfred Gemsa:

> Wolfgang nimmt ein TStrings:
>
> var ...
> Rueck : TStrings;
> ...
>

Ohwei, das ist mir gar nicht aufgefallen. Hier ist ja auch ein Typ-Missmatch: TStrings:=TStringlist.create; Ich habe gerade nachgeschaut, in meinem Testprogramm hab ich instinktiv Rueck:TStringlist genommen. Hab aber jetzt gerade mal mit Fehler ausprobiert, also Delphi XE2 und Win 8.1 machen hier keinen Crash.

@Alfred: Das ärgert mich jetzt, dass ich den Fehler nicht vor dir gefunden habe :-))

Gruß

Alfred Gemsa

unread,
Apr 23, 2020, 6:30:19 AM4/23/20
to
Am 23.04.2020 um 12:08 schrieb Christian Schmitt:

> @Alfred: Das ärgert mich jetzt, dass ich den Fehler nicht vor dir gefunden habe :-))

Tja, man muss och jönne könne. ;-)

Ich habe mehr als dreißig Jahre beruflich mit Delphi rumgemacht und bin
inzwischen im Ruhestand.

Jetzt weiß ich endlich, was abstrakte Methoden sind, man lernt nie aus.

Alfred.

Wolfgang

unread,
Apr 23, 2020, 8:14:55 AM4/23/20
to
Am 23.04.2020 um 12:08 schrieb Christian Schmitt:
Mach dir keine Kopf. Mir ist es auch Jahre nicht aufgefallen und das war
des öfteren im Einsatz.;-( Der Compiler liefert nicht eimal eine
Warnung. Ich bin auf jeden Fall sehr froh dass das Problem jetzt behoben
ist. *g* Danke!

lg Wolfgang

Alfred Gemsa

unread,
Apr 23, 2020, 9:28:02 AM4/23/20
to
Am 23.04.2020 um 14:14 schrieb Wolfgang:

> Mach dir keine Kopf. Mir ist es auch Jahre nicht aufgefallen und das war
> des öfteren im Einsatz.;-( Der Compiler liefert nicht eimal eine
> Warnung. Ich bin auf jeden Fall sehr froh dass das Problem jetzt behoben
> ist. *g* Danke!

Das sehe ich allerdings, wenn ich TStrings nehme_

[Warnung] Unit1.pas(29): Instanz von 'TStrings' mit der abstrakten
Methode 'TStrings.Clear' wird angelegt
[Warnung] Unit1.pas(29): Instanz von 'TStrings' mit der abstrakten
Methode 'TStrings.Delete' wird angelegt
[Warnung] Unit1.pas(29): Instanz von 'TStrings' mit der abstrakten
Methode 'TStrings.Insert' wird angelegt

Alfred.

Christian Schmitt

unread,
Apr 23, 2020, 9:29:39 AM4/23/20
to
Am Donnerstag, 23. April 2020 12:30:19 UTC+2 schrieb Alfred Gemsa:

> Tja, man muss och jönne könne. ;-)

Ik jönns dir ja, wa? :-)


Alfred Gemsa

unread,
Apr 23, 2020, 9:40:33 AM4/23/20
to
Ah jetzt ja:

Gruß aus der Kölner Gegend an die Havel!

Jens Köhler

unread,
Apr 29, 2020, 11:23:41 AM4/29/20
to
Am 23.04.2020 um 15:28 schrieb Alfred Gemsa:
> Am 23.04.2020 um 14:14 schrieb Wolfgang:
>
> Das sehe ich allerdings, wenn ich TStrings nehme_
>
> [Warnung] Unit1.pas(29): Instanz von 'TStrings' mit der abstrakten
> Methode 'TStrings.Insert' wird angelegt
>
> Alfred.

Hallo,

wo ist da eigentlich das Problem?

es ist zwar
Rueck : TStrings deklariert aber mit
Rueck := TStringList.Create; erzeugt.

Da mache ich eignetlich immer so und bin der Meinung das auch in der
Doku und in Beispielen gesehen zu haben das die Basisklasse deklariert
und die Spazialität erzeugt wird.
Auch als Parameter ist TStrings öfters deklariert.

Jens
0 new messages