| Autor |
Beitrag |
Wolle92
      
Beiträge: 1296
Windows Vista Home Premium
Delphi 7 PE, Delphi 7 Portable, bald C++ & DirectX
|
Verfasst: Fr 14.09.07 07:36
Hallo,
weiß irgendjemand, wieso der Code nur funktioniert, wenn ich ein ShowMessage irgenwo in die Funktion schreibe?
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
| function decode(text: PChar): PChar; export; var current, text2, result2: String; i: Integer; begin result2 := ''; text2 := String(text); while not (text2 = '') do begin current := Copy(text2,1,Pos('z',text2)-1); text2 := Copy(text2,Pos('z',text2)+1,Length(text2)-Pos('z',text2)); i := StrtoInt(current); result2 := result2 + Chr(Byte(i)); end; result := PChar(result2); end; |
Und wenn irgendwo ShowMessage('Ein beliebiger Text!'); steht, kommt kein Fehler mehr... So gibts den Fehler '*' is not valid integer value! wobei das * für dieses Kästchen steht, wenn ein Zeichen nicht angezeigt werden kann...
|
|
Gausi
      
Beiträge: 8554
Erhaltene Danke: 481
Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
|
Verfasst: Fr 14.09.07 07:44
Du gibst einen Zeiger auf eine lokale Variable zurück. Das geht nicht. Da die String-Variable nach verlassen der Prozedur nicht mehr benötigt wird, kann der Platz wieder überschrieben werden, und der Zeiger wird ungültig.
Ob der Fehler nun daran liegt, weiß ich nicht. Ich könnte mir vorstellen, dass die Speicherstruktur durch diese Message anders aussieht, und das Programm deswegen zufällig durchläuft.
_________________ We are, we were and will not be.
|
|
Wolle92 
      
Beiträge: 1296
Windows Vista Home Premium
Delphi 7 PE, Delphi 7 Portable, bald C++ & DirectX
|
Verfasst: Fr 14.09.07 07:55
aber wie soll ich den wert denn sonst zurückgeben?
|
|
Gausi
      
Beiträge: 8554
Erhaltene Danke: 481
Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
|
Verfasst: Fr 14.09.07 08:54
Wenns innerhalb eines Programms ist, als String.
Bei dlls bin ich mir nicht sicher, wie das geht. Eine Möglichkeit beschreibt Luckie hier.
_________________ We are, we were and will not be.
|
|
Wolle92 
      
Beiträge: 1296
Windows Vista Home Premium
Delphi 7 PE, Delphi 7 Portable, bald C++ & DirectX
|
Verfasst: Fr 14.09.07 09:00
das versteh ich jetzt nicht so ganz... mit der DLL...
|
|
Gausi
      
Beiträge: 8554
Erhaltene Danke: 481
Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
|
Verfasst: Fr 14.09.07 09:05
Soll das denn ne Funktion für ne dll werden, oder willst du diese Funktion ganz normal in einem Programm benutzen?
_________________ We are, we were and will not be.
|
|
Stefan.Buchholtz
      
Beiträge: 612
WIN 2000, WIN XP, Mac OS X
D7 Enterprise, XCode, Eclipse, Ruby On Rails
|
Verfasst: Fr 14.09.07 09:17
Der Aufrufer muss einen Speicherblock in der passenden Größe erzeugen und ihn als Parameter an die DLL-Funktion übergeben, die ihn dann füllen kann. Von der Delphi-Runtime verwaltete Typen mit dynamischer Speicherverwaltung (Strings, dynamische Arrays, Varianten) dürfen nicht zu oder von einer DLL übergeben werden. Einzige Ausnahme: Eingabeparameter, die von der DLL-Funktion nicht in der Länge verändert werden.
Wenn das aufrufende Programm auch in Delphi geschrieben ist, kannst du natürlich auch ShareMem einbinden, dann können die dynamischen Typen problemlos zwischen DLL und Hauptprogramm ausgetauscht werden.
Willst du ShareMem nicht verwenden, müsste es so gehen (ungetestet):
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: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35:
| uses StrUtils;
function decode(const text: PChar; const resultBuf : PChar; const resultBufSize: Integer): Integer; export; var current : String; text2 : String; i : Integer; idx : Integer; p : Integer; begin SetLength(text2, StrLen(text)); StrCopy(PChar(text2), text);
idx := 0; while text2 <> '' do begin p := Pos('z', text2); current := Copy(text2, 1, p-1); text2 := LeftStr(text2, p + 1); i := StrToInt(current); if resultBufSize <= idx then begin Result := -1; exit; end; resultBuf[idx] := Chr(Byte(i)); Inc(idx); end; Result := idx; end; |
Stefan
_________________ Ein Computer ohne Windows ist wie eine Schokoladentorte ohne Senf.
|
|
Wolle92 
      
