0

I have written 2 programs, they are both compiled, and both contain "STRINGTABLES" resources added via .rc files.

So, let's call App #1 "app1.exe" and App #2 "app2.exe".

Here is my issue. In app2.exe, I have a string table that looks like this:

STRINGTABLE
{
1000, "Hello"
1001, "There"
}

When I run app1.exe, I am attempting to update resources stored in app2.exe via the Win32 API UpdateResource() function.

I can use Delphi's LoadStr() function to load the strings from the stringtable just fine.

My problem is I need to understand how to use UpdateResource() in order to change JUST THE STRINGS in that stringtable. So, for example, I want app1.exe to change app2.exe's string table from what you see above to this:

STRINGTABLE
{
1000, "Thank"
1001, "You!"
}

I am sorry that I do not have any source code, however I am starting from scratch and cannot seem to figure this out.

I am using RAD Studio XE7.

If you guys need more to go on, just say so and I will update this as much as possible, but like I said I am just starting out learning about TResourceStream and all this resource stuff so I don't have much to show. All I can tell you is that I am not new to programming. I catch on to stuff very quickly. I have already created a THandle and gotten the app1.exe to look into the app2.exe's resources. I can add stuff to it, but it seems that when I try to add String data from a TEdit or TMemo component it shows up as some weird strange Chinese lettering inside the app2.exe's resources. So I am wondering how to format these strings so that they show up properly inside the resources and stuff.

To shed some more light on this, when I run app1.exe and attempt to enter the string "Hello" into app2.exe's stringtable, it gives me this:

00230BF8  00 00 00 00                                       ••••

Any clues why ?

I am using an app called "Resource Hacker" to check the resources after I run the programs.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490

1 Answers1

7

String table resources are stored in blocks of 16, each item written with a word-sized length marker and the UTF-16 encoded characters following on. If an item in a block isn't explicitly defined in the original RC file, then it's still there in the compiled resource, but with a length of 0.

So, let's assume you've carefully chosen your IDs to be in the same group of 16. I'll stick with the IDs you've given, and assume you're wanting to update a DLL called TestLib.dll in the same directory as the EXE; I'll also assume my own locale (British English) - you'll need to change the MAKELANGID arguments as necessary:

const
  LibName = 'TestLib.dll';
  ID_FIRST  = 1000;
  ID_SECOND = 1001;

function StringIDToGroupID(ID: UINT): UINT; inline;
begin
  Result := (ID shr 4) + 1;
end;

procedure UpdateStrings(const NewFirst, NewSecond: string);
var
  Handle: THandle;
  GroupID: UINT;
  Stream: TCustomMemoryStream;
  GroupStrings: array[0..15] of string;
  StrLen: Word;
  I: Integer;
begin
  GroupID := StringIDToGroupID(ID_FIRST);
  //get existing data...
  Handle := LoadLibraryEx(LibName, 0, LOAD_LIBRARY_AS_DATAFILE);
  if Handle = 0 then RaiseLastOSError;
  try
    Stream := TResourceStream.CreateFromID(Handle, GroupID, RT_STRING);
    try
      for I := Low(GroupStrings) to High(GroupStrings) do
      begin
        Stream.ReadBufferData(StrLen);
        SetLength(GroupStrings[I], StrLen);
        Stream.ReadBuffer(PChar(GroupStrings[I])^, StrLen * SizeOf(Char));
      end;
    finally
      Stream.Free;
    end;
  finally
    FreeLibrary(Handle);
  end;
  //update the strings we're interested in...
  GroupStrings[ID_FIRST mod Length(GroupStrings)] := NewFirst;
  GroupStrings[ID_SECOND mod Length(GroupStrings)] := NewSecond;
  Stream := TMemoryStream.Create;
  try
    for I := Low(GroupStrings) to High(GroupStrings) do
    begin
      StrLen := Length(GroupStrings[I]);
      Stream.WriteData(StrLen);
      Stream.WriteBuffer(PChar(GroupStrings[I])^, StrLen * SizeOf(Char));
    end;
    //update DLL...
    Handle := BeginUpdateResource(LibName, False);
    if Handle = 0 then RaiseLastOSError;
    try
      UpdateResource(Handle, RT_STRING, PChar(GroupID),
        MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_UK), Stream.Memory, Stream.Size);
    finally
      EndUpdateResource(Handle, False);
    end;
  finally
    Stream.Free;
  end;
end;
Chris Rolliston
  • 4,788
  • 1
  • 16
  • 20
  • Thank you so much for the Detailed Explanation :) I got it working and edited to my liking. Ill go ahead and accept your answer now, man I cannot thank you enough! Much appreciated :) – TheBitmasterXor Sep 28 '14 at 20:22