Entwickler-Ecke

Sonstiges (Delphi) - Problem mit PChar();


NeWsOfTzzz - Mo 28.02.05 16:00
Titel: Problem mit PChar();
das problem ist folgendes: ich habe edit felder und die werden mit pchar umgewandelt
beispiel:

Delphi-Quelltext
1:
pchar(edit.text);                    

wenn in diesen edit feldern jetzt text ist, der irgendeine funktion/variable von delphi enthält gibt es pointer fehler :(
also funktionierende beispiele:

Delphi-Quelltext
1:
2:
edit.text:='beispieltext';
edit.text:='abcdef';

nicht funktionierende:

Delphi-Quelltext
1:
2:
edit.text:='TSTRINGLIST1');
edit.text:='ACCOUNT1');

was soll das denn? ich versteh diese fehler nicht..

wobei ich noch sagen muss, dass das bestimmten regeln folgen muss. vor oder nach dem wort (z.b. TSTRINGLIST) darf nur ein buchstabe/zahl stehen und wenn man das wort ganz alleine eingibt also nur TSTRINGLIST passiert auch nix
ps: ich benutze delphi 4

Moderiert von user profile iconraziel: Beiträge zusammengefasst.
Moderiert von user profile iconMotzi: Delphi-Tags hinzugefügt.


NeWsOfTzzz - Mo 28.02.05 18:50

mein gott ihr mods seid aber kleinlich
:P
aber ne lösung wisst ihr wohl auch nicht? hier mal ein quelltext so dass jeder den fehler replizieren kann:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
procedure provoke_bug;
var
s: string;
p: pchar;
begin
s:='tstringlist1';
p:=pchar(s);
end;


Radioactive - Mo 28.02.05 18:57

Ich kann den Fehler nicht nachvollziehen. Wo kommt der und was für einer?


NeWsOfTzzz - Mo 28.02.05 18:59

ICH KANN DEN FEHLER AUCH NICHT NACHVOLLZIEHEN !!! *HEUL*
der kommt bei

Delphi-Quelltext
1:
p:=pchar(s);                    

und tritt fast immer auf wenn im string der name einer delphi komponente steht..


Radioactive - Mo 28.02.05 19:01

Wie lautet die genaue Fehlerbeschreibung, die unten steht?

Also du weißt, dass so etwas nicht geht:

Delphi-Quelltext
1:
p := PChar(Edit1.Text := 'ACCOUNT'); // p ist ein PChar                    

sondern so:

Delphi-Quelltext
1:
2:
Edit1.Text := 'ACCOUNT';
p := PChar(Edit1.Text);


NeWsOfTzzz - Mo 28.02.05 19:08

das ist kein kompilierfehler, das ist ein laufzeit fehler. und naja ich hab nochwas vergessen, nämlich getmem und freemem: hiermit lässt sich der fehler auf jedenfall replizieren(weil ich´s gerade selber kompiliert habe, das andere war nur frei hand geschrieben):

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
procedure TForm1.Button1Click(Sender: TObject);
var
s: string;  
p: pchar;  
begin  
s:='tstringlist1';
getmem(p,length(s));
p:=pchar(s);
freemem(p);
end;

PS: der fehler liegt wohl gar nicht am p:=pchar(s); sondern am freemem(p)..


delfiphan - Mo 28.02.05 19:09

Ich glaub du hast noch nicht ganz alles gepostet, das mit dem Fehler zusammenhängt... Was machst du nachher mit dem PChar?
//Edit: Den Post gerade drüber hatte ich natürlich noch nicht gesehn! Das da oben geht natürlich nicht!


Radioactive - Mo 28.02.05 19:11

das gleube ich auch.
Du brauchst doch gar kein GetMem/FreeMem nur um einen String in einen PChar umzuwandeln.


NeWsOfTzzz - Mo 28.02.05 19:11

doch das ist exakt alles was ich mache, aber der fehler tritt nicht beim p:=pchar(s); auf sondern beim freemem(p);
klar brauch ich getmem und freemem -.-
pchar ist eine zeigerklasse -.-


Radioactive - Mo 28.02.05 19:12

dann spar dir das ganze mit FreeMem/GetMem. Ich habe es bei keiner einzigen Anwendung gebraucht. Es klappt ohne das wunderbar.


NeWsOfTzzz - Mo 28.02.05 19:14

das stimmt doch gar nicht! was erzählst du denn da?


wulfskin - Mo 28.02.05 19:15

Hallo,

es kommt immer drauf an, was du mit dem PChar machen willst. Wenn du einen String in einen PCHar umwandeln möchtest, dann hilft ein einfacher Typcast. Ansonsten kannst du es mal so versuchen:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
var
  S: String;
  Size: Integer;
  P: PChar;
