AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Sprachen und Entwicklungsumgebungen FreePascal FreePascal Unicode und WideChar API-Funktionen
Thema durchsuchen
Ansicht
Themen-Optionen

Unicode und WideChar API-Funktionen

Ein Thema von Dalai · begonnen am 27. Mär 2023 · letzter Beitrag vom 12. Apr 2023
Antwort Antwort
Seite 1 von 2  1 2      
Benutzerbild von Dalai
Dalai
Online

Registriert seit: 9. Apr 2006
1.682 Beiträge
 
Delphi 5 Professional
 
#1

Unicode und WideChar API-Funktionen

  Alt 27. Mär 2023, 16:37
Hallo ihr .

Aus bestimmten Gründen versuche ich mich derzeit an Free Pascal. Leider blieb ich sehr schnell an der Thematik Unicode und WideChar API-Funktionen hängen, und auch die vielfältigen Angaben im Internet helfen mir nicht dabei, die Sache für mich zufriedenstellend - mit möglichst wenigen Änderungen am Code - zu lösen.

Gegeben sei folgender, auf das Wesentliche reduzierte Beispiel-Code:
Delphi-Quellcode:
program project1;

{$MODE DELPHI}

uses Classes, SysUtils, CustApp, Windows;

type
  TMyApplication = class(TCustomApplication)
  private
    FStr: string;
  [...]
  end;

procedure TMyApplication.DoRun;
begin
  [...]
  SetLength(FStr, 32768);
  SetLength(FStr, ExpandEnvironmentStrings(PChar('%SystemRoot%\System32'), @FStr[1], Length(FStr))-1);
  WriteLn(FStr);
  ReadLn;
end;
Funktioniert einwandfrei. Aber es wird die ANSI-Variante der API-Funktion (ExpandEnvironmentStringsA) importiert. Nun wollte ich die Sache unicodefähig machen, also dachte ich mir, setze ich {$MODE DELPHIUNICODE} . Leider Fehlanzeige:
Code:
Compile Project, Mode: Debug, Target: project1.exe: Exit code 1, Errors: 1, Warnings: 2
project1.lpr(36,14) Warning: Implicit string type conversion from "AnsiString" to "UnicodeString"
project1.lpr(38,44) Warning: Implicit string type conversion with potential data loss from "UnicodeString" to "AnsiString"
project1.lpr(52,50) Error: Incompatible type for arg no. 1: Got "PWideChar", expected "PChar"
(Der Error wird an der Stelle des Calls der API-Funktion gemeldet.) Soweit ich das bisher verstandenn habe, liegt das daran, dass nur die eigene Unit in Bezug auf Strings und PChars umdefiniert wird, die von FPC mitgebrachten Units aber nicht. Es wird also weiterhin ExpandEnvironmentStringsA benutzt.

Wie kann man das lösen bzw. was ist der empfohlene Weg, dass der Code sowohl in Delphi als auch FPC verwendbar ist? Es kann ja nicht Sinn der Sache sein, den Code mit expliziten Aufrufen der Wide-Funktionen und - bei umfangreicherem Code - zahllosen IFDEFs zuzupflastern...

Grüße
Dalai
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.184 Beiträge
 
Delphi 12 Athens
 
#2

AW: Unicode und WideChar API-Funktionen

  Alt 27. Mär 2023, 17:34
Dann mach es doch explizit als Unicode?

Delphi-Quellcode:
  private
    FStr: UnicodeString; // oder WideString

  SetLength(FStr, ExpandEnvironmentStringsW(PWideChar('%SystemRoot%\System32'), @FStr[1], Length(FStr))-1);

Als ich zuletzt in FreePascal / Lazarus schautet, dann war da nichts mit "Unicode".
Oft wurde aber mit UTF-8 in einem "AnsiString" gearbeitet.

Windows benutzt aber UTF-16 bzw. früher UCS-2.
$2B or not $2B
  Mit Zitat antworten Zitat
Benutzerbild von Dalai
Dalai
Online

Registriert seit: 9. Apr 2006
1.682 Beiträge
 
Delphi 5 Professional
 
#3

AW: Unicode und WideChar API-Funktionen

  Alt 27. Mär 2023, 21:46
Dann mach es doch explizit als Unicode?
Das geht zwar, dann müsste ich aber sämtliche Variablen umstellen und alle API-Aufrufe ersetzen - bei beiden mit einigen Ausnahmen, weil es API-Funktionen gibt, die ANSI-Chars erwarten bzw. ausgeben. Den Aufwand, diese Änderungen in wahrscheinlich einem Dutzend eigener Units durchzuführen, will ich vermeiden. Zumal dabei die Gefahr besteht, im alten Delphi funktionierenden Code zu vermurksen.

