Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   Delphi PChar schneidet letztes Zeichen ab (https://www.delphipraxis.net/78531-pchar-schneidet-letztes-zeichen-ab.html)

TheMiller 6. Okt 2006 15:06


PChar schneidet letztes Zeichen ab
 
Hallo,

da ich Werte mit Hilfe einer DLL Werte aus der Registry auslese, verwende ich zur Stringübertragung PChar. Doch ich habe einen Wert in der Registry, bei dem - und nur bei dem - schneidet er mit das letzte Zeichen ab. Verändere ich den Wert in der Registry um einen Buchstaben, so wird der zuvor abgeschnittene UND der neue mit angezeigt.

Was ist den da bitte jetzt wieder los?

Go2EITS 6. Okt 2006 15:26

Re: PChar schneited letztes Zeichen ab
 
Bin zwar nicht so fit mit der Registry, aber die Routinen zum Lesen eines Eintrages und zum Setzen müssten funktionieren. "Zeig mal Code", wie es schön heißt..

3_of_8 6. Okt 2006 15:28

Re: PChar schneidet letztes Zeichen ab
 
Hat vielleicht irgendwas damit zu tun, dass ein PChar von einem #0 terminiert wird.

DGL-luke 6. Okt 2006 15:39

Re: PChar schneidet letztes Zeichen ab
 
Dann sag uns doch mal, wie der wert aussieht...

TheMiller 6. Okt 2006 15:45

Re: PChar schneidet letztes Zeichen ab
 
Der Wert lautet ganz einfach:

InstallDir - C:\Programme\MyProg\Test

und das "t" wird abgeschnitten. Hänge ich ein "s" dran (Tests) dann wird "C:\Programme\MyProg\Tests" ausgegeben! Was ist da los? *g*

TheMiller 6. Okt 2006 15:45

Re: PChar schneidet letztes Zeichen ab
 
Was bedeuted ein #0 ?

mkinzler 6. Okt 2006 15:48

Re: PChar schneidet letztes Zeichen ab
 
Zitat:

Was bedeuted ein #0 ?
ASCII 0 markiert das Ende eines Strings (EOS) in c/c++

DGL-luke 6. Okt 2006 15:49

Re: PChar schneidet letztes Zeichen ab
 
Warum Doppelpost?

Die Windows-API arbeitet mit nullterminierten Strings. Das heißt, es wird ein Pointer auf einen Speicherbereich übergeben. Das Ende des Strings wird von einem #0 gekennzeichnet.

Und wenn du uns jetzt noch kurzdeien Code zeigst...

TheMiller 6. Okt 2006 16:05

Re: PChar schneidet letztes Zeichen ab
 
Delphi-Quellcode:
// DLL
function GetInstallDir: PChar; stdcall;
var
  Reg:TRegistry;
begin
  Reg:=TRegistry.Create;
  Reg.RootKey:=HKEY_CURRENT_USER;
  Reg.OpenKey('Software\MyProg\Test\', false);
  result:=PChar(Reg.ReadString('InstallDir'));
end;


//Programm
function GetInstallDir: PChar; stdcall; external SysCtrl;

ShowMessage(PChar(GetInstallDir));

Luckie 6. Okt 2006 16:06

Re: PChar schneidet letztes Zeichen ab
 
Zitat:

Zitat von Raymond Chen
Even though a value is stored in the registry as REG_SZ, this doesn't mean that the value actually ends with a proper null terminator.

Schreibst du den Wert eventuell nicht richtig rein?

Roter Kasten:
Deine Methode mit den PChar kommt mir etwas komisch vor. Hier mal ein Auszug aus einer DLL von mir, die auch mit PChar arbeitet.
Delphi-Quellcode:
function ReturnString(InStr: PChar; Buffer: PChar; lenBuffer: Integer): Integer; stdcall
var
  cls              : JClass;
  mid              : JMethodID;
  res              : JString;
  instance         : JObject;
  s: String;
begin
  cls := jvm.JniEnv.FindClass('HelloWorld');
  Assert(Assigned(cls), 'Class HelloWorld not found');

  mid := jvm.JniEnv.GetMethodID(cls, '<init>', '()V');
  Assert(Assigned(mid), 'Constructor not found');

  instance := jvm.JniEnv.NewObject(cls, mid, []);
  // Signatur: String Parameter, Rückgabetyp String
  mid := jvm.JniEnv.GetMethodID(cls, 'strTest2', '(Ljava/lang/String;)Ljava/lang/String;');
  Assert(Assigned(mid), 'Method "strTest2" not found');

  res := jvm.JniEnv.CallObjectMethod(instance, mid, [String(InStr)]);

  s := jvm.JniEnv.JStringToString(res);

  if length(s) < lenBuffer then
  begin
    result := Length(s);
  end
  else
  begin
    lstrcpy(Buffer, PChar(s));
    result := Length(s);
  end;
end;
Was sie letztendlich macht ist egal. Wichtig ist nur der letzte Teil.

Aufruf aus dem Programm dann:
Delphi-Quellcode:
procedure TForm1.btnRetStrClick(Sender: TObject);
type
  TReturnStr = function(InStr: PChar; Buffer: PChar; lenBuffer: Integer): Integer; stdcall;
var
  ReturnStr        : TReturnStr;
  Buffer           : PChar;
  len              : Integer;
begin
  Buffer := nil;
  @ReturnStr := GetProcAddress(hLib, 'ReturnString');
  if Assigned(ReturnStr) then
  begin
    len := ReturnStr('Hello ', nil, 0);
    try
      GetMem(Buffer, len + 1);
      ReturnStr('Hello ', Buffer, len);
      StB.SimpleText := string(Buffer);
    finally
      FreeMem(Buffer, len + 1);
    end;
  end
  else
    StB.SimpleText := SysErrorMessage(GetLastError);
end;
Prinzip: Die DLL-Funktion gibt die Länge zurück. Ich rufe sie erst mit nil auf, sie schlägt dann fehl und sagt mir, wie groß der Buffer sein muss. Dann alloziiere ich entsprechend Speicher und rufe sie mit einem Zeiger auf einem entsprechend großen Speicherbereich auf.
Das ist auch die übliche Vorgehensweise bei entsprechenden Windows-API Funktionen.

mkinzler 6. Okt 2006 16:07

Re: PChar schneidet letztes Zeichen ab
 
Zitat:

Delphi-Quellcode:
ShowMessage(PChar(GetInstallDir));

Ist doch schon PChar?

Muetze1 6. Okt 2006 16:08

Re: PChar schneidet letztes Zeichen ab
 
Du konvertierst den String temporär zu einem PChar und gibst ihn per Result zurück. Problem dabei ist, dass die Typkonvertierung nur temporär ist und nach verlassen der Funktion ist der Speicher wo der PChar angelegt wurde ungültig. Daher ist es kein Wunder, dass die Daten nicht mehr stimmen. Es ist eher erstaunlich das der PChar soweit noch lesbare Zeichen enthält.

Ich empfehle dir eine andere Art der Stringübergabe: Besorg dir Speicher für den PChar im Hauptprogramm und übergebe diesen samt der Angabe der Länge an die DLL Funktion, welche den Buffer befüllt, wenn er genügend Platz bietet.

Beispiel dazu:
Delphi-Quellcode:
  // Hauptprogramm:
Procedure xy;
Var
  lString: String;
Begin
  SetLength(lString, 2048);
  SetLength(lString, DeineDLLFunc(PChar(lString), 2048));
  ShowMessage(lString);
End;


  // DLL
Function DeineDLLFunc(ABuffer: PChar; Const ABufferLen: Integer): Integer;
Var
  lStr: String;
Begin
  Result := 0;
 
  lStr := Reg.ReadString('whatever');

  If ( ABufferLen > Length(lStr) ) Then // > weil wegen #0 !
  Begin
    StrPCopy(ABuffer, lStr);
    Result := Length(lStr);
  End;
End;
/EDIT: Super - da tippt man die ganze Zeit und zwischenzeitlich wird das gleiche getippelt... Ich hasse umsonst arbeiten...

TheMiller 6. Okt 2006 16:11

Re: PChar schneidet letztes Zeichen ab
 
Hm,

wie soll ich ihn den sonst zurückgeben als per result.

@Luckie: Den Wert habe ich per Hand angelegt. Neuer Schlüssel -> REG-SZ und dann den Pfad eingetragen, aber KEIN abschließendes Leerzeichen. Muss ja auch nicht, oder?

mkinzler 6. Okt 2006 16:13

Re: PChar schneidet letztes Zeichen ab
 
Zitat:

wie soll ich ihn den sonst zurückgeben als per result.
Versuch den Wert mal zwischenzuspeichern bevor du ihn zurückgibst.

Luckie 6. Okt 2006 16:14

Re: PChar schneidet letztes Zeichen ab
 
Siehe mein Edit. Ist auch die Lösung zur Anmerkung von Muetze1.

TheMiller 6. Okt 2006 16:16

Re: PChar schneidet letztes Zeichen ab
 
Hm...

Aber bei ALLEN anderen Werten und sonstigen Strings funktioniert es und hat immer funktioniert... Deine Methode mag richtig und professionell sein - das kann ich jetzt nicht beurteilen - aber das sieht mir nach mehr Aufwand aus, als durch eine DLL gespart werden sollte...

ACHSO: Und Muetze1, umsonst war es nicht. Ist auch sehr gut erklärt. Ich denke, ich teste das mal...

Luckie 6. Okt 2006 16:19

Re: PChar schneidet letztes Zeichen ab
 
Bei deinem Glück solltest du in Erwägung ziehen Lotto zu spielen. Würde sich eventuell lohnen. ;)

Tatsache ist eben, wie schon gesagt wurde, dass es eher verwunderlich ist, dass du überhaupt etwas zurückbekommst. Ob es den Aufwand nun trechtfertigt oder nicht, musst du entscheiden.

Muetze1 6. Okt 2006 16:20

Re: PChar schneidet letztes Zeichen ab
 
Zitat:

Zitat von DJ-SPM
Aber bei ALLEN anderen Werten und sonstigen Strings funktioniert es und hat immer funktioniert... Deine Methode mag richtig und professionell sein - das kann ich jetzt nicht beurteilen - aber das sieht mir nach mehr Aufwand aus, als durch eine DLL gespart werden sollte...

Dann hast du bei ALLEN anderen Funktionen mit einem PChar als Rückgabewert reines Glück, dass der Stack/Heap soweit nicht verändert wurde. Du programmierst jetzt einfach nur nach Zufall - somit: es mag auf x % Rechnern/Konstellationen klappen und bei 100 - x % Rechner im Gegensatz nicht. Auch ist die Suche nach komischen Variablen immer recht (wahn)witzig, vor allem wenn man schon weiss, wo dran es liegt...

/EDIT: Ok, wieder umsonst. Roter Kasten im Urlaub?

TheMiller 6. Okt 2006 16:21

Re: PChar schneidet letztes Zeichen ab
 
Ja das macht es allemahl. Mit dem Lotto kann ich mir auch nochmal überlegen. Können ja, wie der Raab eine Tippgemeinschaft für die 29 Millionen aufmachen!

Ok, ich werde das mit dem Buffer so machen. Ist es auch ok, dass ich einen Buffer von 2048 für meinen String reserviere - oder ist das zu viel?

Luckie 6. Okt 2006 16:22

Re: PChar schneidet letztes Zeichen ab
 
Bau die Funktion so um, dass sie dir sagt, wie viel Speicher sie braucht. Ist, meiner Merinung nach, die sauberste Lösung.

TheMiller 6. Okt 2006 16:24

Re: PChar schneidet letztes Zeichen ab
 
Ok, aber das habe ich noch nicht so recht aus deinem Beispiel verstanden...

Luckie 6. Okt 2006 16:26

Re: PChar schneidet letztes Zeichen ab
 
Ich schreibe heute Abend einen kurzen Artikel mit Demo. Würde ich jetzt nochmachen, aber ich habe Feierabend und will ins Fitnessstudio. ;)

