AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

LoadString macht Probleme

Ein Thema von Christian Seehase · begonnen am 23. Nov 2002 · letzter Beitrag vom 14. Okt 2003
Antwort Antwort
Seite 1 von 2  1 2      
Christian Seehase
(Co-Admin)

Registriert seit: 29. Mai 2002
Ort: Hamburg
11.105 Beiträge
 
Delphi 11 Alexandria
 
#1

LoadString macht Probleme

  Alt 23. Nov 2002, 15:46
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
Tschüss Chris
Die drei Feinde des Programmierers: Sonne, Frischluft und dieses unerträgliche Gebrüll der Vögel.
Der Klügere gibt solange nach bis er der Dumme ist
  Mit Zitat antworten Zitat
Christian Seehase
(Co-Admin)

Registriert seit: 29. Mai 2002
Ort: Hamburg
11.105 Beiträge
 
Delphi 11 Alexandria
 
#2
  Alt 26. Nov 2002, 19:45
Moin Zusammen,

keiner 'ne Idee?
Tschüss Chris
Die drei Feinde des Programmierers: Sonne, Frischluft und dieses unerträgliche Gebrüll der Vögel.
Der Klügere gibt solange nach bis er der Dumme ist
  Mit Zitat antworten Zitat
Benutzerbild von sakura
sakura

Registriert seit: 10. Jun 2002
Ort: München
11.412 Beiträge
 
Delphi 11 Alexandria
 
#3
  Alt 26. Nov 2002, 21:16
Die Frage hatte ich noch gar nicht gesehen Schaue ich mir morgen früh mal an...Schwups nach oben damit...
Daniel W.
Ich bin nicht zurück, ich tue nur so
  Mit Zitat antworten Zitat
Benutzerbild von sakura
sakura

Registriert seit: 10. Jun 2002
Ort: München
11.412 Beiträge
 
Delphi 11 Alexandria
 
#4
  Alt 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

......
Daniel W.
Ich bin nicht zurück, ich tue nur so
  Mit Zitat antworten Zitat
Christian Seehase
(Co-Admin)

Registriert seit: 29. Mai 2002
Ort: Hamburg
11.105 Beiträge
 
Delphi 11 Alexandria
 
#5
  Alt 3. Dez 2002, 09:38
Moin sakura,

Diesmal andersherum

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;
Tschüss Chris
Die drei Feinde des Programmierers: Sonne, Frischluft und dieses unerträgliche Gebrüll der Vögel.
Der Klügere gibt solange nach bis er der Dumme ist
  Mit Zitat antworten Zitat
Benutzerbild von sakura
sakura

Registriert seit: 10. Jun 2002
Ort: München
11.412 Beiträge
 
Delphi 11 Alexandria
 
#6
  Alt 3. Dez 2002, 11:22
Ich habs

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;
Daniel W.
Ich bin nicht zurück, ich tue nur so
  Mit Zitat antworten Zitat
Christian Seehase
(Co-Admin)

Registriert seit: 29. Mai 2002
Ort: Hamburg
11.105 Beiträge
 
Delphi 11 Alexandria
 
#7
  Alt 3. Dez 2002, 12:13
Moin sakura,

vielen Dank. Super, so geht dem

Aber: Wo hast Du das gefunden?
Tschüss Chris
Die drei Feinde des Programmierers: Sonne, Frischluft und dieses unerträgliche Gebrüll der Vögel.
Der Klügere gibt solange nach bis er der Dumme ist
  Mit Zitat antworten Zitat
Benutzerbild von sakura
sakura

Registriert seit: 10. Jun 2002
Ort: München
11.412 Beiträge
 
Delphi 11 Alexandria
 
#8
  Alt 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.

......
Daniel W.
Ich bin nicht zurück, ich tue nur so
  Mit Zitat antworten Zitat
PlanLos

Registriert seit: 13. Okt 2003
11 Beiträge
 
#9

Re: LoadString macht Probleme

  Alt 13. Okt 2003, 20:19
Abend Seehase

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')
Ohne Fleis kein Reis
  Mit Zitat antworten Zitat
Christian Seehase
(Co-Admin)

Registriert seit: 29. Mai 2002
Ort: Hamburg
11.105 Beiträge
 
Delphi 11 Alexandria
 
#10

Re: LoadString macht Probleme

  Alt 13. Okt 2003, 22:53
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.
Tschüss Chris
Die drei Feinde des Programmierers: Sonne, Frischluft und dieses unerträgliche Gebrüll der Vögel.
Der Klügere gibt solange nach bis er der Dumme ist
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


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 22:41 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