AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Win32/Win64 API (native code) Delphi Windows Codepages - Kernel32.dll - EnumSystemCodePages

Windows Codepages - Kernel32.dll - EnumSystemCodePages

Ein Thema von arcticwolf · begonnen am 21. Jun 2023 · letzter Beitrag vom 28. Jun 2023
Antwort Antwort
Benutzerbild von arcticwolf
arcticwolf

Registriert seit: 3. Aug 2021
Ort: Erfurt
41 Beiträge
 
Delphi 11 Alexandria
 
#1

AW: Windows Codepages - Kernel32.dll - EnumSystemCodePages

  Alt 22. Jun 2023, 15:41
Vielen Dank für die Erklärungen. Ich denke ich habe das Prinzip von statischen Klassen beziehungsweise von statischen Funktionen einer Klasse verstanden.
Hier eine verkürzte Version meines Codes:

Delphi-Quellcode:
Type TWindowsCodepages = class
     private
       class var FList : TStrings;
     protected
       class function GetCodepageName(Codepage:Cardinal) : String; static;
       class function EnumCodePagesProc(CodePage:PWideChar) : Boolean; static; stdcall;
       class function GetList : String; static;
       class procedure SetList(Const Value:String); static;
     public
       class constructor CreateClass;
       class destructor DestroyClass;
       class property List:String Read GetList Write SetList;
       class function RetrieveSupported : Boolean; static;
     end;

class function TWindowsCodepages.GetCodepageName(Codepage:Cardinal) : String;
var CpInfoEx : TCPInfoEx;
begin
  Result := '';
  if IsValidCodePage(Codepage) then
  begin
    GetCPInfoEx(Codepage, 0, CpInfoEx);
    Result := Format('%s', [CpInfoEx.CodePageName]);
  end;
end;

class function TWindowsCodepages.EnumCodePagesProc(CodePage:PWideChar) : Boolean;
Var CP : cardinal;
begin
  Result := False;
  if (CodePage<>Nil) then
  begin
    if (Length(Codepage)>0) then
    begin
      CP := StrToIntDef(CodePage,0);
      if (CP>0) then
      begin
        FList.Text := FList.Text + GetCodepageName(CP);
        Result := True;
      end;
    end;
  end;
end;

class function TWindowsCodepages.GetList : String;
begin
  Result := FList.Text;
end;

class procedure TWindowsCodepages.SetList(Const Value:String);
begin
  FList.Text := Value;
end;

class constructor TWindowsCodepages.CreateClass;
begin
  FList := TStringList.Create;
  FList.Delimiter := ';';
end;

class destructor TWindowsCodepages.DestroyClass;
begin
  FList.Free;
end;

class function TWindowsCodepages.RetrieveSupported : Boolean;
begin
  Result := EnumSystemCodePagesW(@EnumCodePagesProc,CP_SUPPORTED);
end;

Procedure DoSomething;
begin
  TWindowsCodepages.RetrieveSupported;
  Writeln(TWindowsCodepage.List);
end;
Da ich die Unit mit dem Code sowohl unter Win32, als auch unter Win64 nutzen möchte, ist noch wichtig zu erwähnen, dass dann auch bei der Callback-Funktion (EnumCodePagesProc) das "stdcall" dahinter stehen muss, sonst knallt es bei der Win32-Version.

Trotzdem stellt sich mir die Frage, wo der Vorteil ist? Die statische Klasse erstellt auch eine "globale" Variable, ja OK, nicht wirklich global, weil ich nur über die Klasse darauf zugreifen kann. Aber sie wird beim Start des Programmes über CreateClass erzeugt und erst beim Beenden des Programmes über DestroyClass wieder freigegeben und belegt somit für die komplette Laufzeit Speicher. Auch dann wenn die Klasse nur in irgendeinem Modul benötigt wird, das vielleicht gar nicht vom User verwendet wird. Gibt es da nicht eine elegantere Lösung für API Callback-Funktionen, die nur dann Speicher belegen wenn sie aufgerufen werden, also ohne globale Variable?
Wolfgang
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.643 Beiträge
 
Delphi 12 Athens
 
#2

AW: Windows Codepages - Kernel32.dll - EnumSystemCodePages

  Alt 22. Jun 2023, 16:30
