Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi LoadString macht Probleme (https://www.delphipraxis.net/1410-loadstring-macht-probleme.html)

Christian Seehase 23. Nov 2002 15:46


LoadString macht Probleme
 
Moin Zusammen,

im Zusammenhang mit dem Auslesen von Resourcen bin ich gerade auf ein Problem gestossen.

Ich erzeuge mir eine Dateiliste (mit Pfaden) die ich dann in einer Schleife durchgehe, um mir die enthaltenen Resourcentypen und Namen herauszusammeln.
Als Ergebnis habe ich dann einen TreeView, in dem auf der obersten Ebene die Nodes als Text den Pfad der Datei haben, in der darunterliegenden Ebene die Resourcentypen, und unter diesen dann die Resourcennamen.
Das klappt auch soweit.
Jetzt wollte ich mir für den Typ RT_STRING auch jeweils gleich die Werte auslesen, und bin da auf ein Problem gestossen.

Durchgehen durch die Dateiliste:

Delphi-Quellcode:
procedure ExtractResourceInfo(const p_slFileList : TStringList;const p_tvRoot : TTreeView);

var
  i     : integer;
  hFile : DWORD;
  tnWork : TTreeNode;

begin
  for i := 0 to p_slFileList.Count-1 do
  begin
    Application.ProcessMessages;
    hFile := LoadLibraryEx(PChar(p_slFileList[i]),0,LOAD_LIBRARY_AS_DATAFILE);
    try
      if (hFile <> 0) then
      begin
        tnWork := p_tvRoot.Items.Add(nil,p_slFileList[i]);
        EnumResourceTypes(hFile,@BuildResourceTypeList,integer(tnWork));
      end;
    finally
      if hFile <> 0 then
      begin
        FreeLibrary(hFile);
      end;
    end;
  end;
end;
Callback Funktion EnumResTypeProc

Delphi-Quellcode:
function BuildResourceTypeList(const p_hFile : DWORD;const p_pszType : PChar;p_pLParam : TTreeNode) : Boolean; stdcall;

const
  RT_HTML    = MAKEINTRESOURCE(23);
  RT_MANIFEST = MAKEINTRESOURCE(24);

var
  tnWork : TTreeNode;
  sType : string;

begin
  if IS_INTRESOURCE(integer(p_pszType)) then
  begin
    case integer(p_pszType) of
      integer(RT_ACCELERATOR) : sType := 'RT_ACCELERATOR';
      integer(RT_ANICURSOR)   : sType := 'RT_ANICURSOR';
      integer(RT_ANIICON)     : sType := 'RT_ANIICON';
      integer(RT_BITMAP)      : sType := 'RT_BITMAP';
      integer(RT_CURSOR)      : sType := 'RT_CURSOR';
      integer(RT_DIALOG)      : sType := 'RT_DIALOG';
      integer(RT_DLGINCLUDE)  : sType := 'RT_DLGINCLUDE';
      integer(RT_FONT)        : sType := 'RT_FONT';
      integer(RT_FONTDIR)     : sType := 'RT_FONTDIR';
      integer(RT_GROUP_CURSOR) : sType := 'RT_GROUP_CURSOR';
      integer(RT_GROUP_ICON)  : sType := 'RT_GROUP_ICON';
      integer(RT_HTML)        : sType := 'RT_HTML';
      integer(RT_ICON)        : sType := 'RT_ICON';
      integer(RT_MANIFEST)    : sType := 'RT_MANIFEST';
      integer(RT_MENU)        : sType := 'RT_MENU';
      integer(RT_MESSAGETABLE) : sType := 'RT_MESSAGETABLE';
      integer(RT_PLUGPLAY)    : sType := 'RT_PLUGPLAY';
      integer(RT_RCDATA)      : sType := 'RT_RCDATA';
      integer(RT_STRING)      : sType := 'RT_STRING';
      integer(RT_VERSION)     : sType := 'RT_VERSION';
      integer(RT_VXD)         : sType := 'RT_VXD';
      else
        sType := '#'+IntToStr(integer(p_pszType));
    end;
    tnWork := TTreeView(p_pLParam.TreeView).Items.AddChild(p_pLParam,sType);
    EnumResourceNames(p_hFile,p_pszType,@BuildResourceNameTable,integer(tnWork));
  end
  else
  begin
    sType := trim(p_pszType);
    tnWork := TTreeView(p_pLParam.TreeView).Items.AddChild(p_pLParam,sType);
    EnumResourceNames(p_hFile,PChar(sType),@BuildResourceNameTable,integer(tnWork));
  end;
  Result := true;
end;
Callback Funktion EnumResNameProc

Delphi-Quellcode:
function BuildResourceNameTable(const p_hFile : DWORD;const p_pszType : PChar;const p_pszName : PChar;const p_pLParam : TTreeNode) : Boolean; stdcall;

var
  sName   : string;
  szBuffer : PChar;

begin
  if IS_INTRESOURCE(integer(p_pszName)) then
  begin
    sName := '#'+IntToStr(integer(p_pszName));
  end
  else
  begin
    sName := trim(p_pszName);
  end;
  if p_pszType = RT_STRING then
  begin
    if p_hFile <> 0 then
    begin
      szBuffer := StrAlloc(65536);
      try
        // [color=red]Hier schlägt's fehl mit der Meldung, dass die angegebene Resource nicht gefunden werden konnte[/color]
        if LoadString(p_hFile,integer(p_pszName),szBuffer,65535) <> 0 then
        begin
          sName := sName + ' / '+trim(szBuffer);
        end
        else
        begin
          sName := sName + ' / '+SysErrorMessage(GetLastError);
        end;
      finally
        StrDispose(szBuffer);
      end;
    end;
  end;
  TTreeView(p_pLParam.TreeView).Items.AddChild(p_pLParam,sName);
  Result := true;
end;

Als Paramter für die Resource habe ich es auch schon mit cardinal und PChar(sName) probiert (auch wenn letzteres ja eigentlich Unsinn ist).
Das Ergebnis war immer das gleiche.
Ausser z.B. bei der Kernel32 bei der die eine oder andere RT_STRING Resource ausgelesen wurde (ob korrekt oder nicht sei mal dahingestellt) kommt immer nur der Fehler, dass die Resource nicht gefunden wurde.
Interessant dabei: Es wurden nicht alle aus der Kernel32 ausgelesen, nur ein paar (vier um genau zu sein, nämlich 1, 2, 1025 und 1089).

Was auch noch auffiel:
Ich hatte es unmittelbar vor dem LoadString auch schon mal mit GetModuleHandle versucht um ein Handle auf die geladene Datei zu bekommen. Dies schlug mit schöner Regelmässigkeit fehl mit der Meldung, dass das Modul nicht gefunden werden konnte. Da es aber über LoadLibraryEx geladen wurde (sonst wären ja die Enum Funktionen gar nicht aufgerufen worden) kann das irgendwie auch nicht sein, ausser LOAD_LIBRARY_AS_DATAFILE verhindert die erfolgreiche Suche von GetModuleHandle.
Wäre denkbar, denn bei Kernel32 schlug's nicht fehl, und die wird ja zu jedem Prozess geladen.

Hat irgendjemand 'ne Idee?

BTW: Getestet unter W2K SP2

Christian Seehase 26. Nov 2002 19:45

Moin Zusammen,

keiner 'ne Idee?

sakura 26. Nov 2002 21:16

Die Frage hatte ich noch gar nicht gesehen :oops: Schaue ich mir morgen früh mal an...Schwups nach oben damit...

sakura 3. Dez 2002 09:09

Habe es mir endlich mal zu Gemüte gezogen - soweit aber erfolglos. Wo (oder wie) ist den die Funktion IS_INTRESOURCE definiert :?

...:cat:...

Christian Seehase 3. Dez 2002 09:38

Moin sakura,

:oops: Diesmal andersherum :mrgreen:

Das IS_INTRESOURCE Macro habe ich als Funktion implementiert.

Delphi-Quellcode:
function IS_INTRESOURCE(const p_iParameter : integer) : Boolean;
begin
  Result := (p_iParameter and $FFFF0000) = 0;
end;

sakura 3. Dez 2002 11:22

Ich habs :bounce2:

Der Trick liegt bei MS. Jede String-Resource ID verweist auf bis zu 16 Strings. Dadurch wird die Kapazität für verfügbare String Sourcen erhöht. Das ganze lässt sich dann wie folgt berechnen:

RealStringID = [$FFFF - ($1000 - EnumStringID) * 16 - $0F..$FFFF - ($1000 - EnumStringID) * 16 - $00];

Code:
function BuildResourceNameTable(const p_hFile : DWORD;const p_pszType : PChar;const p_pszName : PChar;const p_pLParam : TTreeNode) : Boolean; stdcall;
var
  sName   : string;
  szBuffer : PChar;
  i       : integer;
  offset  : integer;
begin
  if IS_INTRESOURCE(integer(p_pszName)) then
  begin
    sName := '#'+IntToStr(integer(p_pszName));
  end
  else
  begin
    sName := trim(p_pszName);
  end;
  if p_pszType = RT_STRING then
  begin
    if p_hFile <> 0 then
    begin
      szBuffer := StrAlloc(65536);
      try
        offset := $FFFF - (($1000 - integer(p_pszName)) shl 4);
        for i := $0F downto $00 do
          if Boolean(LoadString(p_hFile, offset - i, szBuffer, 65535)) then
            TTreeView(p_pLParam.TreeView).Items.AddChild(p_pLParam, '#'+
                IntToStr(offset-i) + ' / ' + Trim(szBuffer));
      finally
        StrDispose(szBuffer);
      end;
    end;
  end
  else
  begin
    TTreeView(p_pLParam.TreeView).Items.AddChild(p_pLParam,sName);
  end;
  Result := true;
end;

Christian Seehase 3. Dez 2002 12:13

Moin sakura,

vielen Dank. Super, so geht dem :D

Aber: Wo hast Du das gefunden? :shock:

sakura 3. Dez 2002 12:23

Die Info, dass hinter jede ID bis zu 16 Strings versteckt sind, findest DU im MSDN. Die Berechnung habe ich hergeleitet, nachdem ich mir mit einem Resource-Explorer mal so eine EXE angeschaut habe. Dann habe ich versucht, aus den Daten von Deinem Code-Snippsel und der Ziel-Darstellung Sinn zu machen. Dass war dann die Lösung. :mrgreen:

...:cat:...

PlanLos 13. Okt 2003 20:19

Re: LoadString macht Probleme
 
Abend Seehase :hi:

Ich habe oben bei dir gesehen dass du EnumResourceNames benutzt hast und wollte fragen ob du mir mal die funktion erklären kannst. Die ersten beiden Parameter sind mir schon klar was ich da einsetzen muss aber bei den rest leider nicht. Ich wollte eine funktion schreiben in dem ich alle Resournamen
in der Stringtable auslesen wollte.(zum beispiel '4086')

Christian Seehase 13. Okt 2003 22:53

Re: LoadString macht Probleme
 
Moin Planlos,

der dritte Parameter ist die Adresse einer Funktion (Callbackfunktion), die EnumResourceNames für jeden gefundenen Resourcennamen aufruft.
Wie die Funktion deklariert werden muss steht weiter oben im Thread schon beschrieben. (Wichtig bei Callbackfunktionen für Funktionen der Windows API ist die Aufrufkonvention stdcall).
Der vierte Parameter ist einfach ein Integer Wert, der frei verwendet werden kann, wie z.B. die Eigenschaft Tag bei vielen Controls.
Ich habe ihn dafür verwendet die Adresse eines TreeNodes zu verwenden, um die Ergebnisse der Funktion direkt in einen TreeView schreiben zu können.

Wird als Name der Resource um eine Int-Resource (IS_INTRESOURCE, s.o., gibt true zurück, oder der Name beginnt mit einem #), und handelt es sich um eine String Resource, so können sich dahinter bis zu 16 Strings verbergen.
Falls man also nicht exakt die Resource ID eines Strings hat, muss man alle 16 Möglichkeiten durchprobieren.
Entscheidend ist hierbei, dass die Funktion LoadString durchaus 0 zurückgeben kann, was eigentlich auf einen Fehler hindeutet, GetLastError aber trotzdem ERROR_SUCCES meldet.
Grund hierfür: Die ausprobierte Resource ID ist nicht als Resource in der Datei vorhanden, aber der Aufruf war kein Fehler, weil es grundsätzlich eine Stringtabelle in diesem Bereich gibt, nur eben keinen String mit exakt dieser ID.

Die realen Resource IDs für die Strings lassen sich übrigens einfacher Berechnen, als von sakura ermittelt:

(Keine Lauffähige Prozedur. Ich möchte nur die Berechnung der möglichen Stringresource IDs deutlich machen.)

Delphi-Quellcode:
var
  iResourceRoot : integer;
  i            : integer;

begin
  iResourceRoot := (UebergebeneIntResourceIDeinerStringResource-1)*16;
  for i := iResourceRoot to iResourceRoot+15 do begin
    if LoadString(...,i,...,...) <> 0 then begin
    end else begin
      if GetLastError <> ERROR_SUCCESS then begin
        // Erst jetzt ist wirklich ein Fehler aufgetreten
      end;
    end;
  end;
end;
Die Schleife muss übrigens immer voll durchlaufen werden, da die Sub IDs nicht lückenlos verwendet werden müssen.

Übrigens schon einmal mein Dank an Dich, denn durch Deine Frage bin ich dazu gekommen, mich noch einmal mit den String Resourcen zu beschäftigen, was dann zu dieser vereinfachten Berechnung geführt hat.


Alle Zeitangaben in WEZ +1. Es ist jetzt 21:32 Uhr.
Seite 1 von 2  1 2      

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