覆盖Delphi函数System.Round

前端之家收集整理的这篇文章主要介绍了覆盖Delphi函数System.Round前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我刚刚发现我必须重新实现的软件广泛使用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位整数.

这是怎么做的.

  1. procedure PatchCode(Address: Pointer; const NewCode; Size: Integer);
  2. var
  3. OldProtect: DWORD;
  4. begin
  5. if VirtualProtect(Address,Size,PAGE_EXECUTE_READWRITE,OldProtect) then
  6. begin
  7. Move(NewCode,Address^,Size);
  8. FlushInstructionCache(GetCurrentProcess,Address,Size);
  9. VirtualProtect(Address,OldProtect,@OldProtect);
  10. end;
  11. end;
  12.  
  13. type
  14. PInstruction = ^TInstruction;
  15. TInstruction = packed record
  16. Opcode: Byte;
  17. Offset: Integer;
  18. end;
  19.  
  20. procedure RedirectProcedure(OldAddress,NewAddress: Pointer);
  21. var
  22. NewCode: TInstruction;
  23. begin
  24. NewCode.Opcode := $E9;//jump relative
  25. NewCode.Offset :=
  26. NativeInt(NewAddress)-NativeInt(OldAddress)-SizeOf(NewCode);
  27. PatchCode(OldAddress,NewCode,SizeOf(NewCode));
  28. end;
  29.  
  30. function System_Round: Pointer;
  31. asm
  32. MOV EAX,offset System.@Round
  33. end;
  34.  
  35. procedure _ROUND;
  36. asm
  37. { -> FST(0) Extended argument }
  38. { <- EDX:EAX Result }
  39.  
  40. // your implementation goes here
  41. end;
  42.  
  43. initialization
  44. RedirectProcedure(System_Round,@_ROUND);

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

  1. function MyRound(x: Extended): Int64;
  2. begin
  3. // your implementation goes here
  4. end;
  5.  
  6. procedure _ROUND;
  7. var
  8. x: Extended;
  9. asm
  10. { -> FST(0) Extended argument }
  11. { <- EDX:EAX Result }
  12.  
  13. FSTP TBYTE PTR [x]
  14. CALL MyRound
  15. end;

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

猜你在找的Delphi相关文章