Trotzdem stellt sich mir die Frage, wo der Vorteil ist? Die statische Klasse erstellt auch eine "globale" Variable, ja OK, nicht wirklich global, weil ich nur über die Klasse darauf zugreifen kann. Aber sie wird beim Start des Programmes über CreateClass erzeugt und erst beim Beenden des Programmes über DestroyClass wieder freigegeben und belegt somit für die komplette Laufzeit Speicher. Auch dann wenn die Klasse nur in irgendeinem Modul benötigt wird, das vielleicht gar nicht vom User verwendet wird. Gibt es da nicht eine elegantere Lösung für API Callback-Funktionen, die nur dann Speicher belegen wenn sie aufgerufen werden, also ohne globale Variable?
Also, es wird ja lediglich der Speicher für ein dynamisches Array - also ein Pointer - verwendet. Das sind in 64-Bit gerade mal 8 Byte. Ich glaube kaum, dass das ins Gewicht fällt.

Hier ein alternativer Ansatz, der auch mehrere Threads berücksichtig. Basiert noch auf deinem ursprünglichen Code für die Formatierung. (Vermutlich nicht relevant, aber der Form halber: StrToUIntDef)
Delphi-Quellcode:
type
  TCodePageEnumerator = class
  strict private
  class threadvar
    CodePageNumbers: TList<Cardinal>;
    class function EnumCodePagesProc(CodePage: PWideChar): Cardinal; static; stdcall;
  strict protected
    class function FormatCodePageName(AValue: Cardinal; const AName: string): string; virtual;
    class function GetCodePageName(CP:Cardinal): String;
  public
    class procedure GetCodepageList(CodePageList: TStrings);
  end;

class procedure TCodePageEnumerator.GetCodepageList(CodePageList: TStrings);
begin
  CodePageNumbers := TList<Cardinal>.Create;
  try
    CodePageList.Clear;
    EnumSystemCodePages(@EnumCodePagesProc, CP_SUPPORTED);
    for var codePageNumber in CodePageNumbers do
    begin
      if codePageNumber > 0 then
        CodepageList.Add(Format('%.5d: %s', [codePageNumber, GetCodePageName(codePageNumber)]));
    end;
  finally
    CodePageNumbers.Free;
    CodePageNumbers := nil;
  end;
end;

class function TCodePageEnumerator.EnumCodePagesProc(CodePage: PWideChar): Cardinal;
Var
  Cp: Cardinal;
begin
  Result := 0;
  if CodePageNumbers = nil then Exit;
  if (CodePage <> Nil) then
  begin
    if (Length(CodePage) > 0) then
    begin
      Cp := StrToUIntDef(CodePage, 0);
      if (Cp > 0) then
      begin
        CodePageNumbers.Add(Cp);
        Result := 1;
      end;
    end;
  end;
end;

class function TCodePageEnumerator.FormatCodePageName(AValue: Cardinal; const AName: string): string;
begin
  Result := AName.Remove(0, AValue.ToString.Length + 1).TrimLeft.Trim(['(', ')']);
end;

class function TCodePageEnumerator.GetCodePageName(CP:Cardinal): String;
var CpInfoEx : TCPInfoEx;
begin
  Result := '';
  if IsValidCodePage(Cp) then
  begin
    GetCPInfoEx(Cp, 0, CpInfoEx);
    Result := FormatCodePageName(CP, CpInfoEx.CodePageName);
  end;
end;
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
TiGü

Registriert seit: 6. Apr 2011
Ort: Berlin
3.074 Beiträge
 
Delphi 10.4 Sydney
 
#3

AW: Windows Codepages - Kernel32.dll - EnumSystemCodePages

  Alt 22. Jun 2023, 16:52
Sollte die EnumCodePagesProc nicht ein BOOL als Rückgabewert haben?
https://learn.microsoft.com/en-us/pr...317809(v=vs.85)
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.643 Beiträge
 
Delphi 12 Athens
 
#4

AW: Windows Codepages - Kernel32.dll - EnumSystemCodePages

  Alt 22. Jun 2023, 17:06
Deklariert ist sie als TFarProc = Pointer. Solange der Rückgabewert binär dem erwarteten True oder False entspricht, sollte das kein Problem sein. Aber formal hast du recht.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

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

AW: Windows Codepages - Kernel32.dll - EnumSystemCodePages

  Alt 22. Jun 2023, 17:51
Njaaaaaaaaaa, ...