Man, wann ist denn der Tortoise fertig mit einchecken? :wall:

TheMiller 6. Okt 2006 16:28

Re: PChar schneidet letztes Zeichen ab
 
Dankeschön! Find ich nett von Dir / Euch!

Dann warte ich mal gespannt!

Luckie 6. Okt 2006 19:07

Re: PChar schneidet letztes Zeichen ab
 
Ich setze mich jetzt mal dran. Um acht muss ich allerdings meine Freundin abholen. Spätestens um 12 sollte es online sein, wenn nichts dazwischen kommt. ;)

TheMiller 6. Okt 2006 19:10

Re: PChar schneidet letztes Zeichen ab
 
Nur kein Stress! Dank Dir!

Luckie 6. Okt 2006 20:06

Re: PChar schneidet letztes Zeichen ab
 
Hier schon mal die Demo.
DLL:
Delphi-Quellcode:
(******************************************************************************
 *                                                                            *
 *  StringDLL                                                                *
 *  DLL zum Demo-Programm DLLProg                                            *
 *                                                                            *
 *  Copyright (c) 2006 Michael Puff [url]http://www.michael-puff.de[/url]              *
 *                                                                            *
 ******************************************************************************)

library StringDLL;

uses
  SysUtils;

function func1(s: PChar; Buffer: PChar; lenBuffer: Integer): Integer; stdcall;
var
  foo: String;