Zitat:
Als ich zuletzt in FreePascal / Lazarus schautet, dann war da nichts mit "Unicode".
Auch wenn es den Mode jetzt gibt, nützt das IMO überhaupt gar nichts, wenn nicht auch die mitgelieferten Units mit umgestellt sind/werden. Praktisch verwendbar ist diese nur partielle Umstellung nicht. Aber vielleicht gibt es noch einen mir bislang unbekannten Weg, der keine umfangreichen Umbauarbeiten erfordert.

Grüße
Dalai
  Mit Zitat antworten Zitat
Benutzerbild von Dalai
Dalai
Online

Registriert seit: 9. Apr 2006
1.682 Beiträge
 
Delphi 5 Professional
 
#4

AW: Unicode und WideChar API-Funktionen

  Alt 29. Mär 2023, 00:15
Das Eingangsbeispiel ist wohl etwas zu knapp ausgefallen ist. Hier ein etwas längeres, das die Problematik (hoffentlich) besser verdeutlicht:
Delphi-Quellcode:
program project1;

{$MODE DELPHI}

uses Classes, SysUtils, CustApp, Windows, IniFiles;

type
  TMyApplication = class(TCustomApplication)
  private
    FStr: WideString;
    FValue: WideString;
    FFileName: WideString;
  [...]
  end;

procedure TMyApplication.DoRun;
begin
  [...]
  FFileName:= 'C:\irgendeine.ini';
  FIni:= TIniFile.Create(FFileName); // <--
  try
    FValue:= FIni.ReadString('foo', 'bar', 'blub'); // <--
    SetLength(FStr, 32768);
    SetLength(FStr, ExpandEnvironmentStringsW(PWideChar(FValue), @FStr[1], Length(FStr))-1);
  finally
    FIni.Free;
  end;
  WriteLn(FStr);
  ReadLn;
end;
Die markierten Zeilen werfen folgende Warnungen:
Code:
Compile Project, Mode: Debug, Target: project1.exe: Success, Warnings: 2
project1.lpr(55,35) Warning: Implicit string type conversion with potential data loss from "WideString" to "AnsiString"
project1.lpr(57,18) Warning: Implicit string type conversion from "AnsiString" to "WideString"
Das heißt, selbst wenn ich explizit WideString nutze, renne ich gegen eine andere Wand. Das muss doch besser gehen.

Grüße
Dalai
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.184 Beiträge
 
Delphi 12 Athens
 
#5

AW: Unicode und WideChar API-Funktionen

  Alt 29. Mär 2023, 01:24
Bei implizitem Cast wird gemeckert,

aber bei expliziten Casts nicht:
Delphi-Quellcode:
  FIni:= TIniFile.Create(AnsiString(FFileName));
  try
    FValue:= WideString(FIni.ReadString('foo', 'bar', 'blub'));
Das Zweite wäre kein Problem, da ja Klein zu Groß und es somit rein passen würde.

Beim Ersten, sieht es anders aus ... also problematisch, wenn Unicodezeichen im Dateiname vorkommen würden, welche außerhalb des jeweiligen ANSI-Zeichenraumes liegen.


ABER, ist das wirklich AnsiString, oder nicht vielleicht doch z.B. UTF-8 String?

Ich dachte der FPC hätte in seiner RTL schon "Unicode" und eben nicht ANSI.
Wenn z.B. ein UTF8-String, dann wäre es schon unpraktisch, wenn FPC keine automatische Konvertierung zwischen den String-Typen bietet.


Wie genau sind denn TIniFile.Create und TIniFile.ReadString "wirklich" deklariert?
https://www.freepascal.org/docs-html...eadstring.html

Wenn tatsächlich "String" und String = AnsiString, dann ... Wo bitte hat FPC denn seine unicode-fähige INI-Klasse versteckt?
$2B or not $2B

