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
Thema durchsuchen
Ansicht
Themen-Optionen

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

Windows Codepages - Kernel32.dll - EnumSystemCodePages

  Alt 21. Jun 2023, 16:17
Hallo,

ich verwende folgenden Code um die Codepages aufzulisten, die Windows installiert hat.

Delphi-Quellcode:
Var CodePageNumbers : TArray<Cardinal>;

procedure GetCodepageList(Var CodePageList:TStringList);

  function GetCPName(CP:Cardinal) : String;
  var CpInfoEx : TCPInfoEx;
  begin
    Result := '';
    if IsValidCodePage(Cp) then
    begin
      GetCPInfoEx(Cp, 0, CpInfoEx);
      Result := Format('%s', [CpInfoEx.CodePageName]);
      Result := ReplaceStr(Result,CP.ToString+' ','').TrimLeft;
      Result := Copy(Result,2,length(Result)-2);
    end;
  end;

  function EnumCodePagesProc(CodePage:PWideChar) : Cardinal; stdcall;
  Var Cp : cardinal;
  begin
    Result := 0;
    if (CodePage<>Nil) then
    begin
      if (Length(Codepage)>0) then
      begin
        Cp := StrToIntDef(CodePage,0);
        if (cp>0) then
        begin
          SetLength(CodePageNumbers,Length(CodePageNumbers)+1);
          CodePageNumbers[High(CodePageNumbers)] := CP;
          Result := 1;
        end;
      end;
    end;
  end;

var I : Integer;
begin
  SetLength(CodePageNumbers,0);
  CodePageList.Clear;
  EnumSystemCodePagesW(@EnumCodePagesProc, CP_SUPPORTED);
  for I := 0 to High(CodePageNumbers) do
  begin
    if CodePageNumbers[i]>0 then
    begin
      CodepageList.Add(IntToString(CodePageNumbers[i],5) + ': '
        + GetCPName(CodePageNumbers[i]));
    end;
  end;
  SetLength(CodePageNumbers,0);
end;
Das funktioniert und Win32 problemlos, wenn ich jedoch auf Zielplattform Win64 umstelle kommt in der EnumCodePagesProc nichts mehr an. Ein Microsoft Bug, oder stelle ich mich nur zu blöd an? Oder gibt es dafür eine elegantere Lösung, ohne EnumSystemCodePages / EnumSystemCodePagesA / EnumSystemCodePagesW?
Wolfgang
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

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

AW: Windows Codepages - Kernel32.dll - EnumSystemCodePages

  Alt 21. Jun 2023, 20:12
Zitat:
function EnumCodePagesProc(CodePage:PWideChar) : Cardinal; stdcall;
Das darf keine SubMethode sein.
Auch schon in Win32 war das schon immer so falsch. (es ist Zufall, dass es hier grade so geht ... weil der Optimimierer mal gute eine Arbeit machte und du darin nicht auf Self oder CodePageList zugegriffen hattest,
bzw. eigentlich sollte jemand für die globale Variable paar auf die Finger bekommen, aber hier ist es einer der Gründe, warum es zufällig ging)


Sie muß eine "alleinstehene" Function sein, oder eine "static" Class-Function.



stdcall; ist unter Win64 egal (es gibt nur noch eine einzige Aufrufkonvention),
aber als Kompatibilität zu Win32 schadet es nicht das dran zu lassen. (wird einfach vom Compiler ignoriert)
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests

