![]() |
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:
Callback Funktion EnumResTypeProc
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;
Delphi-Quellcode:
Callback Funktion EnumResNameProc
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;
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 |
Moin Zusammen,
keiner 'ne Idee? |
Die Frage hatte ich noch gar nicht gesehen :oops: Schaue ich mir morgen früh mal an...Schwups nach oben damit...
|
Habe es mir endlich mal zu Gemüte gezogen - soweit aber erfolglos. Wo (oder wie) ist den die Funktion IS_INTRESOURCE definiert :?
...:cat:... |
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; |
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; |
Moin sakura,
vielen Dank. Super, so geht dem :D Aber: Wo hast Du das gefunden? :shock: |
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:... |
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') |
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:
Die Schleife muss übrigens immer voll durchlaufen werden, da die Sub IDs nicht lückenlos verwendet werden müssen.
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; Ü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. |
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