For reference - following is my code, where I am getting StackOverflow exception in NewLoadResString function. The case is like I have creted two stringlist i.e. RecStrNameIdMap and NewStringValueList. Here RecStrNameIdMap is hash string list to store name and string Identifier mapping. So that I can refer Resource string name for its Identifier i.e ID.
NewStringValueList is a string list which contains new values for few of the Resourcestrings.
I have hooked up NewLoadResString method on system.LoadResString method. In new method I am checking if I have new value for given resourcestring in NewStringValueList then get that value and return new instead of old declared value.
Stack Overflow exception occurs on line *
if RecStrNameIdMap.IndexOfName(IntToStr(ResStringRec^.Identifier)) > -1 then
* Can anyone please check why I am getting this error.
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.
It seems that the replacing procedure cannot be a nested routine.
As stated in the documentation:
...
A procedural type is a Pointer. While a nested routine cannot be used as procedural type, I assume that a Pointer to a nested routine cannot be used as a procedure parameter or this action may have unpredictable result like in this case.
The procedure is hooked correctly (you did); I extracted the procedure
NewLoadResString
and the stackoverflow error does not happen anymore.The
resourcestring
which pops up is always the old one but I made no changes in theNewLoadResString
procedure.The entire edited unit follows.