begin
  // Strings aneinanderhängen
  foo := 'foo'+ s;
  // wenn Stringlänge kleiner oder gleich lang wie lenBuffer
  // String in Buffer kopieren
  if length(foo) <= lenBuffer then
  begin
    StrLCopy(Buffer, PChar(foo), length(foo)+1);
  end;
  // auf alle Fälle immer Länge des Strings zurückgeben
  result := length(foo);
end;

exports
  func1;

begin
end.
Programm:
Delphi-Quellcode:
(******************************************************************************
 *                                                                            *
 *  DLLProg                                                                  *
 *  Demo-Programm Strings und DLLs                                           *
 *                                                                            *
 *  Copyright (c) 2006 Michael Puff [url]http://www.michael-puff.de[/url]              *
 *                                                                            *
 ******************************************************************************)

program DLLProg;

{$APPTYPE CONSOLE}

uses
  windows;

type
  Tfunc1 = function(s: PChar; Buffer: PChar; lenBuffer: Integer): Integer; stdcall;

var
  hLib: THandle;
  s: String;
  func1: Tfunc1;
  len: Integer;
  Buffer: PChar;

begin
  Buffer := nil;

  // DLL laden
  hLib := LoadLibrary('StringDLL.dll');
  if hLib = 0 then
  begin
    Str(GetLastError, s);
    Writeln(s);
    readln;
    exit;
  end;
  Str(hLib, s);
  Writeln('hlib: ' + s);
  // Adresse der exportierten Funktion holen
  @func1 := GetProcAddress(hLib, 'func1');
  if (not Assigned(func1)) then
  begin
    Str(GetLastError, s);
    Writeln(s);
    readln;
    exit;
  end;
  Str(Integer(@func1), s);
  Writeln('@func1: ' + s);
  // Funktion aufrufen, um Größe des Buffers zu ermitteln
  len := func1('bar', nil, 0);
  Str(len, s);
  Writeln('len: ' + s);
  try
    // Speicher anforden
    GetMem(Buffer, len);
    // Funktion mit Buffer aufrufen
    len := func1('bar', Buffer, len);
    Str(len, s);
    // Buffer ausgeben
    writeln(String(Buffer)+ ' [' + s + ']');
  finally
    // Speicher wieder freigeben
    FreeMem(Buffer);
  end;
  readln;