Boolean und auch ByteBool sind Beide bloß je 1 Byte groß (das Eine mit $01 und das Andere mit -1 aka $FF standardmäßig als "True"),
aber wenn EnumSystemCodePages das aber als BOOL aka LongBool auswertet, dann könnte es passieren, dass es eine Fehlerkennung gibt.

Wenn die Rückgabe wirklich nur das erste Byte (AL) und nicht das komplette Register (EAX) auf 0 setzt.
z.B. in den höheren Bytes steht irgendwas mit <> 0, es wird nur das erste Byte auf False (0) gesetzt, dann bliebe es insgesamt dennoch auf True (<>0)





PS: Da hier die ganze Klasse "statisch" ist und es praktisch nie eine Instanz gibt,
könnte man sich überlegen, eine Warnung vom Compiler zu benutzen, falls doch irgendwo im Code versucht wird eine Instanz zu erstellen.
"class static" oder Sowas gibt es nicht, aber "class abstract" sollte auch helfen. (WOBEI es das grade irgendwie nicht macht )

* class abstract = diese Klasse muß erst abgeleitet werden, bevor man davon eine Instanz erstellen darf
* class sealed = ich bin der Letzte ... von mir das niemand mehr ableiten
https://docwiki.embarcadero.com/RADS...jekte_(Delphi)

Wie wenn man ausversehn versucht ein TStream.Create oder TStrings.Create zu machen, anstatt z.B. TFileSteam oder TStringList,
wo der Compiler dir eine Abstract-Warnun um die Ohren haut.

Delphi-Quellcode:
type
  TTest = class abstract
  private
    class var Abc: Integer;
  public
    class procedure Test; {mit oder ohne static}
  end;

procedure TForm25.FormCreate(Sender: TObject);
begin
  TTest.Test; // geht
  {var X :=} TTest.Create; // hier sollte der Compiler eigentlich meckern (dachte ich)
end;
Für reine "statischen" Klassen verwende ich inzwischen auch stattdessen gern Records.
Delphi-Quellcode:
type
  TTest = record
  private {PS: protected und published gibt es hier nicht}
    class var FAbc: Integer;
  public
    class procedure Test; static;
  end;
Und die "class var" mache ich bei mir als eigenständigen "Block" (finde es für mich so übersichtlicher)
Delphi-Quellcode:
  TTest = record {aber auch bei class}
  private class var
    FAbc: Integer;
  private {"private var" wäre hier auch bmöglich}
    FDef: Integer;
  public
    class procedure Test; static;
  end;
Ein Therapeut entspricht 1024 Gigapeut.
  Mit Zitat antworten Zitat
Benutzerbild von arcticwolf
arcticwolf

Registriert seit: 3. Aug 2021
Ort: Erfurt
41 Beiträge
 
Delphi 11 Alexandria
 
#6

AW: Windows Codepages - Kernel32.dll - EnumSystemCodePages

  Alt 22. Jun 2023, 18:16
Zitat:
Vermutlich nicht relevant, aber der Form halber: StrToUIntDef
Du hast natürlich Recht, vielen Dank für den Hinweis.

Zitat:
Result := AName.Remove(0, AValue.ToString.Length + 1).TrimLeft.Trim(['(', ')']);
Eleganter Ansatz, aber funktioniert so leider nicht, weil die Funktion zum Beispiel "20290 (IBM EBCDIC - Japanisch (erweitertes Katakana))" liefert. Dann macht er hinten beide Klammern weg. Ich werde meinen Code aber an der Stelle aber noch ein bisschen smarter machen.

Zitat:
Sollte die EnumCodePagesProc nicht ein BOOL als Rückgabewert haben?
Ja, ist mir auch schon aufgefallen, habe ich korrigiert. -> Müssen wir uns nicht darüber streiten

Zitat:
Für reine "statischen" Klassen verwende ich inzwischen auch stattdessen gern Records.
Absolut, auf dem Trichter war ich auch schon. Erübrigt sich dann nicht auch die Frage um die Compiler-Warnung wenn jemand eine Instanz erstellt? Da muss ich noch einmal darüber nachdenken, beziehungsweise ausprobieren was da möglich oder sinnvoll ist.


