【问题标题】:Delphi XE2 FormatDateTime passing -693594Delphi XE2 FormatDateTime 传递 -693594
【发布时间】:2012-02-13 03:46:06
【问题描述】:

我们这里有点问题。我们已经从 Delphi 2006 升级到 Delphi XE2,并且正在转换我们的代码。

问题是,我们通过我们的应用程序和数据库记录使用值 -693594 来表示无日期(零日期)。在 Delphi 2006 中,FormatDateTime 函数会正确地将其格式化为 00/00/0000(假设日期格式为 dd/mm/yyyy)。

但是在 Delphi XE2 中,他们在 System.SysUtils 的 DateTImeToTimeStamp 函数中添加了对 ValidateTimeStampDate 的调用,这会引发错误“无效的浮点运算”。传递大于 -693594 的任何值,例如 -693593,都可以正常工作。

还有其他人遇到过这个问题和/或有人知道解决方法吗?

【问题讨论】:

  • 我可能错了,但不是说“通过任何小于 -693594 的东西”?此外,这在 XE 中有效,如何将 d2006 函数复制粘贴到项目“utils”中并始终将其添加为“uses”子句中的最后一个单元,这样编译器将使用 THAT 而不是默认的。
  • 编写自己的函数来包装 rtl 函数并检测该包装器中的标记值
  • @大卫赫弗南:+1!没有勺子喂的代码
  • 嗨,大卫。这听起来像一个好主意。但是我该怎么做呢?抱歉,我在这方面没有经验。
  • 抱歉,david,我以为您的意思是以某种方式挂钩 rtl 函数并从检测和处理该值的包装函数中调用它。我们已经编写了一个我们正在使用的替代函数,但 FormatDateTime 也在整个 VCL 中使用,所以我担心当将 -693594 传递给 VCL 控件时,这个问题会发生在 VCL 控件中。有没有办法拦截 FormatDateTime 并提供替代方案?或者如何修改 FormatDateTime 并重建 RTL?

标签: delphi delphi-xe2 delphi-2006 formatdatetime


【解决方案1】:

如果你真的很想修补到以前的行为,你可以使用这样的东西:

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

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, Size, 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 DateTimeToTimeStamp(DateTime: TDateTime): TTimeStamp;
const
  FMSecsPerDay: Single = MSecsPerDay;
  IMSecsPerDay: Integer = MSecsPerDay;
var
  LTemp, LTemp2: Int64;
begin
  LTemp := Round(DateTime * FMSecsPerDay);
  LTemp2 := (LTemp div IMSecsPerDay);
  Result.Date := DateDelta + LTemp2;
  Result.Time := Abs(LTemp) mod IMSecsPerDay;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessage(FormatDateTime('dd/mm/yyyy', -693594));
end;

initialization
  RedirectProcedure(@System.SysUtils.DateTimeToTimeStamp, @DateTimeToTimeStamp);

end.

这适用于 32 位代码。它也适用于 64 位代码,前提是旧功能和新功能都驻留在同一个可执行模块中。否则跳跃距离可能会超出 32 位整数的范围。如果您的 RTL 驻留在运行时包中,它也将不起作用。这两个限制都可以轻松解决。

此代码所做的是将所有对SysUtils.DateTimeToTimeStamp 的调用重新路由到本单元中实现的版本。本单元中的代码只是 XE2 源代码中的PUREPASCAL 版本。

满足 cmets 中列出的需求的唯一其他方法是修改和重新编译 SysUtils 单元本身,但我个人避免使用这种解决方案。

【讨论】:

  • 谢谢。我发现了一个开源项目 uallCollection,它有一个名为 HookCode 的方法,它的作用与您描述的相同,但我不知道它是否在 64 位下工作。从我听到的消息来看,该项目现在已经死了,所以我不得不对其进行修改以使其能够在 Delphi XE2 下编译。无论如何感谢您的代码。
  • 您是否介意解释一下代码是如何工作的,因为我真的很想知道。 RedirectMethod 计算一个偏移量,我们在这里实际找到的偏移量是什么?这适用于大多数 RTL 函数还是仅适用于 FormatDateTime?
  • 是的,这里没有原创内容!
  • 它是如何工作的?它将OldAddress 函数开头的代码替换为无条件跳转(JMP) 到NewAddress 的代码。 NewAddress 的代码必须具有与OldAddress 完全相同的参数集和调用约定。这种方法适用于任何功能,也可以用于实例方法。你可以用这个钩任何东西。话虽如此,它是一个非常原始的,一个好的钩子库将提供更多的功能。
  • 谢谢你,我现在明白了。我们只是用相对跳转指令替换旧函数的前 5 个字节,该指令将跳转旧函数中 TInstruction 的结束字节和新函数的第一个字节之间的距离,从而调用新函数。跨度>
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-24
  • 2011-12-19
  • 2023-03-27
  • 2012-09-22
  • 2011-11-26
相关资源
最近更新 更多