Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi PChars und Dll (https://www.delphipraxis.net/75036-pchars-und-dll.html)

Billi Berserker 13. Aug 2006 10:43


PChars und Dll
 
Ich habe ein ziemliches Problem mit PChars die aus einer Dll in irgendeine Anwendung oder dll übergeben werden.
In einer dll gibt eine funktion einen String als PChar an die Anwendung zurück, die Anwendung ließt den PChar einfach auf einen String und benutzt den weiter. Funktioniert an sich relativ gut, nur ist das Problem das total zufällig und nur für manche User am Ende der PChars noch die 0 stehen bleibt.
Gibt die DLL den String 'C:\SharpE\Settings' zurück kommt bei einigen wenigen Usern 'C:\SharpE\Settings0' an. Das seltsame ist das es im normalfall ohne Probleme geht und nur ganz selten diese Strings mit der 0 am Ende ankommen. Das ganze betrifft auch nicht nur eine funktion der dll, sondern ausnahmslos alle die einen PChar zurück geben.

der Code sieht folgendermaßen aus:

Beispiel für eine function aus der dll:
Delphi-Quellcode:
function GetCurrentSkinFile : PChar;
var
  SkinDir : String;
  SkinName : String;
  stemp:String;
begin
  SkinDir := GetSkinDirectory;
  SkinName := GetSkinName;
  stemp := SkinDir +
           IncludeTrailingBackslash(SkinName) +
           'Skin.xml';
  result := PChar(stemp);
end;
Ich habe es auch bereits so probiert das stemp eine globale variable in der dll ist... hat auch keinen großen Unterschied gemacht.
in der Anwendung werden die funktionen meisten direkt benutzt oder auf Strings eingelesen.
also z.b. direkt: XML.LoadFromFile(SharpApi.GetCurrentSkinFile);

Ich geh einfach mal schwer davon aus das irgendwas mit der PChar Handhabung in der dll nicht stimmt,
die Frage ist nur was genau?

Jelly 13. Aug 2006 10:47

Re: PChars und Dll
 
Hast Du sowohl in deiner DLL als auch in deinem Programm jeweils die Unit ShareMem eingebunden. Die Unit muss als allererste eingebunden werden.

Jürgen Thomas 13. Aug 2006 10:51

Re: PChars und Dll
 
Sind GetSkinDirectory und GetSkinName Strings oder PChar?

Delphi nimmt die Umwandlung bei den Zuweisungen zwar oft automatisch vor; aber sicherer ist ggf.:
Delphi-Quellcode:
//  nur gültig, wenn GetSkinDirectory und GetSkinName jeweils PChar liefern
SkinDir := StrPas(GetSkinDirectory);
SkinName := StrPas(GetSkinName);
Hoffentlich hilft's! Jürgen

SirThornberry 13. Aug 2006 10:52

Re: PChars und Dll
 
dir fehlt glaub ich das Verständnis was PChar ist.
PChar ist nichts anderes als ein Pointer der auf eine Reihe von zeischen zeigt welche in der Regel mit #0 terminiert wird.
Mit
Delphi-Quellcode:
result := PChar(stemp);
gibst du also einen Pointer auf die Zeischen von "sTemp" zurück.
Allerdings ist nach verlassen deiner Funktion sTemp gar nicht mehr verfügbar da es eine lokale Variable ist welche auf dem Stack abgelegt werden. Nach verlassen deiner Funktion ist sTemp also nicht mehr gültig. Deine Funktion funktioniert allerdings ab und zu weil zu dem Zeitpunkt vermutlich auf dem Stack noch nix neues liegt. Sobald aber was neues auf dem Stack liegt überschreibt dies den Wert der früher auf dem Stack lagt wo dein Pointer drauf zeigt.

Billi Berserker 13. Aug 2006 11:02

Re: PChars und Dll
 
Zitat:

Zitat von Jelly
Hast Du sowohl in deiner DLL als auch in deinem Programm jeweils die Unit ShareMem eingebunden. Die Unit muss als allererste eingebunden werden.

Nein ist weder in dll noch in irgendeiner der Anwendungen angegeben.
Ich dachte eigentlich das man ShareMem nicht braucht wenn man mit PChars arbeitet.

Sollte ich jetzt ShareMem in der dll hinzufügen,
bringt das irgendwelche Nachteile mit sich und könnten auch Anwendungen die kein ShareMem eingebunden haben die dll noch nutzen?
Und was ist mit anwendungen die in anderen Programmiersprachen geschrieben sind?


Zitat:

Zitat von Jürgen Thomas
Sind GetSkinDirectory und GetSkinName Strings oder PChar?

Delphi nimmt die Umwandlung bei den Zuweisungen zwar oft automatisch vor; aber sicherer ist ggf.:
Delphi-Quellcode:
//  nur gültig, wenn GetSkinDirectory und GetSkinName jeweils PChar liefern
SkinDir := StrPas(GetSkinDirectory);
SkinName := StrPas(GetSkinName);
Hoffentlich hilft's! Jürgen

Alle funktionen geben PChar zurück.
Die zusätzliche 0 tritt im normalfall auch nur am Ende auf und nicht mittendrin.


Zitat:

Zitat von SirThornberry
dir fehlt glaub ich das Verständnis was PChar ist.
PChar ist nichts anderes als ein Pointer der auf eine Reihe von zeischen zeigt welche in der Regel mit #0 terminiert wird.
Mit
Delphi-Quellcode:
result := PChar(stemp);
gibst du also einen Pointer auf die Zeischen von "sTemp" zurück.
Allerdings ist nach verlassen deiner Funktion sTemp gar nicht mehr verfügbar da es eine lokale Variable ist welche auf dem Stack abgelegt werden. Nach verlassen deiner Funktion ist sTemp also nicht mehr gültig. Deine Funktion funktioniert allerdings ab und zu weil zu dem Zeitpunkt vermutlich auf dem Stack noch nix neues liegt. Sobald aber was neues auf dem Stack liegt überschreibt dies den Wert der früher auf dem Stack lagt wo dein Pointer drauf zeigt.

Wäre der ganze String in der Anwendung total verstümmelt und falsch würde ich dem ja zustimmen, die sache ist aber das nur am Ende eine 0 übrig bleibt und der spaß in 99,9% aller fälle funktioniert.
Was passiert denn wenn ich in der Anwendunge eine globale/lokale String variable habe und den rückgabewert darauf zuweise? Da wird sicherlich nicht nur einfach der pointer auf die dll Daten kopiert sondern der Inhalt in die neue lokale/globale String variable kopiert. Oder?

faux 13. Aug 2006 11:07

Re: PChars und Dll
 
Zitat:

Zitat von Jelly
Hast Du sowohl in deiner DLL als auch in deinem Programm jeweils die Unit ShareMem eingebunden. Die Unit muss als allererste eingebunden werden.

Aber doch nur, wenn man Strings selbst übergibt. Er übergibt doch einen PChar und wandelt diesen intern in einen String um, um besser damit arbeiten zu können, oder irre ich?!

SirThornberry 13. Aug 2006 11:14

Re: PChars und Dll
 
@Billi Berserker: Wenn dein String eine globale Variable ist funktioniert es ohne Probleme mit
Delphi-Quellcode:
result := PChar(GlobaleStringVariable);
es muss eben nur eine Variable sein die nach verlassen der Funktion noch gültig ist.

Das mit dem Stack war ein Fehler meiner seits. Bei Strings wird auf dem Stack nur ein Pointer abglegt welcher dann auf den eigentlichen String zeigt. Durch den Cast auf PChar wird ein Pointer zurück gegeben der auf den Speicher zeigt wo die Zeischen liegen. Nach verlassen der Funktion wird der Speicher zwar freigegeben (vom Memorymanager) aber ist immer noch im Prozess gültig. In 99% der Fälle wird dieser Speicher danach nicht wieder verwendet. In einigen Fällen wird aber an der Stelle wo ursprünglich die zeischen des Strings lagen was neues hinn geschrieben und somit ist dein String nicht mehr korrekt.

Und die Unit ShareMem braucht man nur wenn man zwischen DLL und Hauptprogramm mit Strings etc. arbeiten will wie direkt im Programm. Dies hat allerdings den Nachteil das Programme anderer Programmiersprachen die Funktionen nicht nutzen können weil diese keine Delphistrings kennen. Zudem muss die sharemem.dll mit dem Programm immer mitgegeben werden.

Billi Berserker 13. Aug 2006 11:20

Re: PChars und Dll
 
Ich werds mal versuchen stemp dann als global in der dll zu definieren.
Hatte ich zwar soweit ich mich erinnere bereits probiert aber ich geh jetzt nochmals alle funktionen durch.
Das komische ist halt aber wirklich das der String an sich noch korrekt ist, der kann extremst lang sein und es sind keine Fehler drin. Nur sehr sehr selten ist am Ende noch eine zusätzliche '0' dran.

Was ich im ersten post vergessen habe ist das die meisten Anwendungen die die dll benutzen mit vcl und rtl runtime packages compiliert sind und die dll nicht mit den runtime packages compiliert ist.

m.wollert 13. Aug 2006 11:33

Re: PChars und Dll
 
Hi,

die Frage an sich ist, wie lange "stemp" gültig ist.

Ich würde als Versuch für "stemp" explizit Speicher reservieren und in der DLL den Speicher wieder freigeben.

Flocke 13. Aug 2006 12:49

Re: PChars und Dll
 
Einen PChar als Ergebnis einer Funktion zurückzugeben (egal ob DLL oder nicht) ist keine gute Idee. Was denkst du, warum es keine Windows-API-Funktion gibt, die so etwas tut? Funktionen, die einen String ermitteln, bekommen normalerweise zwei Parameter: einen PChar von der aufrufenden Funktion und dessen Größe (siehe z.B. MSDN-Library durchsuchenGetWindowsDirectory).

Selbst wenn du eine globale Stringvariable für den Rückgabewert benutzt, so hättest du immer noch das Problem, dass das Ergebnis nur so lange Gültigkeit besitzt, wie du diese Variable nicht änderst. Und ob der Aufrufer ein korrektes Ergebnis erhält hängt einfach nur davon ab, ob der Speicher in der Zwischenzeit schon überschrieben wurde oder nicht (worauf du normalerweise keinen Einfluss hast).

Die einzige wirklich funktionierende Lösung wäre ein festes char-Array:
Delphi-Quellcode:
// Globale Variable mit gleichbleibender Adresse
var
  ReturnValue: array [0 .. 255] of char;

function GetCurrentSkinFile : PChar;
var
  SkinDir : String;
  SkinName : String;
  stemp:String;
begin
  SkinDir := GetSkinDirectory;
  SkinName := GetSkinName;
  stemp := SkinDir +
           IncludeTrailingBackslash(SkinName) +
           'Skin.xml';
  StrCopy(@ReturnValue[0], PChar(stemp));
  result := @ReturnValue[0];
end;


Alle Zeitangaben in WEZ +1. Es ist jetzt 21:00 Uhr.
Seite 1 von 2  1 2      

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-2025 by Thomas Breitkreuz