Ich feile noch ein bisschen an der Klasse und werde diese in Kürze als Teil einer größeren Bibliothek auf GitHub veröffentlichen. Sobald die Bibliothek veröffentlich ist, werde ich diese hier auch noch einmal verlinken, falls jemand nach einer CodepageListe basierend auf EnumSystemCodePages sucht.
Wolfgang
  Mit Zitat antworten Zitat
Benutzerbild von arcticwolf
arcticwolf

Registriert seit: 3. Aug 2021
Ort: Erfurt
41 Beiträge
 
Delphi 11 Alexandria
 
#7

AW: Windows Codepages - Kernel32.dll - EnumSystemCodePages

  Alt 27. Jun 2023, 15:19
Wie versprochen, hier nun mein Code wie ich ihn letzendlich verwende:
Delphi-Quellcode:
Type TWindowsCodepages = record
     strict private
       class threadvar FList: TStrings;
       class function EnumCodePagesProc(CodePage:PWideChar) : Boolean; static; stdcall;
       class function GetCodepageName(Codepage:Cardinal) : String; static;
     public
       class function GetInstalled(Var CodePageList:TStrings) : Boolean; static;
       class function GetSupported(Var CodePageList:TStrings) : Boolean; static;
       class function GetName(CodePage: Cardinal) : String; static;
     end;

class function TWindowsCodepages.GetCodepageName(Codepage:Cardinal) : String;
var CpInfoEx : TCPInfoEx;
begin
  Result := '';
  if IsValidCodePage(Codepage) then
  begin
    if (GetCPInfoEx(Codepage, 0, CpInfoEx)) then
    begin
      Result := CpInfoEx.CodePageName;
      Result := Result.Substring(7,Length(Result)-8);
    end;
  end;
end;

class function TWindowsCodepages.EnumCodePagesProc(CodePage:PWideChar) : Boolean;
Var CP : cardinal;
begin
  Result := False;
  if (CodePage<>Nil) then
  begin
    if (Length(Codepage)>0) then
    begin
      CP := StrToUIntDef(CodePage,0);
      if (CP>0) then
      begin
        FList.Add(Format('%.5d: %s', [CP, GetCodepageName(CP)]));
        Result := True;
      end;
    end;
  end;
end;

class function TWindowsCodepages.GetInstalled(Var CodePageList:TStrings) : Boolean;
begin
  Result := False;
  FList := TStringList.Create;
  try
    if (EnumSystemCodePagesW(@EnumCodePagesProc,CP_INSTALLED)) then
    begin
      CodePageList.AddStrings(FList);
      FList.Clear;
      Result := True;
    end;
  finally
    FList.Free;
    FList := Nil;
  end;
end;

class function TWindowsCodepages.GetSupported(Var CodePageList:TStrings) : Boolean;
begin
  Result := False;
  FList := TStringList.Create;
  try
    if (EnumSystemCodePagesW(@EnumCodePagesProc,CP_SUPPORTED)) then
    begin
      CodePageList.AddStrings(FList);
      FList.Clear;
      Result := True;
    end;
  finally
    FList.Free;
    FList := Nil;
  end;
end;

class function TWindowsCodepages.GetName(CodePage: Cardinal) : String;
begin
  Result := GetCodepageName(CodePage);
end;
Zu finden ist der Code auch in der Ply.SysUtils.pas auf GitHub:

https://github.com/playcom-de/Console

Ein Anwendungsbeispiel gibt es dort in der Demo08.
Wolfgang
  Mit Zitat antworten Zitat
Benutzerbild von DeddyH
DeddyH

Registriert seit: 17. Sep 2006
Ort: Barchfeld
27.659 Beiträge
 
Delphi 12 Athens
 
#8

AW: Windows Codepages - Kernel32.dll - EnumSystemCodePages

  Alt 28. Jun 2023, 05:57
2 Fragen dazu:
1. Wieso sind die Stringlisten als Var-Parameter deklariert? Ich persönlich würde eher const nehmen und per Assertion sicherstellen, dass mir auch eine Instanz übergeben wurde.
2. Wozu die temporäre Liste, wenn man auch gleich die übergebene verwenden kann?
Detlef
"Ich habe Angst vor dem Tag, an dem die Technologie unsere menschlichen Interaktionen übertrumpft. Die Welt wird eine Generation von Idioten bekommen." (Albert Einstein)
Dieser Tag ist längst gekommen
  Mit Zitat antworten Zitat
Antwort Antwort

Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

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 17:16 Uhr.
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