Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   FreePascal eingebunde Resourcen abfragen (RCDATA) (https://www.delphipraxis.net/167161-eingebunde-resourcen-abfragen-rcdata.html)

thomasschaf 15. Mär 2012 18:53

Delphi-Version: 5

eingebunde Resourcen abfragen (RCDATA)
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo,

wie kann ich eingebunde Resourcen auslesen ?
Der Typ ist RC_DATA.

Ich kann sie übrigens mit dem "Resource Hacker" auslesen.


Thomas

Luckie 15. Mär 2012 19:04

AW: eingebunde Resourcen abfragen (RCDATA)
 
Stichwort: TResourceSteam.

thomasschaf 15. Mär 2012 23:00

AW: eingebunde Resourcen abfragen (RCDATA)
 
Zitat:

Zitat von Luckie (Beitrag 1156779)
Stichwort: TResourceSteam.

Delphi-Quellcode:
TResourceStream.Create(HINSTANCE, '99303', RT_RCDATA);
gibt leider die Fehlermeldung:

der angegebene ressourcentyp wurde nicht in der image-datei gefunden

Ist die Syntax denn dem Screenshot zufolge richtig ??

Namenloser 15. Mär 2012 23:08

AW: eingebunde Resourcen abfragen (RCDATA)
 
Nein, es muss heißen
Delphi-Quellcode:
TResourceStream.CreateFromID(HINSTANCE, 99303, RT_RCDATA)
, da du hier eine Ressource nach einer ID (=Nummer) und nicht nach einem Namen (=String) lädst. Ich persönlich bin ja eher ein Freund von benannten Ressourcen, aber wie du deine Ressourcen verwaltest, ist natürlich dir überlassen.

thomasschaf 15. Mär 2012 23:33

AW: eingebunde Resourcen abfragen (RCDATA)
 
Liste der Anhänge anzeigen (Anzahl: 1)
Leider funktioniert das auch nicht und gibt den selben Fehler aus.

Wie kommst du denn darauf, dass ich nach einer Nummer suche ?
Dort, wo im Screenshot 99303 steht, könnte auch ein String stehen.

Im Anhang habe ich noch ein Beispiel erstellt.

Ist es denn sicher, dass man die Resourcen auslesen kann, wenn sie im "Resource Hacker" angezeigt werdne oder gibt es da noch Spezialfälle, die es zu unterscheiden gibt ?

Namenloser 15. Mär 2012 23:42

AW: eingebunde Resourcen abfragen (RCDATA)
 
Zitat:

Zitat von thomasschaf (Beitrag 1156814)
Dort, wo im Screenshot 99303 steht, könnte auch ein String stehen.

Im Anhang habe ich noch ein Beispiel erstellt.

Ah ok, dann hab ich das falsch gesehen, sorry. Dann sollte dein Beispiel aber eigentlich funktionieren.

Ist wirklich sicher, dass die Ressource auch in der Exe ist und du nicht mit Reshacker versehentlich die falsche Datei untersucht hast?

himitsu 15. Mär 2012 23:48

AW: eingebunde Resourcen abfragen (RCDATA)
 
Wie hast du diese Resource erstellt?

thomasschaf 15. Mär 2012 23:48

AW: eingebunde Resourcen abfragen (RCDATA)
 
Liste der Anhänge anzeigen (Anzahl: 1)
Es ist sicher, dass ich die richtige Datei mit dem Resourcehacker geöffnet habe.

Zwei mögliche Probleme sehe ich:
- Ich arbeite mit Lazarus, nicht mit Delphi.
- Die Resource, die ich auslesen möchte, habe ich auch in die exe-Datei geschrieben (mit einem anderen Programm über UpdateResource).
-> Es könnte also sein, dass die Resource nicht "ordentlich" reingeschrieben worden ist, obwohl ich sie mit dem reshacker sehen kann (daher auch die Frage, ob es da noch Unterschiede/Spezialfälle gibt)
--> Wenn ich die Resource in die exe-Datei schreibe und diese starte, sieht das Design auch anders aus (siehe screenshot), es wurden also die Resourcen, die für das Design verantwortlich sind, entfernt?

lbccaleb 16. Mär 2012 02:10

AW: eingebunde Resourcen abfragen (RCDATA)
 
Es kann sein, dass das Design des Programms aus der Ressource geladen wird und du mit deiner veränderung selbiger, diese irgendwie zerstörst. Er somit die benötigten Daten nicht mehr bekommt.

Ich kann dir nur dazu raten, dich vorher mit Tutorials zu dem Thema Ressourcen zu befassen. Google ist da dein Freund ;)

thomasschaf 16. Mär 2012 14:40

AW: eingebunde Resourcen abfragen (RCDATA)
 