end.
Kommentare sollten selbsterklärend sein.

TheMiller 6. Okt 2006 20:28

Re: PChar schneidet letztes Zeichen ab
 
Danke für Deine Bemühungen. Ich werde es gleich mal durcharbeiten! :thumb:

Luckie 6. Okt 2006 21:22

Re: PChar schneidet letztes Zeichen ab
 
Als ich auf dem Parkplatz auf meine Freundin gewartet habe, habe ich mir noch mal gedanken über die Längenangaben gemacht. Die haben mir keine Ruhe gelassen, was heißt, dass da irgendwas nicht ganz stimmte. Hier noch mal der überarbeitet Code.

DLL
Delphi-Quellcode:
(******************************************************************************
 *                                                                            *
 *  StringDLL                                                                *
 *  DLL zum Demo-Programm DLLProg                                            *
 *                                                                            *
 *  Copyright (c) 2006 Michael Puff [url]http://www.michael-puff.de[/url]              *
 *                                                                            *
 ******************************************************************************)

library StringDLL;

uses
  SysUtils;

function func1(s: PChar; Buffer: PChar; lenBuffer: Integer): Integer; stdcall;
var
  foo: String;
begin
  // Strings aneinanderhängen
  foo := 'foo'+ s;
  // wenn Stringlänge kleiner oder gleich lang wie lenBuffer
  // String in Buffer kopieren
  if length(foo) < lenBuffer then
  begin
    StrLCopy(Buffer, PChar(foo), lenBuffer);
  end;
  // auf alle Fälle immer Länge des Strings zurückgeben
  result := length(foo);