begin
  S := 'testeg4 t34tsf';
  Size := Length(S) + 1;
  GetMem(P, Size);
  StrPCopy(P, S);
  FreeMem(P, Size);
end;
Gruß Hape!


Radioactive - Mo 28.02.05 19:17

Also ich habe keine Probleme, wenn ich folgendes habe:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
var s: String;

begin
  s := 'Hallo!';
  Application.MessageBox(PChar(s),'Titel',0+64);
end;


NeWsOfTzzz - Mo 28.02.05 19:20

Also es geht darum: ich entwickle ein mmorpg und an mehreren stellen kann man eingaben machen, das läuft auch soweit alles mit dem system welches ich benutze.. mehr oder minder durch zufall ist mir dann letztens aufgefallen dass es bei der eingabe account1 abstürzt :( und dass der fehler halt daran lag, dass account die bezeichnung für eine delphi komponente ist. ich kopier mal den quelltext so dass man sieht, wofür ich pchar brauche und dass es nicht ohne getmem, freemem geht und warum ich length und nicht length+1 nehme (die nullterminierung ist bei mir ohne bedeutung):

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
procedure TForm1.CreateCharacter;
var
  Len: word;
  MSGType: word;
  MSGSize: longword;
  MSG: pchar;
  sndBytes: ^TByte;
  x: longword;
  s: string;
Begin
Len:=Length(CreateCharacterForm.ENewCharacterName.text);
if Len = 0 then begin showmessage('No new name entered.'); exit; end;
if (Len = 1or (Len = 2then begin showmessage('New name is too short!'); exit; end;
MSGSize := (2+Len);
getmem(sndBytes,MSGSize);
getmem(MSG,len);
MSGType:=7;
Move(msgType,sndBytes^[0],2);
StrLCopy(MSG,PChar(CreateCharacterForm.ENewCharacterName.text),Len);
for x:=0 to len-1 do
  sndBytes^[2+x]:=ord(MSG[x]);
ClientSocket.Socket.SendBuf(sndBytes^,MSGsize);
freemem(sndBytes);
freemem(MSG);
end;


@Radioactive
wenn der string einen beliebigen wert hat, funktioniert auch alles fehlerfrei.. abstürzen tut´s nur wenn der name einer delphi komponente vorkommt im string


delfiphan - Mo 28.02.05 19:20

Damit:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
Procedure sowieso;
var
 p: PChar;
begin
 p := PChar('hallo');
 //...
end;

erstellst du ein PChar, welches beim Verlassen der Prozedur gelöscht wird. Du brauchst kein Freemem.


wulfskin - Mo 28.02.05 19:21

Radioactive hat folgendes geschrieben:
Also ich habe keine Probleme, wenn ich folgendes habe:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
var s: String;

begin
  s := 'Hallo!';
  Application.MessageBox(PChar(s),'Titel',0+64);
end;
Speicher doch einfach mal einen String in einem Zeiger und gib ihn wieder frei. Dann weisst du, was er meint.


NeWsOfTzzz - Mo 28.02.05 19:23

meinen quellcode von vorhin läuft fehlerfrei wie gesagt, der fehler tritt nur auf wenn in dem string der name einer delphi komponente vorkommt, also zum beispiel s:='tstringlist1';
wenn ich s:='hallo'; nehme, funzt alles fehlerfrei


Radioactive - Mo 28.02.05 19:26

Gut, da bin ich jetzt ratlos, in meinen PChars sind noch nie Namen von Komponenten vorgekommen. Muss ja ein seltsames Prog sein.


delfiphan - Mo 28.02.05 19:26

Dein Len ist doch eins zu klein, PChar ist nullterminiert und muss um 1 länger sein als ein äquivalenter Pascal String.


NeWsOfTzzz - Mo 28.02.05 19:29

nein es muss nicht einen länger sein, weil ich auch so schon alle buchstaben habe. aber um das auszuschliessen hab ich das mal getestet aber der fehler besteht nach wie vor...
ausserdem kommen in meinen pchars keine namen von komponenten vor, das sind die eingaben der benutzer..


F34r0fTh3D4rk - Mo 28.02.05 19:35

ich bekomme den fehler auch, ist mir aber schleierhaft ...


delfiphan - Mo 28.02.05 19:37

Und ohne die ganze PChar Bastelei? Du Kopierst einen String in einen PChar und liest nachher Byte für Byte vom PChar und schreibst es ins sndBytes. Das ist alles gar nicht nötig.

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
Var
 Buffer : PByteArray;
 Len : Integer;
 MsgType : Word;
 Msg : String;
begin
 Msg := CreateCharacterForm.ENewCharacterName.text;
 Len := length(Msg);
 GetMem(Buffer, Len+2);
 MSGType := 7;
 try
  Move(MsgType, Buffer^[0], 2);
  Move(Msg[1], Buffer^[2], Len);
  ClientSocket.Socket.SendBuf(Buffer^,Len+2);
 finally
  FreeMem(Buffer);
 end;
end;

Ich glaube du überschreibst sonstwo irgend was im Speicher und erhältst deswegen nen Fehler..
//Edit: Korrektur


delfiphan - Mo 28.02.05 19:41

NeWsOfTzzz hat folgendes geschrieben:
nein es muss nicht einen länger sein, weil ich auch so schon alle buchstaben habe. aber um das auszuschliessen hab ich das mal getestet aber der fehler besteht nach wie vor...


Delphi Dokumentation: "StrLCopy copies a null terminator as well as MaxLen characters (das heisst du brauchst MaxLen+1 Bytes zur Verfügung). Be sure that Dest is large enough to accommodate the null terminator at the end."

Es ist egal ob du "so schon alle Buchstaben hast". Du brauchst Platz für die 0 am Ende. Du überschreibst vielleicht an anderen Orten im Programm was und kriegst Folgefehler, wenn du auf ne Komponente zugreifst.


NeWsOfTzzz - Mo 28.02.05 19:47

delfiphan hat folgendes geschrieben:
Es ist egal ob du "so schon alle Buchstaben hast". Du brauchst Platz für die 0 am Ende. Du überschreibst vielleicht an anderen Orten im Programm was und kriegst Folgefehler, wenn du auf ne Komponente zugreifst.

oh, der böse bube kopiert die null bei length+1 einfach drüber? nenene.. was der sich erlaubt ^^
aber ich kann dir garantieren dass das nicht die quelle des fehlers war, und das mit pbytearray guck ich mir mal an


delfiphan - Mo 28.02.05 19:57

Oder in Kurzform ;)

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
Type
 Word2Char = packed record
  Lo, Hi : Char;
 end;
var
 S: string;
 MsgType : Word;
Begin
 MsgType := 7;
 S := Word2Char(MsgType).Lo+Word2Char(MsgType).Hi+CreateCharacterForm.ENewCharacterName.text;
 ClientSocket.Socket.SendBuf(S[1],length(S));
end;


NeWsOfTzzz - Mo 28.02.05 20:09

omg, jetzt hab ich gerade gesehen dass ich für den testlauf mit der länge+1 falsch angesetzt habe, nämlich bei "sndBytes" und nicht bei "MSG" :oops:
und wenn ich jetzt tstringlist1 eingebe, dann geht das..
was mich wundert, was hat diese nullterminierung, die ja so in MSG fehlt, bloß mit delphi komponenten zu tun?
ich hab vorher schon 1000 eingaben gemacht und nie ist das ohne die nullterminierung abgestürzt, erst mit diesem account1...


Motzi - Mo 28.02.05 20:55

Also generell zum Thema PChars, Strings und Typecasting von String nach PChar kann ich dir mal mein String-Tutorial empfehlen: http://www.manuel-poeter.de


NeWsOfTzzz - Mo 28.02.05 21:08

guck ich mir mal an, danke.. ich wusste ehrlich gesagt bis heute nicht dass die normalen strings in delphi nicht nullterminiert sind.. hab früher nur mit c++ gearbeitet.. deswegen hab ich auch einfach die länge des normalen strings übernommen :oops:


Motzi - Mo 28.02.05 21:47

NeWsOfTzzz hat folgendes geschrieben:
ich wusste ehrlich gesagt bis heute nicht dass die normalen strings in delphi nicht nullterminiert sind..

Sind sie eh..! Nur Delphi führt dieses Nullzeichen intern automatisch mit und dieses wird bei der Länge nicht berücksichtigt. ;) Lass dir mal das Zeichen an der Stelle String[Length(String)] ausgeben, du wirst feststellen, dass es das letzte Zeichen dest Strings ist, und nicht das Nullzeichen. Das Nullzeichen befindet sich an Position Length(String) + 1.


NeWsOfTzzz - Mo 28.02.05 21:56

man kann mit string[i: integer] auf eine bestimmte stelle im string zugreifen? dann is der ganze umweg über pchar ja eh umsonst?
ps: ja es geht :gruebel: :motz:
soviel arbeit ganz umsonst ^^


Motzi - Mo 28.02.05 22:05

Ja, kann man.. und dass der ganze Umweg über PChar umsonst ist haben dir in dem Thread auch schon ein paar Leute zu erklären versucht.. ;)


NeWsOfTzzz - Mo 28.02.05 22:09

nun reite nicht noch drauf rum, das tut so schon weh O.o


bockwurst - Di 15.03.05 11:56

hallo zusammen,
alles läuft immer wunderbar wenn du den String mit StrAlloc oder mit StrNew reserviert. Dannach mußt du ihn immer mit StrDispose freigeben.


Andreas