Ich habe mich in der Zwischenzeit mit dem Thema Resourcen sehr ausgiebig beschäftigt, trotzdem kann ich mir nicht erklären, warum die Resource nicht gefunden werden kann, aber mit einem anderen Programm (Resource Hacker) angezeigt werden kann.

Könnte jemand vielleicht mit Delphi folgenden Code compilen und die exe hier im Forum anhängen ?
Delphi-Quellcode:
function TForm1.GetResValue(resname: string): string;
var
  RS: TResourceStream;
begin
  try
    RS := TResourceStream.Create(hinstance, resname, RT_RCDATA);
    ShowMessage('(1)');
    RS.Free;
    ShowMessage('(2)');
    SetString(Result, RS.Memory, RS.Size);
  except
    ShowMessage(SysErrorMessage(GetLastError));
    Result := '';
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  s: string;
begin
  s := GetResValue(Edit1.Text);
  if (s <> '') then
    ShowMessage(s);
end;
mit folgenden Komponenten:
- Editfeld: "Edit1"
- Button: "Button1"

Dann könnte ich testen, ob es möglich ist, die Resource abzufragen, die ich in eine exe reinschreibe.

Grüße

himitsu 16. Mär 2012 14:56

AW: eingebunde Resourcen abfragen (RCDATA)
 
Zitat:

Zitat von thomasschaf (Beitrag 1156936)
Dann könnte ich testen, ob es möglich ist, die Resource abzufragen, die ich in eine exe reinschreibe.

Bringt nichts, da der Code eh nicht funktioniert.

Delphi-Quellcode:
    RS := TResourceStream.Create(hinstance, resname, RT_RCDATA);
    ShowMessage('(1)');
    RS.Free; // hier freigeben
    ShowMessage('(2)');
    SetString(Result, RS.Memory, RS.Size); // und hier das nicht mehr existierende Objekt verwenden wollen?
  except

thomasschaf 16. Mär 2012 15:21

AW: eingebunde Resourcen abfragen (RCDATA)
 
Ok, das war dumm von mir.

Der Fehler ist natürlich offensichtlich, ABER es wird ja noch nicht mal die ShowMessage (1) angezeigt.

Mit Hilfe von try-except und den ShowMessages kann ich ja herausfinden, dass das Programm hier abstürzt:
Delphi-Quellcode:
function TForm1.GetResValue(resname: string): string;
var
  RS: TResourceStream;
begin
  try
    RS := TResourceStream.Create(hinstance, resname, RT_RCDATA);

// ------------- ## CUT ## -------------------------------------------------

    ShowMessage('(1)');
    RS.Free;
    ShowMessage('(2)');
    SetString(Result, RS.Memory, RS.Size);
  except
    ShowMessage(SysErrorMessage(GetLastError));
    Result := '';
  end;
end;
Woran kann das dann liegen *ratlos!*

thomasschaf 16. Mär 2012 20:15

AW: eingebunde Resourcen abfragen (RCDATA)
 
Könnte bitte jemand den Code mit Delphi kompilieren, mir die exe zukommen lassen (z.B. per Anhang hier im Forum).

Dann könnte ich überprüfen, ob ich die Resource richtig in das Programm reinschreibe.

Delphi-Quellcode:
function TForm1.GetResValue(resname: string): string;
var
  RS: TResourceStream;
begin
  try
    RS := TResourceStream.Create(hinstance, resname, RT_RCDATA);
    ShowMessage('(1)');
    SetString(Result, RS.Memory, RS.Size);
    ShowMessage('(2)');
    RS.Free;
    ShowMessage('(3)');
  except
    ShowMessage(SysErrorMessage(GetLastError));
    Result := '';
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  s: string;
begin
  s := GetResValue(Edit1.Text);
  if (s <> '') then
    ShowMessage(s);
end;
Vielen Dank

Luckie 16. Mär 2012 20:39

AW: eingebunde Resourcen abfragen (RCDATA)
 
Wie lautet denn GetLastError?

thomasschaf 16. Mär 2012 21:50

AW: eingebunde Resourcen abfragen (RCDATA)
 
Liste der Anhänge anzeigen (Anzahl: 3)
Hallo,

die Fehlermeldung ist
"Der angegebene Ressourcentyp wurde nicht in der Image-Datei gefunden".

Was mich sehr wundert ist, dass das Design so komisch aussieht (siehe Screenshot 1).

Zum Test habe ich mal die Resource in ein Programm geschriben, das nicht von mir stammt (in diesem Beispiel in eine Kopie des Windows Calculator) und das Design und alle Funktionalitäten blieben erhalten. Leider verrät er mir selbst nicht, ob dieResource auch drin ist; Resource Hacker hat es aber wieder bestätigt. (siehe Screenshot 2)

