String to PWideChar

Für Fragen von Einsteigern und Programmieranfängern...
Antworten
michael76
Beiträge: 24
Registriert: Fr 18. Dez 2020, 21:56

String to PWideChar

Beitrag von michael76 »

Hallo Zusammen,

ich habe wieder mal ein Problem, bei dem ich nicht mehr weiter komme.

Habe eine kleine DLL mit einer Function geschrieben.
Übergeben wird ein Dateiname, eine Nummer und eine Kennung.
Die DLL liest dann aus der Datei (*.INI) etwas aus und soll sie als PWideChar zurück geben. Das muss auch so sein, denn darauf hab ich keinen Einfluss.
Irgendwie muss ich aus dem ausgelsenen String der INI Datei ein PWideChar machen, bin aber ratlos, da ich schon einiges probiert (als kommentar im Quelltext) hab. Hat jemand eine Idee wie ich das machen muss?

Viele Grüße
Michael

Code: Alles auswählen

function ReadString(Dateiname : PWideChar; Number : Integer; Kennung : PWideChar) : PWideChar; stdcall;
var
  ini: TIniFile;
  Filename : String;
  Position : String;
  INI_Kennung: String;
  iniwert : String;
  wertW : WideString;
  wertPWC   : PWideChar;
begin
  Filename := 'C:\' + WideCharToString(Dateiname) + '.ini';
  ini := TIniFile.Create(Filename);
  Position := 'Position' + IntToStr(Number);
  INI_Kennung := WideCharToString(Kennung);
  iniwert := ini.ReadString (Position, INI_Kennung,  'Nix');
  ini.Free;

// Alle Kommentare hier hab ich schon probiert, jedoch immer mit dem Ergebniss eines Fehlers 
//  wertWS := UTF8Decode(iniwert);
//  StringToUnicodeChar(iniwert,wertPWC,30);
//  wertPWC := StrCopy(iniwert,0,Length(wert));
//nok  StrCopy(wertPWC,PWideChar(wertWS));

//  getmem(tempwert, 256);
//  wertPWC := StringToWideChar(iniwert, tempwert, 256);
//  freemem(tempwert,256);

//  StringToWideChar(iniwert,wertPWC,30);

//  iniwert := 'Hier ist der String';
//  StringToWideChar(iniwert,wertPWC,30);


// Wenn ich den PWideChar direkt initialisiere, läuft alles korrekt.
  wertPWC := 'Hier ist der String';
  result := wertPWC;
end;
exports
ReadString;


Benutzeravatar
theo
Beiträge: 10500
Registriert: Mo 11. Sep 2006, 19:01

Re: String to PWideChar

Beitrag von theo »

Bin jetzt nicht ganz sicher wegen der DLL/Heap, aber grundsätzlich ist das ja ein Zeiger auf einen WideChar.
Der sollte natürlich erst mal existieren.

Code: Alles auswählen

function WCTest:PWideChar;
var WC:WideChar;
begin
 WC:='a';
 Result:=@WC;
end;   

michael76
Beiträge: 24
Registriert: Fr 18. Dez 2020, 21:56

Re: String to PWideChar

Beitrag von michael76 »

Hallo Theo,

das hab ich ja so probiert und so geht es auch. Mein Problem ist, das ich den String (aus der INI Datei) nicht in einen WideChar oder PWideChar umwandeln kann

Benutzeravatar
Jorg3000
Lazarusforum e. V.
Beiträge: 170
Registriert: So 10. Okt 2021, 10:24
OS, Lazarus, FPC: Win64
Wohnort: NRW

Re: String to PWideChar

Beitrag von Jorg3000 »

Hi!
Da das Result auch dann noch im Speicher vorhanden sein muss, nachdem die Funktion verlassen wurde, muss der zurückgegebene String z.B. als globale Variable weiterexistieren ...
var GlobalerIniWert: UnicodeString = '';

In der Funktion wird die globale Variable befüllt, also mittels GlobalerIniWert:=ini.ReadString (...);

Die Funktion übergibt als Result dann einen Zeiger auf den String-Speicher der globalen Variable:
Result:=PWideChar(GlobalerIniWert);
gleichbedeutend mit Result:=@GlobalerIniWert[1]; (funzt aber nur wenn nicht leer)

Grüße, Jörg

michael76
Beiträge: 24
Registriert: Fr 18. Dez 2020, 21:56

Re: String to PWideChar

Beitrag von michael76 »

Ich glaube die Lösung gefunden zu haben. Die Länge vom WideChar schon bei der Initialisierung definiert.