Geändert von himitsu (29. Mär 2023 um 01:27 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von jaenicke
jaenicke

Registriert seit: 10. Jun 2003
Ort: Berlin
9.648 Beiträge
 
Delphi 11 Alexandria
 
#6

AW: Unicode und WideChar API-Funktionen

  Alt 29. Mär 2023, 11:02
Es gibt bei Lazarus eine Includedatei für Ansi und eine für Unicode für den Zugriff auf die API. Ich habe aber keine Ahnung, wie man die Unicodeversion aktiviert. Der Schalter in den Projekteinstellungen scheint es nicht zu sein.

Ich habe aktuell Lazarus nicht installiert und kann nicht nachschauen. Ich hatte aber mal gesucht.

Soweit ich das sehe, wird intern zwar UTF-8 verwendet, aber statt dieses dann umgewandelt in UTF-16 automatisch an die Unicode-API zu füttern, wird offenbar (mit den Standarddirektiven) fest Ansi verwendet.

Das war bei Delphi 7, an das ja auch die Oberfläche von Lazarus angelehnt ist, aber auch nicht anders. Insofern...
Sebastian Jänicke
AppCentral
  Mit Zitat antworten Zitat
Benutzerbild von Dalai
Dalai
Online

Registriert seit: 9. Apr 2006
1.682 Beiträge
 
Delphi 5 Professional
 
#7

AW: Unicode und WideChar API-Funktionen

  Alt 4. Apr 2023, 21:02
Bitte entschuldigt die längere Funkstille meinerseits. Die vergangenen Tage hab ich damit verbracht, einen Teil meiner Units für Free Pascal umzuschreiben. Leider sind die Ergebnisse alles andere als zufriedenstellend. Die Sache ist so undurchsichtig und stellenweise widersprüchlich, dass bei mir einige Fragen aufgetaucht sind und ich die Hoffnung habe, dass jemand sie eindeutig beantworten kann.
  • Nehmen wir diese Funktion aus source\rtl\objpas\sysutils\sysutils.inc:
    Delphi-Quellcode:
    function GetModuleName(Module: HMODULE): string;
    var
      ResultLength, BufferLength: DWORD;
      Buffer: UnicodeString;
    begin
      BufferLength := MAX_PATH div 2;
      repeat
        Inc(BufferLength, BufferLength);
        SetLength(Buffer, BufferLength);
        ResultLength := GetModuleFileNameW(Module, Pointer(Buffer), BufferLength);
        if ResultLength = 0 then
          Exit('');
      until ResultLength < BufferLength;
      SetLength(Buffer, ResultLength);
      Result := Buffer;
    end;
    Warum wird hier ein UnicodeString an einen String (laut Hint in der Lazarus IDE ist String = AnsiString) zugewiesen? Während der Umstellung meiner Units sind mir noch andere Funktionen aufgefallen, die nach demselben Prinzip arbeiten.

    Aus Interesse hab ich die Funktion unverändert in meine Projektdatei kopiert (und umbenannt), und wie erwartet wird die Zuweisung an Result mit der entsprechenden Warnung bemängelt. Andererseits steht im Wiki-Artikel, dass man eine solche Zuweisung machen kann - dort string an UnicodeString, aber das Prinzip bleibt - und der Compiler sich um die Konvertierung kümmert:
    Zitat:
    When a parameter type is a pointer PWideChar, you need a temporary UnicodeString variable. Assign your String to it. The compiler then converts its data.
    Entweder ist die Warnung Banane und die Konvertierung erfolgt sauber, oder die Warnung ist berechtigt und die Zuweisung führt potentiell zu Datenverlust. Beides ist unbefriedigend, letzteres sogar fatal. Was ist richtig?

  • Was passiert bei einem direkten (harten) Cast von UnicodeString nach String, vor allem mit enthaltenen Unicode-Zeichen außerhalb der eigenen ANSI-Codepage? Werden die Zeichen verstümmelt oder landet das zweite Byte des Zeichens unverändert im (Ansi)String? Kommen die Zeichen wieder sauber heraus, wenn ein UnicodeString an einen String und dieser danach wieder an einen UnicodeString zugewiesen wird, oder sind die Zeichen danach kaputt?
  • Anschließend an den vorherige Punkt: Sollte ich Casts benutzen oder Konvertierungsfunktionen?
  • Sofern die Antwort auf die vorherige Frage "Konvertierungsfunktionen" lautet, sollte ich die Unit LazUnicode verwenden oder könnte ich auch simple Funktionen wie die im Thema Delphi7 mit nur einer Unit Unicode fähig machen zu findenden benutzen? Wenn Unit LazUnicode, sollte ich UseUTF16 definieren/verwenden oder nicht?

    Wobei ich mich schon frage, wozu die Unit LazUnicode gut sein soll, wenn doch der Compiler (laut Wiki) die Konvertierung durchführt... Wie schon gesagt, es ist undurchsichtig.

  • Apropos Free Pascal Wiki. Dort ist der Satz zu finden
    Zitat:
    There is also a test program LazUnicodeTest in components/lazutils/test directory.
    Im ganzen Verzeichnis von Lazarus gibt es keine Datei mit einem solchen Namen. Nach einiger Sucherei hab ich das gemeinte Projekt gefunden unter components\lazutils\examples\LazUnicode\LazUnicode Console.lpi.
    Im Wiki steht zuvor noch der Satz
    Zitat:
    Currently the {$ModeSwitch UnicodeStrings} can be tested by defining "UseUTF16".
    Also dachte ich, ich geh in den Projektoptionen meines Projekts zu "Custom Options" > Defines und setze dort UseUTF16. Hat keine Wirkung. Ist meine Annahme falsch, dass die dort definierten Defines für alle Units des Projekts gelten?

    Erst nach dem Auffinden des Test-/Beispielprojekts konnte ich sehen, dass UseUTF16 dort im Bereich "Additions and Overrides" der Projektoptionen definiert wird. Solche Ungenauigkeiten bzw. fehlerhafte Angaben nerven und sind für Neulinge in Lazarus bzw. Free Pascal nicht gerade förderlich.
Es gibt noch Klassen und Funktionen (TIniFile, TFileStream, FormatDateTime, AnsiQuotedStr), für die ich keine unicodefähigen Varianten gefunden habe bzw. Konvertierungsmöglichkeiten brauche.

Grüße
Dalai
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.184 Beiträge
 
Delphi 12 Athens
 
#8

AW: Unicode und WideChar API-Funktionen

  Alt 4. Apr 2023, 21:34
Also, wenn man mit Pointer rumpfuscht, anstatt PWideChar zu benutzen ... wenn irgendwann der Typ nicht mehr stimmt, dann beschwere dich bitte nicht, wenn der Compiler dir nichts sagen wird.

Warum wird hier ein UnicodeString an einen String (laut Hint in der Lazarus IDE ist String = AnsiString) zugewiesen?
Jemand hat vergessen zu sagen in welcher Zeile.
Ich rate mal.
Die Letzte? (mit dem Result:=)
Und nun überleg mal was du dort machst.

Delphi-Quellcode:
SetLength(Buffer, GetModuleFileNameW(Module, nil, 0));
GetModuleFileNameW(Module, PWideChar(Buffer), Length(Buffer)+1);
Aber wenn du sowie so kein Unicode haben willst, dann

Delphi-Quellcode:
function GetModuleName(Module: HMODULE): string; // oder eben UnicodeString und nachfolgend PWideChar
begin
  SetLength(Result, GetModuleFileName(Module, nil, 0));
  GetModuleFileName(Module, PChar(Result), Length(Result)+1);
end;
Im Delphi war String früher AnsiString und GetModuleFileName ein Alias für GetModuleFileNameA, bzw. PChar ein PAnsiChar,
und nun ist String ein UnicodeString und GetModuleFileName ein Alias für GetModuleFileNameW mit PWideChar.
Eventuell gibt es sowas mit dem genannten {$ModeSwitch UnicodeStrings} auch für Lazarus?



Delphi-Quellcode:
Result := Buffer;
Result := String(Buffer);
Mit Cast oder implitzt kommt erstmal das Gleiche bei raus, also vom Inhalt ... nur das Letzteres eben keine Cast-Warnung wirft.

Ich weiß nicht wie Lazarus/FPC das macht.
Vermutlich wie Delphi früher. Da wurde die Codepage des Types verwendet, also von der Variable.
Delphi seit 2009, hat die Codepage zusätzlich im String gespeichert und Verwendet vorzugsweise Dieses. (auch wenn man mal z.B. UTF-8 in einem "AnsiString" drin hat und nicht den UTF8String benutzte)



Zitat:
Currently the {$ModeSwitch UnicodeStrings} can be tested by defining "UseUTF16".
Vielleicht meinen die es andersrum?

vorher irgendwo {$ModeSwitch UnicodeStrings}
und später dann mit {$IFDEF UseUTF16} testen.
$2B or not $2B

Geändert von himitsu ( 5. Apr 2023 um 03:25 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von KodeZwerg
KodeZwerg

Registriert seit: 1. Feb 2018
3.691 Beiträge
 
Delphi 11 Alexandria
 
#9

AW: Unicode und WideChar API-Funktionen

  Alt 4. Apr 2023, 23:43
{$mode delphiunicode} sollte dir dabei gut weiterhelfen das alles so bleiben kann.
oder ein anderer ansatz, {$modeswitch m_default_unicodestring}
Gruß vom KodeZwerg
  Mit Zitat antworten Zitat
Benutzerbild von Dalai
Dalai
Online

Registriert seit: 9. Apr 2006
1.682 Beiträge
 
Delphi 5 Professional
 
#10

AW: Unicode und WideChar API-Funktionen

  Alt 5. Apr 2023, 17:19
Also, wenn man mit Pointer rumpfuscht, anstatt PWideChar zu benutzen ... wenn irgendwann der Typ nicht mehr stimmt, dann beschwere dich bitte nicht, wenn der Compiler dir nichts sagen wird.
Ich dachte, es wäre klar gewesen (vor allem durch Angabe der Quelltextdatei), dass diese Funktion exakt so in den Sourcen von FPC steht. Deswegen auch meine Frage, warum die Funktion so aussieht.

Zitat:
Jemand hat vergessen zu sagen in welcher Zeile.
Ich rate mal.
Die Letzte? (mit dem Result:=)
Es gibt in dieser Funktion nur eine Zeile, wo ein UnicodeString an einen String zugewiesen wird - die Zuweisung an Result.

Zitat:
Und nun überleg mal was du dort machst.
Ich hab den Code nicht geschrieben, er ist 1:1 aus den Sourcen von FPC übernommen. Die Frage war, warum das dort so steht und ob die Warnung (bzgl. möglichem Datenverlust wegen UnicodeString -> String), die bei Übernahme der Funktion in eine eigene Unit auftaucht, gerechtfertigt ist.

Es sei noch ergänzt, dass ich die Funktion nur aus Neugier und Verwunderung ins eigene Projekt kopiert habe. Beim Lesen der Funktion tauchten sofort die Fragen auf, warum Pointer statt PWideChar benutzt wird und warum ohne Cast/Konvertierung ein UnicodeString an String zugewiesen wird. Ich wollte herausfinden, ob bzgl. des (Unicode)Strings eine Warnung auftaucht oder nicht. Sie taucht auf, und nun würde ich gern wissen, ob die berechtigt ist oder nicht.

Zitat:
Im Delphi war String früher AnsiString und GetModuleFileName ein Alias für GetModuleFileNameA, bzw. PChar ein PAnsiChar,
und nun ist String ein UnicodeString und GetModuleFileName ein Alias für GetModuleFileNameW mit PWideChar.
Das ist mir alles bekannt. Hier im Thread geht's aber explizit um Free Pascal.

Zitat:
Eventuell gibt es sowas mit dem genannten {$ModeSwitch UnicodeStrings} auch für Lazarus?
Das hatte ich ja am Anfang probiert, zwar mit {$Mode DelphiUnicode} , aber das Ergebnis ist dasselbe wie mit {$ModeSwitch UnicodeStrings} . Dabei hab ich festgestellt, dass die Funktionen/Klassen aus der FPC RTL eben nicht alle mit UnicodeString verfügbar sind. Das ist ja der Grund für den Thread hier und meine Fragen.

Zitat:
Delphi-Quellcode:
Result := Buffer;
Result := String(Buffer);
Mit Cast oder implitzt kommt erstmal das Gleiche bei raus, also vom Inhalt ... nur das Letzteres eben keine Cast-Warnung wirft.
Dass die Warnung unterdrückt wird, ist mir klar. Die Frage ist, ob man einfach so casten kann oder ob dadurch Zeichen kaputtgehen. Die Konvertierungsfunktionen (spätestens MultiByteToWideChar/WideCharToMultiByte in der Win32 API) existieren ja sicherlich auch nicht ohne Grund.

Zitat:
Ich weiß nicht wie Lazarus/FPC das macht.
Schade. Ich hoffe, jemand kann etwas zu diesem Punkt sagen.

Zitat:
Delphi seit 2009, hat die Codepage zusätzlich im String gespeichert und Verwendet vorzugsweise Dieses.
String ist doch seit 2009 ein UnicodeString. Inwiefern spielt dabei eine Codepage eine Rolle? Oder meinst du AnsiString?

Zitat:
Vielleicht meinen die es andersrum?

vorher irgendwo {$ModeSwitch UnicodeStrings}
und später dann mit {$IFDEF UseUTF16} testen.
Das glaube ich nicht. In der Unit LazUnicode kann man am Anfang Folgendes finden:
Delphi-Quellcode:
// For testing the UTF16 version.
{$IF DEFINED(FPC) and DEFINED(UseUTF16)}
 {$ModeSwitch UnicodeStrings}   // Sets also FPC_UNICODESTRINGS.
{$ENDIF}

{$IF DEFINED(FPC_UNICODESTRINGS) or not DEFINED(FPC)}
 {$DEFINE ReallyUseUTF16}       // FPC with UTF-16 or Delphi
{$ENDIF}
Und im Code dieser Unit wird dann an bestimmten Stellen {$IFDEF ReallyUseUTF16 benutzt.

Grüße
Dalai
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:33 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz