2012-02-13 7 views
3

Nous avons un peu un problème ici. Nous avons mis à jour Delphi 2006 vers Delphi XE2 et sommes en train de convertir notre code.Delphi XE2 FormatDateTime passing -693594

Le problème est que nous utilisons la valeur -693594 via nos enregistrements d'application et de base de données pour représenter aucune date (date zéro). Dans Delphi 2006, la fonction FormatDateTime formaterait correctement ceci en tant que 00/00/0000 (avec un format de date de jj/mm/aaaa).

Toutefois, dans Delphi XE2, ils ont ajouté un appel à ValidateTimeStampDate dans la fonction DateTImeToTimeStamp dans System.SysUtils, ce qui déclenche l'erreur "opération en virgule flottante non valide". passer quelque chose de plus grand que -693594, comme -693593, fonctionne bien.

Est-ce que quelqu'un d'autre a eu ce problème et/ou quelqu'un connaît-il un travail?

+0

Je peux me tromper, mais ne veut pas dire « passer quoi que ce soit moins -693594 »? aussi, cela fonctionne dans XE, que diriez-vous de copier-coller la fonction d2006 dans le projet "utils" et ajoutez toujours cela comme dernière unité dans la clause "uses", de cette manière le compilateur utilisera CELA au lieu de celui par défaut. – ComputerSaysNo

+4

Ecrivez votre propre fonction qui enveloppe la fonction rtl et détecte la valeur sentinelle dans cette enveloppe –

+0

@David Heffernan: +1! Pas de code cuillère – menjaraz

Répondre

3

Si vous êtes vraiment désespéré de patcher revenir au comportement précédent, vous pouvez utiliser quelque chose comme ceci:

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. 

Cela fonctionne pour 32 bit code. Il fonctionnera également pour le code 64 bits à condition que les anciennes et les nouvelles fonctions résident dans le même module exécutable. Sinon, la distance de saut peut dépasser la plage d'un entier de 32 bits. Cela ne fonctionnera pas non plus si votre RTL réside dans un package d'exécution. Ces deux limitations peuvent être facilement corrigées.

Ce code permet de rediriger tous les appels vers SysUtils.DateTimeToTimeStamp vers la version implémentée dans cet appareil. Le code dans cette unité est juste la version PUREPASCAL de la source XE2.

La seule autre approche qui répond aux besoins décrits dans vos commentaires est de modifier et recompiler l'unité SysUtils elle-même, mais j'évite personnellement ce genre de solution.

enter image description here

+0

Merci pour cela. J'ai trouvé un projet open source uallCollection qui a une méthode appelée HookCode qui fait la même chose que ce que vous avez décrit mais je ne sais pas si ça fonctionne en dessous de 64bit. Le projet est mort maintenant de ce que j'ai entendu, donc j'ai dû le modifier pour le compiler sous Delphi XE2. En tout cas merci pour le code. –

+0

Pourriez-vous m'expliquer comment fonctionne le code, comme j'aimerais vraiment le savoir. Le RedirectMethod calcule un décalage, le décalage de ce que nous trouvons réellement ici? Cela fonctionnerait-il pour la plupart des fonctions RTL ou seulement pour FormatDateTime? –

+0

ouais, il n'y a rien d'original ici! –