Geändert von himitsu (21. Jun 2023 um 20:24 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von arcticwolf
arcticwolf

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

AW: Windows Codepages - Kernel32.dll - EnumSystemCodePages

  Alt 21. Jun 2023, 22:57
Danke himitsu!

Zitat:
Sie muß eine "alleinstehene" Function sein
Da hätte ich auch selber drauf kommen können.

Das mit der globalen Variable ist mir auch ein Dorn im Auge, aber ich wusste nicht wie ich das anders lösen kann. Kannst Du mir das Konzept mit der

Zitat:
"static" Class-Function
bitte erläutern? Wie genau muss ich so eine Klasse deklarieren? Ich habe mal soetwas versucht, aber nicht zum laufen bekommen.

Delphi-Quellcode:
Type TWindowsCodepages = class
     strict private
       class var IList : TArray<String>;
       class function GetCodepageName(Codepage:Cardinal) : String;
       class function EnumCodePagesProc(CodePage:PWideChar) : Boolean; static;
     strict protected
       class function GetEntry(Index:integer) : String; static;
       class procedure SetEntry(Index:Integer; uString:String); static;
       class function GetCount : Integer; static;
     public
       class property List[Index:Integer]:String Read GetEntry Write SetEntry;
       class property Count:Integer Read GetCount;
       class function Get_Supported : TArray<String>;
     end;
Wolfgang
  Mit Zitat antworten Zitat
peterbelow

Registriert seit: 12. Jan 2019
Ort: Hessen
675 Beiträge
 
Delphi 11 Alexandria
 
#4

AW: Windows Codepages - Kernel32.dll - EnumSystemCodePages

  Alt 22. Jun 2023, 12:39
Wenn man eine Klassenmethode als "class procedure" oder "class function" deklariert hat die Methode einen verborgenen Parameter (self), der im Unterschied zu einer non-class Methode aber die Referenz auf die Klasse selbst enthält und nicht die auf eine Instanz (Objekt) der Klasse.

Definiert man eine Methode dagegen als "static class function" bzw. "static class procedure" hat sie diesen verborgenen Parameter nicht, ihre Signatur ist dann identisch mit einer außerhalb der Klasse deklarierten function oder procedure mit der gleichen Parameterliste und calling convention. Deshalb kann man sie als API callback verwenden.

Der Nachteil: man kann aus einer static class Method nur auf ebenfalls als static deklarierten Elemente der Klasse zugreifen, da der Compiler das beim kompilieren auflösen kann. Innerhalb der Methode gibt es ja keine Referenz auf die Klasse...
Peter Below
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

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

AW: Windows Codepages - Kernel32.dll - EnumSystemCodePages

  Alt 22. Jun 2023, 15:12
Zitat:
class function GetEntry(Index:integer) : String; static;
jupp, genau so ... dieses "static".

Eine Funktion oder Class-Function in einer Klasse oder Record besitzen einen impliziten/unsichtbaren Parameter "Self". (der steht immer als Erstes in der Parameterliste)
Eine statische Class-Function besitzt dieses Self nicht.
Auch diese eingebetteten Methoden besitzen "normalerweise" so eine versteckte Variable (hier der StackPointer auf den Speicherberech der übergeordneten Funktion, mit dessen Self, dessen Parametern und den lokalen Variablen ... nur von Denen vor der eingebetteten Methode)

Funktion : Self = der Instanzzeiger
Class-Function: Self = der "aktuelle" Klassentyp -> von der Instanz aus der Variable (Variable.MyFunction) oder der benutzte Klassenname (TMyClass.MyFunction)
static Class-Function: besitzen kein Self und gehen immer "hart" auf den Typ der zugehörigen Klasse

Drum muß man auch aufpassen, ob man Class-Functions über eine Variable oder über den Typ aufruft.
Denn mit NIL oder schlimmer einem ungültigen Instanzzeiger, würde versucht daraus den "aktuellen" ClassTyp für das "Self" auszulesen, was dann wunderschön knallt.

PS:
Class-Funktions in Records müssen leider immer static sein (k.A. warum der Compiler das nicht implizit macht, selbst wenn man es nicht explizit angibt )
MethodenZeigern (z.B. TNotifyEvent) kann man sowohl normale Methoden zuweisen, als auch KlassenMethoden.
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests

Geändert von himitsu (22. Jun 2023 um 15:15 Uhr)
  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, 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.048 Beiträge
 
Delphi 12 Athens
 
#7

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
Antwort Antwort


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 04:26 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