Beiträge: 1296
Windows Vista Home Premium
Delphi 7 PE, Delphi 7 Portable, bald C++ & DirectX
|
Verfasst: Fr 14.09.07 09:46
OK, aber ich verändere die Parameter doch überhaupt nicht... die werden doch alle in String-Variablen kopiert...
|
|
jaenicke
      
Beiträge: 19346
Erhaltene Danke: 1754
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Fr 14.09.07 10:00
Der Punkt ist, dass du einen Zeiger auf eine bestimmte Speicherstelle zurückliefern wolltest, an dieser Speicherstelle aber gar kein gültiger Wert mehr steht, wenn die Funktion beendet ist. Denn die Variable war ja nur lokal definiert, existiert also nur während der Laufzeit der Prozedur. Genau das hat Gausi aber auch schon gesagt.
Die Lösung, die Stefan.Buchholtz gepostet hat, besteht darin, dass du vorher einen Zeiger auf einen Speicherbereich nimmst, dort den benötigten Speicher allozierst, d.h. reservierst, und dann die Funktion aufrufst und als Parameter diesen Zeiger sowie die maximale Größe an Daten, die da reingelegt werden dürfen übergibst.
Dieses Prinzip wird auch bei den Windows-API-Funktionen sehr oft verwendet...
|
|
Stefan.Buchholtz
      
Beiträge: 612
WIN 2000, WIN XP, Mac OS X
D7 Enterprise, XCode, Eclipse, Ruby On Rails
|
Verfasst: Fr 14.09.07 10:23
Wolle92 hat folgendes geschrieben: | | OK, aber ich verändere die Parameter doch überhaupt nicht... die werden doch alle in String-Variablen kopiert... |
Eine String-Variable enthält ja nicht direkt den Text, sondern das ist intern ein Zeiger auf einen Speicherblock, in dem der Text drin steht. Bei einer Zuweisung von String-Variablen wird erstmal nur der Zeiger kopiert, so dass beide String-Variablen auf den gleichen Speicherblock zeigen. Zusätzlich merkt sich die Delphi-Runtime, dass jetzt zwei String-Variablen auf den Block verweisen. Erst wenn eine der beiden Variablen verändert wird, wird der eigentliche Text in einen neuen Speicherblock kopiert.
Man muss relativ vorsichtig dabei sein, wenn man zwischen String und PChars castet. Beide sind ein Zeiger auf einen Speicherblock, in dem der Text steht, aber nur Strings haben diese automatische Speicherverwaltung - die nicht funktioniert, wenn der Speicher zwischen Programm und DLL ausgetauscht wird, ausser man verwendet ShareMem. Deswegen habe ich für den Parameter text einen neuen String mit SetLength erzeugt - SetLength erzeugt immer einen neuen Speicherblock der angegebenen Länge - und kopiere text explizit mit StrCopy da rein. Dann kann ich sicher sein, dass der Speicher, der von text2 verwendet wird, in der DLL angefordert wurde, so dass spätere Veränderungen von text2 innerhalb der Funktion unproblematisch sind.
Stefan
_________________ Ein Computer ohne Windows ist wie eine Schokoladentorte ohne Senf.
|
|
Wolle92 
      
Beiträge: 1296
Windows Vista Home Premium
Delphi 7 PE, Delphi 7 Portable, bald C++ & DirectX
|
Verfasst: Fr 14.09.07 11:52
|
|
Wolle92 
      
Beiträge: 1296
Windows Vista Home Premium
Delphi 7 PE, Delphi 7 Portable, bald C++ & DirectX
|
Verfasst: Fr 14.09.07 13:15
kann man resultBuf nicht auch als var anstatt als const deklarieren, und die Größe, falls nötig, in der Prozedur ändern? Ich halte es für sinnvoller, als dass man alles nochmal neu dekodieren muss, wenn resultBuf nur um eins zu klein ist...
Ich stelle mir das ungefähr so vor:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| var resultBuf2: PChar; ... if resultBufSize <= idx then begin Inc(resultBufSize); SetLength(resultBuf2, StrLen(resultBuf)); StrCopy(resultBuf2, resultBuf); SetLength(resultBuf, StrLen(resultBuf)+1); StrCopy(resultBuf, resultBuf2); end; |
Wäre das nicht auch Möglich?
|
|
Stefan.Buchholtz
      
