Delphi 在新钩住的LoadResString方法中读取stringlist时获取StackOverflow异常

Delphi 在新钩住的LoadResString方法中读取stringlist时获取StackOverflow异常,delphi,winapi,hook,resourcestring,Delphi,Winapi,Hook,Resourcestring,仅供参考-以下是我的代码,我在NewLoadResString函数中得到StackOverflow异常。这个案例就像我推荐了两个stringlist,即RecstrNamedMap和NewStringValueList。 这里RecStrNameIdMap是存储名称和字符串标识符映射的哈希字符串列表。这样我就可以引用资源字符串名称作为其标识符,即ID NewStringValueList是一个字符串列表,其中包含少数资源字符串的新值 我已经在system.LoadResString方法上连接了N

仅供参考-以下是我的代码,我在NewLoadResString函数中得到StackOverflow异常。这个案例就像我推荐了两个stringlist,即RecstrNamedMap和NewStringValueList。 这里RecStrNameIdMap是存储名称和字符串标识符映射的哈希字符串列表。这样我就可以引用资源字符串名称作为其标识符,即ID

NewStringValueList是一个字符串列表,其中包含少数资源字符串的新值

我已经在system.LoadResString方法上连接了NewLoadResString方法。在新方法中,我检查NewStringValueList中给定的resourcestring是否有新值,然后获取该值并返回新值而不是旧的声明值

第行发生堆栈溢出异常*

如果RecstrNamedMap.IndexOfName(IntToStr(ResStringRec^.Identifier))> -1那么

* 任何人都可以检查我为什么会出现这个错误

unit UnitTest;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, IniFiles, StdCtrls;

type
  TForm2 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;


type
  TMethodHook = class
  private
    aOriginal : packed array[ 0..4 ] of byte;
    pOldProc, pNewProc : pointer;
    pPosition : PByteArray;
  public
    constructor Create( pOldProc, pNewProc : pointer );
    destructor Destroy; override;
  end;


var
  Form2: TForm2;

implementation

{$R *.dfm}

ResourceString
  RS_1 = 'ABC';
  RS_2 = 'XYZ';

procedure TForm2.Button1Click(Sender: TObject);
var
  aMethodHook: TMethodHook;
  RecStrNameIdMap: THashedStringList;
  NewStringValueList: TStringList;

  {Hookup aNewProcedure on aOriginalProcedure}
  procedure RegisterProcedures(aOriginalProcedure, aNewProcedure: pointer);
  begin
    if Assigned(aOriginalProcedure) and Assigned(aNewProcedure) then
      aMethodHook := TMethodHook.Create( aOriginalProcedure, aNewProcedure);
  end;

  {Replacement for System.LoadResString}
  function NewLoadResString(ResStringRec: PResStringRec): String;
  var
    Buffer: array [0..4095] of char;
  begin
    if ResStringRec = nil then Exit;
    if ResStringRec.Identifier >= 64 * 1024 then
    begin
      Result := PChar(ResStringRec.Identifier);
    end
    else
    begin
      if RecStrNameIdMap.IndexOfName(IntToStr(ResStringRec^.Identifier)) > -1 then
      begin
        Result := NewStringValueList.Values[
          RecStrNameIdMap.Values[IntToStr(ResStringRec^.Identifier)]];
      end
      else
      begin
        SetString(Result, Buffer,
          LoadString(FindResourceHInstance(ResStringRec.Module^),
            ResStringRec.Identifier, Buffer, SizeOf(Buffer)));
      end;
    end;
  end;

  procedure CreateNameIdMapping;
  begin
    {This is done to get string name from ID}
    RecStrNameIdMap.CaseSensitive := False;
    RecStrNameIdMap.Add(Inttostr(PResStringRec(RS_2)^.Identifier)+'='+'XYZ');
  end;

begin
  aMethodHook := nil;
  try
    RecStrNameIdMap := THashedStringList.Create;
    NewStringValueList := TStringList.Create;

    CreateNameIdMapping;

    {Create new value list for ResourceStrings}
    NewStringValueList.Add('XYZ'+'='+'new value for ResourceString RS_2');
    RegisterProcedures(@System.LoadResString, @NewLoadResString);

    {This should return 'new value for ResourceString RS_2' instead of 'XYZ'}
    ShowMessage(RS_2);

    {This should return 'ABC' - no change in value}
    ShowMessage(RS_1);
  finally
    aMethodHook.Free;
    RecStrNameIdMap.Free;
    NewStringValueList.Free;
  end;
end;

{ TMethodHook }

constructor TMethodHook.Create(pOldProc, pNewProc: pointer);
var
  iOffset : integer;
  iMemProtect : cardinal;
  i : integer;
begin
  Self.pOldProc := pOldProc;
  Self.pNewProc := pNewProc;

  pPosition := pOldProc;
  iOffset := integer( pNewProc ) - integer( pointer( pPosition ) ) - 5;

  for i := 0 to 4 do aOriginal[ i ] := pPosition^[ i ];

  VirtualProtect( pointer( pPosition ), 5, PAGE_EXECUTE_READWRITE,
    @iMemProtect );

  pPosition^[ 0 ] := $E9;
  pPosition^[ 1 ] := byte( iOffset );
  pPosition^[ 2 ] := byte( iOffset shr 8 );
  pPosition^[ 3 ] := byte( iOffset shr 16 );
  pPosition^[ 4 ] := byte( iOffset shr 24 );