Code: Alles auswählen

function ReadString(Dateiname : PWideChar; Number : Integer; Kennung : PWideChar) : PWideChar; stdcall;
var
  ini: TIniFile;
  Filename : String;
  Position : String;
  INI_Kennung: String;
  inistring : String;
  return  : array[0..254] of WideChar;
begin
  Filename := 'C:\' + WideCharToString(Dateiname) + '.ini';
  ini := TIniFile.Create(Filename);
  Position := 'Position' + IntToStr(Number);
  INI_Kennung := WideCharToString(Kennung);
  inistring := ini.ReadString (Position, INI_Kennung,  'Nix');
  ini.Free;

  return := inistring;
  result := @return;
end;
exports
ReadString;                 

Benutzeravatar
Jorg3000
Lazarusforum e. V.
Beiträge: 170
Registriert: So 10. Okt 2021, 10:24
OS, Lazarus, FPC: Win64
Wohnort: NRW

Re: String to PWideChar

Beitrag von Jorg3000 »

Da der Speicher für deine lokale return-Variable beim Verlassen der Funktion freigegeben bzw. bei nächster Gelegenheit überschrieben wird, kannst du außerhalb der Funktion nicht zuverlässig darauf zugreifen.
Wenn es in deinem Test funktioniert, ist es eher ein glücklicher Zufall.
Deshalb mein Vorschlag mit der globalen Variable, da sie sich zuverlässig um den Speicherplatz kümmert, solange sie nicht bei einem weiteren Aufruf der Funktion neu belegt wird.

PascalDragon
Beiträge: 834
Registriert: Mi 3. Jun 2020, 07:18
OS, Lazarus, FPC: L 2.0.8, FPC Trunk, OS Win/Linux
CPU-Target: Aarch64 bis Z80 ;)
Wohnort: München

Re: String to PWideChar

Beitrag von PascalDragon »

michael76 hat geschrieben:
Di 16. Mai 2023, 20:29
Ich glaube die Lösung gefunden zu haben. Die Länge vom WideChar schon bei der Initialisierung definiert.

Code: Alles auswählen

function ReadString(Dateiname : PWideChar; Number : Integer; Kennung : PWideChar) : PWideChar; stdcall;
var
  ini: TIniFile;
  Filename : String;
  Position : String;
  INI_Kennung: String;
  inistring : String;
  return  : array[0..254] of WideChar;
begin
  Filename := 'C:\' + WideCharToString(Dateiname) + '.ini';
  ini := TIniFile.Create(Filename);
  Position := 'Position' + IntToStr(Number);
  INI_Kennung := WideCharToString(Kennung);
  inistring := ini.ReadString (Position, INI_Kennung,  'Nix');
  ini.Free;

  return := inistring;
  result := @return;
end;
exports
ReadString;                 
Mach sowas nicht! Wie Jorg3000 geschrieben hat, werden die lokalen Variablen beim Verlassen der Funktion freigegeben und dass es für dich dennoch funktioniert liegt nur daran, dass der Speicher noch nicht überschrieben wurde, was er wird sobald die nächste Funktion aufgerufen wird, die genügend lokale Variablen benötigt.

Stattdessen musst du herausfinden, ob die Bibliotheks API, die du implementierst es vorsieht entweder eine Funktion deiner Bibliothek aufzurufen, um Speicher freizugeben oder davon ausgeht, dass du eine spezifische OS-Funktion zu reservieren des Speichers aufrufst.

Im ersten Fall legst du ein PWideChar an, den du zurückgibst und dann wieder in der anderen Funktion freigibst:

Code: Alles auswählen

function ReadString(...): PWideChar;
   // ...
   w: WideString;
begin
  // ...
  // um sicherzustellen, dass wir die finale Länge haben
  w := inistring;
  // das + 1 ist für das finale #0
  Result := GetMem((Length(w) + 1) * SizeOf(WideChar));
  Move(@w[1], Result, (Length(w) + 1) * SizeOf(WideChar));
end;

// wie auch immer die Funktion dann genannt werden muss
procedure FreeMemory(aArg: Pointer);
begin
  FreeMem(aArg);
end;
Im zweiten Fall musst du den Speicher analog zur ersten Variante allokieren allerdings mit der entsprechenden OS Funktion (zum Beispiel malloc aus der C Bibliothek) und der Nutzer deiner Bibliothek ruft dann die entsprechende Funktion zur Freigabe auf (im Fall von malloc ist das free).
FPC Compiler Entwickler

Antworten