Beiträge: 612
WIN 2000, WIN XP, Mac OS X
D7 Enterprise, XCode, Eclipse, Ruby On Rails
|
Verfasst: Fr 14.09.07 13:25
Wolle92 hat folgendes geschrieben: | kann man resultBuf nicht auch als var anstatt als const deklarieren, und die Größe, falls nötig, in der Prozedur ändern? Ich halte es für sinnvoller, als dass man alles nochmal neu dekodieren muss, wenn resultBuf nur um eins zu klein ist...
Ich stelle mir das ungefähr so vor:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| var resultBuf2: PChar; ... if resultBufSize <= idx then begin Inc(resultBufSize); SetLength(resultBuf2, StrLen(resultBuf)); StrCopy(resultBuf2, resultBuf); SetLength(resultBuf, StrLen(resultBuf)+1); StrCopy(resultBuf, resultBuf2); end; |
Wäre das nicht auch Möglich? |
Nein, genau das geht ja eben nicht. Eine DLL darf keine Speicherblöcke freigeben oder in der Größe ändern, die es vom Hauptprogramm bekommen hat, und umgekehrt entsprechend genauso. Wenn man das macht, kommt die Speicherverwaltung von Delphi durcheinander und es hagelt Zugriffsverletzungen oder sonstige Fehler. Deswegen muss der Aufrufer den Speicherblock erzeugen.
Wenn ich die Funktion richtig verstanden habe, stehen in deinem übergebenen Text durch 'z' getrennte Zahlen drin und die Funktion extrahiert diese Zahlen und schreibt die entsprechenden ASCII-Zeichen in das Resultat. Das bedeutet, dass der Rückgabewert immer kürzer als der übergebene Text ist - somit ist die Länge des übergebenen Textes eine gute Größe für den Ergebnispuffer - das passt immer.
Stefan
_________________ Ein Computer ohne Windows ist wie eine Schokoladentorte ohne Senf.
|
|
Wolle92 
      
Beiträge: 1296
Windows Vista Home Premium
Delphi 7 PE, Delphi 7 Portable, bald C++ & DirectX
|
Verfasst: Fr 14.09.07 14:01
stimmt, könnte man theoretisch sogar noch durch 3 teilen, da die meisten Zeichen ja einen zweistelligen Code haben, einige einen dreistelligen und das z kommt auch noch dazu
...
Aber wenn ich resultBuf als const deklariere, dann kann ich es doch nicht verändern und bekomme kein Ergebnis...
|
|
Motzi
      
Beiträge: 2931
XP Prof, Vista Business
D6, D2k5-D2k7 je Prof
|
Verfasst: Fr 14.09.07 14:35
Ich verweise einfach mal auf meine beiden Tutorials über Strings und Pointer. Beide zu finden auf www.manuel-poeter.de
Gruß, Motzi
_________________ gringo pussy cats - eef i see you i will pull your tail out by eets roots!
|
|
Wolle92 
      
Beiträge: 1296
Windows Vista Home Premium
Delphi 7 PE, Delphi 7 Portable, bald C++ & DirectX
|
Verfasst: Di 18.09.07 16:23
Öhm... mal ne ganz blöde Frage, aber wie kann ich die Länge von einem PChar festlegen? Ich bekomme gerade immer eine AccessViolation wenn ich die decode-Funktion benutzen will...
|
|
Stefan.Buchholtz
      
Beiträge: 612
WIN 2000, WIN XP, Mac OS X
D7 Enterprise, XCode, Eclipse, Ruby On Rails
|
Verfasst: Di 18.09.07 16:49
Du musst dir den Speicher für den PChar mit GetMem(pCharVar, laenge); besorgen und wenn du ihn nicht mehr brauchst mit FreeMem(pCharVar); wieder freigeben. Als Länge musst du die Maximallänge des Strings + 1 angeben. +1 deshalb, weil PChars nullterminiert sind, d.h. das Ende des Strings wird mit einem Nullbyte angezeigt, für das auch noch Platz da sein muss.
Stefan
_________________ Ein Computer ohne Windows ist wie eine Schokoladentorte ohne Senf.
|
|
Wolle92 
      
