如何更改存储在.exe文件中的StringTable值

时间:2014-09-28 17:46:46

标签: delphi winapi delphi-xe7

我编写了2个程序,它们都已编译,并且都包含通过.rc文件添加的“STRINGTABLES”资源。

所以,让我们调用App#1“app1.exe”和App#2“app2.exe”。

这是我的问题。在app2.exe中,我有一个字符串表,如下所示:

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

当我运行app1.exe时,我试图通过Win32 API UpdateResource()函数更新存储在app2.exe中的资源。

我可以使用Delphi的LoadStr()函数来加载字符串表中的字符串。

我的问题是我需要了解如何使用UpdateResource()来更改该字符串表中的 JUST THE STRINGS 。因此,例如,我希望app1.exe将app2.exe的字符串表从您上面看到的更改为:

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

对不起,我没有任何源代码,但是我从头开始,似乎无法解决这个问题。

我正在使用RAD Studio XE7。

如果你们还需要更多的话,那就说出来,我会尽可能地更新这个,但就像我说我刚刚开始学习TResourceStream以及所有这些资源所以我不知道有很多东西要展示。我可以告诉你的是,我不是编程新手。我很快就抓住了东西。我已经创建了一个THandle并获得了app1.exe以查看app2.exe的资源。我可以添加内容,但似乎当我尝试从StringTEdit组件添加TMemo数据时,它会显示为app2.exe中的一些奇怪的中文字母资源。所以我想知道如何格式化这些字符串,以便它们在资源和内容中正确显示。

为了更多地了解这一点,当我运行app1.exe并尝试将字符串"Hello"输入到app2.exe的字符串表中时,它给了我这个:

00230BF8  00 00 00 00                                       ••••

任何线索为什么?

我在运行程序后使用名为“Resource Hacker”的应用程序检查资源。

1 个答案:

答案 0 :(得分:7)

字符串表资源以16个块的形式存储,每个项目使用字大小的长度标记和后面的UTF-16编码字符写入。如果块中的项目未在原始RC文件中明确定义,则它仍然在编译资源中,但长度为0。

所以,让我们假设你已经仔细选择了你的身份证明属于同一组16岁。我会坚持你已经给出的身份证,并假设你&#39 ;想要在与EXE相同的目录中更新名为TestLib.dll的DLL;我还会假设我自己的语言环境(英国英语) - 您需要根据需要更改MAKELANGID参数:

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;