end;

exports
  func1;

begin
end.
Programm:
Delphi-Quellcode:
(******************************************************************************
 *                                                                            *
 *  DLLProg                                                                  *
 *  Demo-Programm Strings und DLLs                                           *
 *                                                                            *
 *  Copyright (c) 2006 Michael Puff [url]http://www.michael-puff.de[/url]              *
 *                                                                            *
 ******************************************************************************)

program DLLProg;

{$APPTYPE CONSOLE}

uses
  windows;

type
  Tfunc1 = function(s: PChar; Buffer: PChar; lenBuffer: Integer): Integer; stdcall;

var
  hLib: THandle;
  s: String;
  func1: Tfunc1;
  len: Integer;
  Buffer: PChar;

begin
  Buffer := nil;

  // DLL laden
  hLib := LoadLibrary('StringDLL.dll');
  if hLib = 0 then
  begin
    Str(GetLastError, s);
    Writeln(s);
    readln;
    exit;
  end;
  Str(hLib, s);
  Writeln('hlib: ' + s);
  // Adresse der exportierten Funktion holen
  @func1 := GetProcAddress(hLib, 'func1');
  if (not Assigned(func1)) then
  begin
    Str(GetLastError, s);
    Writeln(s);
    readln;
    exit;
  end;
  Str(Integer(@func1), s);
  Writeln('@func1: ' + s);
  // Funktion aufrufen, um Größe des Buffers zu ermitteln
  len := func1('bar', nil, 0);
  Str(len, s);
  Writeln('len: ' + s);
  try
    // Speicher anforden
    GetMem(Buffer, len + 1);
    // Funktion mit Buffer aufrufen
    len := func1('bar', Buffer, len + 1);
    Str(len, s);
    // Buffer ausgeben
    writeln(String(Buffer)+ ' [' + s + ']');
  finally
    // Speicher wieder freigeben
    FreeMem(Buffer);
  end;
  readln;
end.
Ich hoffe so stimmt das jetzt. Die Compilermagic kann manch mal ganz schön stören, wenn man nicht weiß, ob sie den String jetzt automatisch nullterminiert oder nicht und so was. :?

Luckie 7. Okt 2006 00:54

Re: PChar schneidet letztes Zeichen ab
 
Fertig. :P

Den Artikel gibt es hier: Rückgabe von Strings aus DLLs

Ich habe die Funktion in der DLL noch etwas vereinfacht. Die komische if-Abfrage ist natürlich überflüssig:
Delphi-Quellcode:
function func1(s: PChar; Buffer: PChar; lenBuffer: Integer): Integer; stdcall;
var
  foo: String;
begin
  // Strings aneinanderhängen
  foo := 'foo'+ s;
  // nur String in Buffer kopieren, wenn Buffer nicht nil ist
  if Assigned(Buffer) then
    StrLCopy(Buffer, PChar(foo), lenBuffer);
  // auf alle Fälle immer Länge des Strings zurückgeben
  result := length(foo);
end;
:roll:

So, ich hoffe, damit kommst du nun klar. ;)

TheMiller 7. Okt 2006 13:06

Re: PChar schneidet letztes Zeichen ab
 
Ok Super!

Werde mich damit befassen. Nur hat jetzt meine Freundin erstmal Geburtstag. Danke Dir.


Alle Zeitangaben in WEZ +1. Es ist jetzt 02:35 Uhr.

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