Beiträge: 1296
Windows Vista Home Premium
Delphi 7 PE, Delphi 7 Portable, bald C++ & DirectX
|
Verfasst: Di 18.09.07 17:24
Tja, jetzt gibt es in dem Programm eine Fehlermeldung "'666**' is not valid integer value" * = Kästchen für nicht darstellbares Zeichen.
An der Länge des codeBuf kann nicht liegen, der ist ja viel länger als das Ergebnis...
Aber: Muss man eventuell noch irgendwas machen, bevor man FreeMem anwendet??
Außerdem ist es qwieder so, dass es funktionert wenn ich an bestimmten Stellen ShopwMEssage benutze
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: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61:
| procedure TForm2.Button1Click(Sender: TObject); var vfl, ls, buffer: String; codeBuf: PChar; f: TextFile; li, i, codeBufSize: Integer; decode: Tcode; handle1: THandle; begin try handle1 := LoadLibrary(PChar(ExtractFilePath(Application.ExeName)+'crypt.dll')); if handle1 = 0 then begin ShowMessage('Erforderliche DLL crypt.dll nicht gefunden...' + #13#10 + 'Vokabelsammlung konnte nicht geladen werden!'); Exit; end; @decode := GetProcAddress(handle1, 'decode'); vfl := ExtractFilePath(Application.ExeName) + 'lists\' + ListView1.Selected.Caption + '.vfl'; try AssignFile(f, vfl); Reset(f); ReadLn(f, ls); codeBufSize := Length(ls); GetMem(codeBuf, codeBufSize); decode(PChar(ls),codeBuf,codeBufSize); li := StrToInt(String(codeBuf)); SetLength(Form1.ask, li); SetLength(Form1.answer, li); for i := 0 to li - 1 do begin ReadLn(f, buffer); codeBufSize := Length(buffer); FreeMem(codeBuf); GetMem(codeBuf, codeBufSize); decode(PChar(buffer),codeBuf,codeBufSize); Form1.ask[i] := String(codeBuf); ReadLn(f, buffer); codeBufSize := Length(buffer); FreeMem(codeBuf); GetMem(codeBuf, codeBufSize); decode(PChar(buffer),codeBuf,codeBufSize); Form1.answer[i] := String(codeBuf); end; Closefile(f); FreeMem(codeBuf); except on E: EInOutError do begin ShowMessage('Fehler beim lesen der Datei! Fehler-Code: ' + IntToStr(e.ErrorCode)); end; end; Form1.Label5.Caption := 'Aktuelle Sammlung: ' + ListView1.Selected.Caption; Form1.Button3.Enabled := true; Button1.Enabled := false; FreeLibrary(handle1); Form2.Hide; except on EAccessViolation do ShowMessage('Bitte erst eine Sammlung auswählen!'); end; end; |
Zuletzt bearbeitet von Wolle92 am Di 18.09.07 17:54, insgesamt 1-mal bearbeitet
|
|
jaenicke
      
Beiträge: 19346
Erhaltene Danke: 1754
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Di 18.09.07 17:50
Naja, wo tritt der Fehler denn auf? Raten würde ich, dass das in Zeile 26 der Fall ist. Vermutlich, weil eben ein Nicht-Integer-Wert in codeBuf zurückgegeben wird...
Aber das müsstest du schon selbst überprüfen. Bzw.: Mit was für einem Dateiinhalt kann ich das testen? Also was steht in deiner vfl-Datei drin? ^^
Und noch was: Da der Beitrag hier offen im Internet steht, würde ich dir raten die E-Mail-Adresse aus deinem Quelltext zu entfernen, das Postfach könnte sonst ein wenig spamhaltig werden... 
|
|
Wolle92 
      
Beiträge: 1296
Windows Vista Home Premium
Delphi 7 PE, Delphi 7 Portable, bald C++ & DirectX
|
Verfasst: Di 18.09.07 17:57
In meiner VFL-Datei stehen ASCII-Codes von Zeichen, die durch 'z' getrennt sind...
Im Anhang ist die Beispiel-VFL des Vokabeltrainers...
Die Dekodier-Funktion kannst du dir ja besorgen hier aus dem Beitrag...
OffTopic: Danke wegen der EMail-Adresse... bei mir kommen aber wenn dann immer nur so Schwälle... mal zwei Wochen jeden tag zehn Spam-Mails, dann 2 Wochen nix... Aber das ist ein anderes Thema
Einloggen, um Attachments anzusehen!
|
|
|