加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

覆盖Delphi函数System.Round

发布时间:2020-12-15 03:50:15 所属栏目:大数据 来源:网络整理
导读:我刚刚发现我必须重新实现的软件广泛使用System.Round().问题是这个函数使用“Bankers rounding”,并且不能像Math.RoundTo()(rmDown,rmUp,rmNearest,rmTruncate)那样改变行为. 我必须将行为更改为“正常舍入”(12.5 – 13 NOT 12.5 – 12)…所以我想全局覆盖
我刚刚发现我必须重新实现的软件广泛使用System.Round().问题是这个函数使用“Bankers rounding”,并且不能像Math.RoundTo()(rmDown,rmUp,rmNearest,rmTruncate)那样改变行为.

我必须将行为更改为“正常舍入”(12.5 – > 13 NOT 12.5 – > 12)…所以我想全局覆盖System.Round().我想这样做,因为Round()被使用了很多次,我不想手动更改它们.

这怎么可能?

解决方法

警告:虽然下面的答案解决了所提出的问题,但我建议没有人使用它.如果要执行与Round不同的舍入,则编写并调用专用函数.

您可以使用运行时代码钩子来更改Round的实现.

皱纹是得到Round函数的地址有点棘手,因为它是一个内在的.您还必须小心遵循使用的调用约定.输入值在x87堆栈寄存器ST(0)中传递,返回值在EDX:EAX中为64位整数.

这是怎么做的.

procedure PatchCode(Address: Pointer; const NewCode; Size: Integer);
var
  OldProtect: DWORD;
begin
  if VirtualProtect(Address,Size,PAGE_EXECUTE_READWRITE,OldProtect) then 
  begin
    Move(NewCode,Address^,Size);
    FlushInstructionCache(GetCurrentProcess,Address,Size);
    VirtualProtect(Address,OldProtect,@OldProtect);
  end;
end;

type
  PInstruction = ^TInstruction;
  TInstruction = packed record
    Opcode: Byte;
    Offset: Integer;
  end;

procedure RedirectProcedure(OldAddress,NewAddress: Pointer);
var
  NewCode: TInstruction;
begin
  NewCode.Opcode := $E9;//jump relative
  NewCode.Offset := 
    NativeInt(NewAddress)-NativeInt(OldAddress)-SizeOf(NewCode);
  PatchCode(OldAddress,NewCode,SizeOf(NewCode));
end;

function System_Round: Pointer;
asm
  MOV     EAX,offset System.@Round
end;

procedure _ROUND;
asm
        { ->    FST(0)  Extended argument       }
        { <-    EDX:EAX Result                  }

        // your implementation goes here
end;

initialization
  RedirectProcedure(System_Round,@_ROUND);

如果您宁愿在Pascal中实现您的版本而不是asm,那么您需要使_ROUND的非标准调用约定适应标准的Delphi调用约定.像这样:

function MyRound(x: Extended): Int64;
begin
  // your implementation goes here
end;

procedure _ROUND;
var
  x: Extended;
asm
        { ->    FST(0)  Extended argument       }
        { <-    EDX:EAX Result                  }

        FSTP    TBYTE PTR [x]
        CALL    MyRound
end;

请注意,我假设您的程序的目标是32位.如果您需要以64位为目标,则原则大致相同,但细节明显不同.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读