end;

destructor TMethodHook.Destroy;
var
  i : integer;
begin
  for i := 0 to 4 do pPosition^[ i ] := aOriginal[ i ];
  inherited;
end;

end.

似乎替换过程不能是嵌套的例程。
如合同所述:

过程类型允许您将过程和函数视为可分配给变量或传递给其他过程和函数的值

嵌套的过程和函数(在其他例程中声明的例程)不能用作过程值,预定义的过程和函数也不能用作过程值

过程类型是指针。虽然嵌套例程不能用作过程类型,但我假设指向嵌套例程的指针不能用作过程参数,或者此操作可能会产生与本例类似的不可预测的结果。
程序连接正确(您已连接);我提取了过程
NewLoadResString
,stackoverflow错误不再发生。
弹出的
resourcestring
始终是旧的,但我在
newloadRestring
过程中没有做任何更改。
整个编辑单元如下

单元测试;
界面
使用
窗口、消息、系统工具、变体、类、图形、控件、窗体、,
对话框、文件、stdctrl;
类型
TForm2=类别(TForm)
按钮1:t按钮;
程序按钮1点击(发送方:ToObject);
私有的
{私有声明}
RecstrNamedMap:THashedStringList;
NewStringValueList:TStringList;
平民的
{公开声明}
终止
类型
TMethodHook=类
私有的
a原始:字节的压缩数组[0..4];
pOldProc,pNewProc:指针;
前提:PByteArray;
平民的
构造函数创建(pOldProc,pNewProc:pointer);
毁灭者毁灭;推翻
终止
变量
表2:TForm2;
实施
{$R*.dfm}
资源字符串
RS_1=‘ABC’;
RS_2='XYZ';
{替换System.LoadResString}
函数NewLoadResString(ResString:PResStringRec):字符串;
变量
缓冲区:字符的数组[0..4095];
开始
如果ResStringRec=nil,则退出;
如果resString.Identifier>=64*1024,则
开始
结果:=PChar(resString.Identifier);
终止
其他的
开始
如果RecStrNameIdMap.IndexOfName(IntToStr(ResStringRec^.Identifier))>-1,则
开始
结果:=NewStringValueList.Values[
recstrNamedMap.Value[IntToStr(ResStringRec^.Identifier)];
终止
其他的
开始
设置字符串(结果、缓冲区、,
LoadString(FindResourceHInstance(resString.Module^),
resString.Identifier,Buffer,SizeOf(Buffer));
终止
终止
终止
程序TForm2.按钮1单击(发件人:ToObject);
变量
aMethodHook:TMethodHook;
{在原始过程上连接一个过程}
过程寄存器过程(aOriginalProcedure,aNewProcedure:指针);
开始
如果分配(原始程序)和分配(新程序),则
aMethodHook:=TMethodHook.Create(aooriginalprocedure,aNewProcedure);
终止
程序CreateNameIdMapping;
开始
{这样做是为了从ID获取字符串名}
recstrNamedMap.CaseSensitive:=False;
recstrNamedMap.Add(Inttostr(按tringrec(RS_2)^.Identifier)+'='+'XYZ');
终止
开始
aMethodHook:=零;
RecstrNamedMap:=THashedStringList.Create;
NewStringValueList:=TStringList.Create;
尝试
CreateNameIdMapping;
{为ResourceString创建新的值列表}
NewStringValueList.Add('XYZ'+'='+'ResourceString RS_2'的新值);
注册程序(@System.loadrestring,@newloadrestring);
{这应该返回'ResourceString RS_2'的新值,而不是'XYZ'}
ShowMessage(RS_2);
{这应该返回'ABC'-值没有变化}
ShowMessage(RS_1);
最后
免费;
recstrNamedMap.Free;
NewStringValueList.Free;
终止
终止
{TMethodHook}
构造函数TMethodHook.Create(pOldProc,pNewProc:pointer);
变量
iOffset:整数;
I保护:红衣主教;
i:整数;
开始
Self.pOldProc:=pOldProc;
Self.pNewProc:=pNewProc;
条件:=pOldProc;
iOffset:=整数(pNewProc)-整数(指针(位置))-5;
对于i:=0到4,执行原始[i]:=position^[i];
VirtualProtect(指针(预设),第5页,执行,读写,
@i保护);
条件^[0]:=$E9;
位置^[1]:=字节(iOffset);
位置^[2]:=字节(IOFFSETSHR8);
位置^[3]:=字节(IOFFSETSHR16);
位置^[4]:=字节(IOFFSETSHR 24);
终止
析构函数TMethodHook.Destroy;
变量
i:整数;
开始
对于i:=0到4,则相反^[i]:=a原始[i];
继承;
终止
终止

似乎替换过程不能是嵌套的例程。
如合同所述:

过程类型允许您将过程和函数视为可分配给变量或传递给其他过程和函数的值

嵌套的过程和函数(在其他例程中声明的例程)不能用作过程值,预定义的过程和函数也不能用作过程值

过程类型是指针。虽然嵌套例程不能用作过程类型,但我假设Poi