Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Delphi Multilingual mit DLLs u. Unicode - so richtig? (https://www.delphipraxis.net/162635-multilingual-mit-dlls-u-unicode-so-richtig.html)

TheMiller 31. Aug 2011 19:35

Multilingual mit DLLs u. Unicode - so richtig?
 
Hallo,

ich habe jetzt ein System für Mehrsprachenunterstützung fertig, welches auch für Unicode geeignet sein sollte. Ich würde gerne wissen, ob das alles so richtig/in Ordnung ist. Ich weiß, dass es gnugetext etc gibt, ich mag aber nicht noch mehr Abhängigkeiten im Programm haben.

Anmerkungen: Ist angelehnt an Luckies Artikel StringDLLs und an Unicode angepasst (bzw. sollte so sein). Auch habe ich beide Projekte mit FastMM auf Leaks getestet.

Hinweis:
- Beide Projekte haben eine LanguageID-Unit eingebunden, die die Konstanten entählt.

Hier mal der komplette Quelltext:

Von der DLL
Delphi-Quellcode:
library de;

uses
  SysUtils,
  LanguageIDs;

{$R *.res}

function getText(str: Integer): PWideChar;
begin
  case str of
    BUTTON1_C:
      result:='Einloggen';
    BUTTON1_H:
      result:='Hier klicken, um einzuloggen';
  end;
end;

function getCompName(str: PWideChar): PWideChar;
begin
  if (str = 'Button1') then
    result:='Bitte hier nicht drücken';
end;

//Ersetzt später Captions und Hints nach Komponentenname
function _compName(comp: PWideChar; Buffer: PChar; lenBuffer: Integer): Integer; stdcall;
begin
  if Assigned(Buffer) then
    StrLCopy(Buffer, PWideChar(getCompName(comp)), lenBuffer);
  result := length(getCompName(comp));
end;

//Ruft einfach nur Text ab.
function _lang(str: Integer; Buffer: PWideChar; lenBuffer: Integer): Integer; stdcall;
begin
  if Assigned(Buffer) then
    StrLCopy(Buffer, PWideChar(getText(str)), lenBuffer);
  result := length(getText(str));
end;

exports
  _lang,
  _compName;

begin
end.
Haupt-Programm
Delphi-Quellcode:
type
  TForm1 = class(TForm)
    Button1: TButton;
    Label1: TLabel;
    Edit1: TEdit;
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    function _l(strName: Integer): String;
    function _cl(compName: String): String;
  end;

  TLang = function (str: Integer; Buffer: PChar; lenBuffer: Integer): Integer; stdcall;
  TComp = function (comp: PChar; Buffer: PChar; lenBuffer: Integer): Integer; stdcall;

var
  Form1: TForm1;
  hLib: THandle;
  s: String;
  Lang: TLang;
  Comp: TComp;
  len: Integer;
  Buffer: PChar;

implementation

{$R *.dfm}

{****** Holt den Text aus der DLL **********}
function TForm1._l(strName: Integer): String;
begin
  len:=lang(strName, nil, 0);
  try
    // Hier Unicode-Anpassung: jeweils 2 Byte pro Char + 2 Bytes für Stopp: \0
    GetMem(Buffer, len*2+2);

    // Hier Unicode-Anpassung: jeweils 2 Byte pro Char + 2 Bytes für Stopp: \0
    len:=lang(strName, Buffer, len*2+2);

    result:=String(Buffer);
  finally
    FreeMem(Buffer);
  end;
end;


{****** Holt einen Componentennamen aus der DLL **********}
function TForm1._cl(compName: string): String;
var
  s: String;
begin
  len:=comp(PWideChar(compName), nil, 0);
  try
    // Hier Unicode-Anpassung: jeweils 2 Byte pro Char + 2 Bytes für Stopp: \0
    GetMem(Buffer, len*2+2);

    // Hier Unicode-Anpassung: jeweils 2 Byte pro Char + 2 Bytes für Stopp: \0
    len:=comp(PWideChar(compName), Buffer, len*2+2);

    result:=String(Buffer);
  finally
    // Speicher wieder freigeben
    FreeMem(Buffer);
  end;
end;


procedure TForm1.FormCreate(Sender: TObject);
var
  i: Integer;
begin
  hLib := LoadLibrary('de.dll');

  // Beim Laden ist ein Fehler aufgetreten
  if hLib = 0 then
  begin
    Str(GetLastError, s);
    ShowMessage(SysErrorMessage(GetLastError));
    exit;
  end;

  //Funktion laden
  @lang := GetProcAddress(hLib, '_lang');

  //Beim Laden der Funktion ist ein Fehler aufgetreten.
  if (not Assigned(lang)) then
  begin
    Str(GetLastError, s);
    ShowMessage(SysErrorMessage(GetLastError));
    exit;
  end;

  //Funktion laden
  @comp := GetProcAddress(hLib, '_compName');

  //Beim Laden der Funktion ist ein Fehler aufgetreten.
  if (not Assigned(comp)) then
  begin
    Str(GetLastError, s);
    ShowMessage(SysErrorMessage(GetLastError));
    exit;
  end;

  //Spezielle Komponententexte ersetzen.
  for i:=0 to Form1.ComponentCount-1 do
  begin
    if (Form1.Components[i].ClassName = 'TButton') then
    begin
      (Form1.Components[i] as TButton).Caption:=_cl(Form1.Components[i].Name);
    end;
  end;

  FreeLibrary(hlib);
end;

end.
Also. Fehler treten keine auf und es funktioniert eigentlich alles wunderbar. Die Frage ist nur, ob das wirklich so richtig ist. Würde das gerne vorher wissen, bevor ich weitermache. Vielleicht hilft dieser Beitrag ja dem Einen oder Anderen.

Danke im Voraus

himitsu 31. Aug 2011 20:16

AW: Multilingual mit DLLs u. Unicode - so richtig?
 
Binde die Unit SimpleShareMem in deine EXE und DLLs ein. (als Erstes in den DPRs)

Und schon kannst du Strings direkt übergeben.


Ich wäre hier aber eher auf Resource-DLLs übergegangen.
- die Texte als Resourcen in die DLLs eingebunden
- und dann läd die EXE jeweils die gewünschte DLL und nutzt MSDN-Library durchsuchenLoadString mit dem Handle der DLL.


PS:
Zitat:

Delphi-Quellcode:
    BUTTON1_C:
      result:='Einloggen';
    BUTTON1_H:
      result:='Hier klicken, um einzuloggen';

Besser über globale Konstanten lösen, denn die loklen Konstanten sind begrenzt (ab einer gewissen Anzahl kann der Compiler nimmer)

Delphi-Quellcode:
const // oder gar resourcestring statt const
  sLogin = 'Einloggen'

...

BUTTON1_C:
      result:=sLogin;
Ein array of string, als globale Konstante, ginge auch.

TheMiller 31. Aug 2011 21:37

AW: Multilingual mit DLLs u. Unicode - so richtig?
 
Hallo,

danke für deine ausführliche Antwort. Ich habe mich bewusst gegen einen Speicherverwalter entschieden, da ich nichts zusätzliches mit ausliefern möchte. Und das Tutorial von Luckie kannte ich schon lange und habe es auch oft erfolgreich umgesetzt.

Was die DLL als solche angeht: Mit Ressourcen-DLLs habe ich noch nie gearbeitet - da müsste ich mich erstmal umschauen.

Also gehe ich davon aus, dass du mir von meiner Methode abraten würdest, oder?

Ich habe auch späterhin noch bemerkt, dass FastMM doch meckert. Und zwar immer nach FreeMem mit der Meldung "corrupt block during a Freemem-Operation" (ich habe leider die Meldung gerade nicht parat).

Danke für eure/deine Hilfe!

himitsu 31. Aug 2011 22:15

AW: Multilingual mit DLLs u. Unicode - so richtig?
 
Solange DLL und EXE Delphi geschrieben sind, brauchst du nichts Zusätzliches.
Wobei andere Sprachen auch den Speichermanager der EXE mit benutzen könnten, da hier die EXE ja zuerst geladen wird.

Bei ShareMem mußte man noch eine weitere DLL mitgeben (die mit dem globalen Speichermanager ... BorlndMM.dll oder so)

Der FastMM, in deinem Delphi, ist aber so ausgelegt, daß er von sich aus den Speicher sharen kann, welches die besagte Unit für dich erledigt.
> Die EXE/DLL welche hierbei zuerst geladen wird, stellt ihren MM den Anderen zur Verfügung.


[add]
Ach ja ... *aufpfotenhau*

Was suchen z.B. len und buffer in den globalen?
Du kombinierst String mit PWideChar :!:


String + PChar + SizeOf(Char)
WideString|UnicodeString + PWideChar + 2|SizeOf(WideChar)
AnsiString + PansiChar + 1|SizeOf(AnsiChar)
und untereinander niemals vermischen!



PS:
Zitat:

Delphi-Quellcode:
function TForm1._cl(compName: string): String;
var
  s: String; // garnicht verwendet?
begin
  len:=comp(PWideChar(compName), nil, 0);
  try
    GetMem(Buffer, len*2+2);
    len:=comp(PWideChar(compName), Buffer, len*2+2);
    result:=String(Buffer);
  finally
    FreeMem(Buffer);
  end;
end;

Delphi-Quellcode:
function TForm1._cl(compName: UnicodeString): UnicodeString;
begin
  SetLength(Result, comp(PWideChar(compName), nil, 0));
  comp(PWideChar(compName), PWideChar(Result), (Length(Result) + 1) * SizeOf(WideChar));
end;

TheMiller 1. Sep 2011 13:08

AW: Multilingual mit DLLs u. Unicode - so richtig?
 
Danke für deine Hilfe. Das mit den globalen Vars und dem nicht verwendeten s kam vom Testen. Das mit dem WideString etc ist mir dann auch aufgefallen und an UnicodeString habe ich garnicht gedacht....

Also verstehe ich dich richtig, dass ich die o.g. Unit einbinden soll, und dann einfach die Strings übergeben soll, WENN ich schon die Version mit der DLL durchziehen möchte?

Ich würde - warum auch immer, ich weiß es selbst nicht - gerne bei den DLLs bleiben. Finde ich irgendwie übersichtlicher und mit Ressourcen habe ich noch nicht viel gearbeitet.

Was spricht gegen DLLs?

Danke!

himitsu 1. Sep 2011 14:57

AW: Multilingual mit DLLs u. Unicode - so richtig?
 
"Sollen" nicht unbedingt, aber es würde bestimmt Einiges vereinfachen.

- du kannst direkt UnicodeString als parameter angeben (entspricht seit 2009 ja dem string, also keinerlei Konvertierungen im Betrieb)
- und du kannst die Vorzüge der Strings nutzen ... keine "unsicheren" Speicheropertationen und PWideChar-hinundherkopiere

Delphi-Quellcode:
  TLang = function (str: LongInt): UnicodeString;
  TComp = function (const comp: UnicodeString): UnicodeString;
UnicodeString anstatt String, da man solche Interfaces besser mit statischen Typen deklariert ... nicht daß sich das Interface urplötzlich verändert, wenn man auf ein anderes Delphi umsteigt oder für ein anderes System kompiliert (32 Bit und 64 Bit, ANSI und Unicode, ...)


Würde am Ende auch ein bissl der Performance zu Gute kommen, wenn da öfters mal was ausgelesen würde.

Bzw. die UnicodeStrings verbrauchen keinerlei Arbeitsspeicher, da sie, über einen Spezialfall der Referenzzählung als Konstante im "Speicher" liegen.
Die Stringdaten verbleiben in der Datasektion (oder wo auch immer die liegen) und es werden immer nur Zeiger darauf weitergegeben.
(solange man den String nicht manipuliert, ala
Delphi-Quellcode:
Copy
,
Delphi-Quellcode:
Delete
oder
Delphi-Quellcode:
+
)

TheMiller 1. Sep 2011 18:13

AW: Multilingual mit DLLs u. Unicode - so richtig?
 
Hi!

Super Erklärung - hat auch alles soweit super funktioniert. Ist ja wirklich so, als würde man Strings zwischen zwei Formularen austauschen.

Aber eine Frage habe ich noch... Habe ich jetzt noch die Möglichkeit, nach MemoryLeaks über FastMM zu suchen? Eigentlich ja nicht, weil sich FastMM ja nicht mehr laden kann, weil ja schon ein anderer MemoryManager installiert ist. Hast du da zufälligerweise auch noch einen Tipp für mich?

Vielen Dank

TheMiller 2. Sep 2011 12:41

AW: Multilingual mit DLLs u. Unicode - so richtig?
 
Liste der Anhänge anzeigen (Anzahl: 1)
Moin!

Habe jetzt mal soweit alles fertig. Würde es euch aber gerne nochmal zeigen. Zum einen, dass ihr mir noch Vorschläge geben könnt und zum anderen hilft es vielleicht ja jemand anderen noch.

Es ist jetzt so, dass beim Sprachenwechsel alle gewünschten Objekttypen automatisch ersetzt werden. D.h., ich trage in die Änderungsliste den Typ TButton ein und alle Buttons auf der Form werden übersetzt. Vielleicht ist das auch zu kompliziert und ihr habt noch andere Ideen.

Es würde mich freuen, wenn ich nochmal drüberschaut. Es ist nicht viel Quelltext, wollte aber dennoch nicht alles in den Beitrag posten - sonst muss man wieder Kilometer runterscrollen.

Danke

himitsu 2. Sep 2011 12:52

AW: Multilingual mit DLLs u. Unicode - so richtig?
 
Das Ding nutzt ja den FastMM, welcher im Delphi integriert ist ... du mußt nur noch dort die Speicherprüfung aktivieren, wo er geladen ist.


Je nach dem, was zuerst geladen wird, da wird er verwendet.
- statisch geladene DLLs/BPLs werden vo der EXE geladen
- dynamisch (manuell ... LoadLibrary) geladene DLLs/BPLs werden nach der aufrufenden EXE/DLL geladen.


("nochmal drüberschaut" ... nach Feierabend dann)

TheMiller 2. Sep 2011 23:08

AW: Multilingual mit DLLs u. Unicode - so richtig?
 
Ok, vielen Dank für deine Hilfe.


Alle Zeitangaben in WEZ +1. Es ist jetzt 04:13 Uhr.
Seite 1 von 2  1 2      

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