Ich habe mir nun eine Function geschrieben, die mir ausgibt, welche Resourcetypen (RC_STRING, RC_ICON, ...) überhaupt gefunden werden:
Delphi-Quellcode:
function LetzterFehler: string; // Hilfsfunktion
begin
  Result := HintenEntfernen(SysErrorMessage(GetLastError), #13#10);
end;

function ResTypeExists(typeid: integer): boolean;
begin
  Result := False;
  try
    TResourceStream.Create(hinstance, 'irgendwasblabla', MakeIntResource(typeid));
    Result := True;
  except
    if isSubStr(LetzterFehler, 'name') then
      Result := True;
  end;
end;
Damit kann festgestellt werden, ob es an dem Typ liegt oder nur an dem Namen, warum die Resource nicht gefunden worden konnte.
Die Fehlermeldungen unterscheiden sich ja nur in folgendem:
  • Der angegebene Ressourcentyp wurde nicht in der Image-Datei gefunden.
  • Der angegebene Ressourcenname wurde nicht in der Image-Datei gefunden

...und dann
Delphi-Quellcode:
procedure TForm1.Button2Click(Sender: TObject);
var
  i: integer;
  sl: TStringList;
  d: DWORD;
begin
  d := GetTickCount;
  sl := TStringList.Create;
  for i := 0 to 1000 do
    if ResTypeExists(i) then
      sl.Add(IntToStr(i));
  ShowMessage('Zeit: ' + IntToStr(GetTickCount - d) + sLineBreak + 'gefunden:' +
    sLineBreak + sl.Text);
  // Ausgabe im Original: 3,5,14,24
  // Ausgabe nach Bearbeiten durch anderes Programm mittels UpdateResource: nichts mehr.
  // (Vergleich: siehe Screenshot 3)
  sl.Free;
end;
Es werden offensichtlich alle Resourcen vernichtet, wenn ich das Programm mit UpdateResource modifiziere.
Aber ResourceHacker versichert mir, dass die Resource RT_RCDATA hinzugefügt worden ist und alles sonst beibehalten worden ist.

ASM 17. Mär 2012 00:31

AW: eingebunde Resourcen abfragen (RCDATA)
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:

Zitat von thomasschaf (Beitrag 1156948)
Mit Hilfe von try-except und den ShowMessages kann ich ja herausfinden, dass das Programm hier abstürzt:
Delphi-Quellcode:
function TForm1.GetResValue(resname: string): string;
var
  RS: TResourceStream;
begin
  try
    RS := TResourceStream.Create(hinstance, resname, RT_RCDATA);
Woran kann das dann liegen *ratlos!*

Wenn die benötigte Resource als String vorliegt, kann es eigentlich nur sein, dass dieser String entweder als Eintrag in einem in der RC-Datei als RCDATA eingebundenem Textfile vorliegt oder aber Teil einer Stringtable ist. In beiderlei Form eingebracht haben die darin vorhandenen,individuellen Strings jedoch keine eigenen Resourcenamen.

In einer Stringtable sind die enthaltenen Strings mit einem (beliebigen, aber unterschiedlichen) numerischen Wert indiziert. Ausschließlich nur über diesen Index kann ein String aus der Stringtable abgerufen werden.

Liegt der String dagegen in einem als RCDATA eingebunden Textfile vor, muss diese Resource zuerst mit einem ResourceStream abgerufen werden, anschließend dieser ResourceStream in den Stream einer Stringlist übertragen werden und schließlich die Stringlist entsprechend der aktuellen Anforderung analysiert werden.

Ein einzelner String kann nicht singulär als RCDATA (oder als TXT) in den Resourcen eingebunden werden.

Das Vorgehen, einen einzelnen String aus den Resourcen unmittelbar mittels SetString(Result, RS.Memory, RS.Size) abzurufen, kann demnach nicht funktionieren (wobei es ansonsten ohnehin korrekt heißen müßte "pchar(RS.Memory)").

Wird nun eine RCDATA-Resource mit einem Namen aufgerufen, der aber gar nicht in den Resourcen existiert, dann führt das zu einer EResNotFound-Exception - eben wie ereignet..

Abhilfe:
Binde die Unit Resources ein (Quelle).
Prüfe dann mit Hilfe von ResourceExists() erst einmal, ob überhaupt entweder in einer Stringtable ein String unter dem genannten Index vorhanden ist oder eine RCDATA-Resource mit dem genannten Namen existiert. Erst wenn das erfolgreich bestätigt worden ist, rufe die entsprechenden Daten aus den Resourcen ab:

Code:
uses Resources;

// String in einer Stringtable
function ResourceExists(index: integer): Boolean; overload;
var
  buffer: array[0..255] of Char;
  s: string;
begin
  result := false;
  try
    if LoadString(HInstance, index, buffer, SizeOf(buffer)) > 0 then
      s := string(buffer);
    result := s <> '';
  except
    on e: exception do
      Showmessage('Error ' + e.message);
  end;
end;

// String in einer RCDATA-Resource
function ResourceExists(ResName: string): Boolean; overload;
var
  lpResource: TPEResource;
  resGroup: TPEResourceGroup;
begin
  result := false;
  resGroup := TPEResourceGroup.Create(HInstance, RT_RCDATA);
  try
    lpResource := resGroup.Find(pchar(resname));
    if lpResource <> nil then result:=true;
  finally
    if Assigned(resGroup) then FreeAndNil(resGroup);
    resGroup.Free;
  end;
end;

function TForm1.GetResValue(resname: string): string;
var
  RS: TResourceStream;
begin
  RS := nil;
  RS := TResourceStream.Create(hInstance, ResName, RT_RCDATA);
  try
    SetLength(result, RS.Size);
    RS.Read(result[1], Length(result));
  finally
    RS.Free;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  ResName: string;
  slResValue: TStringlist;
begin
  ResName := 'liste'; // Name der RCDATA-Resource!
  if ResourceExists(ResName) then
  begin
    slResValue:=TStringlist.Create;
    try
      slResValue.text := GetResValue(ResName);
      Showmessage(format('Strings in der Resource "%s":'#13#10'%s', [ResName, slResValue.text]));
      // ... und Stringlist nach Bedarf analysieren
    finally
      slResValue.Free;
    end;
  end
  else
    ShowMessage(format('RCDATA-Resource "%s" gibt es nicht!', [ResName]));
end;

himitsu 17. Mär 2012 01:44

AW: eingebunde Resourcen abfragen (RCDATA)
 
Nochmal:
Wie erstellst du diese Resource?

Langsam vergeht einem die Lust immer wieder jeden mehrmals erst nach dem Wichtigsten fragen zu müssen.
Diese Frage hatte schließlich einen guten Grund. :?

thomasschaf 17. Mär 2012 10:48

AW: eingebunde Resourcen abfragen (RCDATA)
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo,

entschuldige himitsu. (Ich habe die verstecke Nachricht übrigens auch gelesen).
Du hast völlig recht, nach weiteren Versuchen konnte ich feststellen:
das Problem liegt definitiv darin, wie die Resource in die Exe geschrieben wird.

Dazu die Funktion:
Delphi-Quellcode:
function SetRes(filename, resname, resvalue: string; langid: word;
  delex: boolean): boolean;
var
  hUpdate: THandle;
  Size:   DWord;
begin
  hUpdate := BeginUpdateResource(PChar(filename), delex);
  if (hUpdate <> 0) and (hUpdate <> INVALID_HANDLE_VALUE) then
  begin
    try
      Size  := Succ(Length(resvalue)) * SizeOf(char);
      Result := UpdateResource(hUpdate, RT_RCDATA, PChar(resname),
        langid, PChar(resvalue), Size);
    finally
      EndUpdateResource(hUpdate, False);
    end;
  end;
end;

// Aufruf:
SetRes(f2, Edit1.Text, Edit2.Text, StrToInt(Edit3.Text), CheckBox1.Checked)
// Rückgabewert ist immer True.

Beispiel:
Edit1.text = TEST
Edit2.text = Hallo
Edit3.text = 0
checkbox1.checked = false

---

Nun erhalte ich aber, wenn ich versuche die Datei zu öffnen, in die ich geschrieben habe, sofort die Fehlermeldung siehe Anhang.

himitsu 19. Mär 2012 09:12

AW: eingebunde Resourcen abfragen (RCDATA)
 
Prüf mal, ob die LangID stimmt.
Diese Resource-Funktionen gehn immer nur auf eine Sprache los.
Wenn also eine andere Sprache angegeben wurde, dann kann das natürlich nicht gefunden werden.

thomasschaf 19. Mär 2012 22:17

AW: eingebunde Resourcen abfragen (RCDATA)
 
Liste der Anhänge anzeigen (Anzahl: 1)
Die Langid ist bei den anderen Resourcen 1033, deswegen habe ich die auch mal ausprobiert.
Leider hat es nicht funktioniert.

Das folgende Problem tauch aber unabhängig davon immer auf:
Nur ein Teil der ReadProcessMemory- oder WriteProcessMemory wurde abgeschlossen

Und zwar sofort nachdem ich die modifizierte EXE starte.
Es ist eine Windows-interne Fehlermeldung.
(siehe Anhang letzten Posts von mir)


Ich mache durch meine Funktion SetRes also die exe (hier: filename) kaputt, wie es scheint.

Ich würde mich freuen, falls es doch noch möglich ist, das Problem zu lösen.


Alle Zeitangaben in WEZ +1. Es ist jetzt 17:42 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