delphi – 修补实例类要求基类在同一单元中?
我正在使用以下函数来修补现有对象的实例类。
原因是我需要修补第三方类的受保护的功能。 procedure PatchInstanceClass(Instance: TObject; NewClass: TClass); type PClass = ^TClass; begin if Assigned(Instance) and Assigned(NewClass) and NewClass.InheritsFrom(Instance.ClassType) and (NewClass.InstanceSize = Instance.InstanceSize) then begin PClass(Instance)^ := NewClass; end; end; 但是由于某些原因,代码只在基类在我自己的单元中定义时有效。 这不行 unit Unit1; interface uses Windows,Messages,SysUtils,Variants,Classes,Graphics,Controls,Forms,Dialogs,StdCtrls,wwdblook,Wwdbdlg; type TwwDBLookupComboDlg = class(Wwdbdlg.TwwDBLookupComboDlg); // This is necessary TForm1 = class(TForm) Button1: TButton; wwDBLookupComboDlg1: TwwDBLookupComboDlg; procedure FormCreate(Sender: TObject); end; var Form1: TForm1; implementation {$R *.dfm} type TButtonEx = class(TButton) end; TwwDBLookupComboDlgEx = class(TwwDBLookupComboDlg) end; procedure PatchInstanceClass(Instance: TObject; NewClass: TClass); type PClass = ^TClass; begin if Assigned(Instance) and Assigned(NewClass) and NewClass.InheritsFrom(Instance.ClassType) and (NewClass.InstanceSize = Instance.InstanceSize) then begin PClass(Instance)^ := NewClass; end; end; procedure TForm1.FormCreate(Sender: TObject); begin PatchInstanceClass(Button1,TButtonEx); showmessage(Button1.ClassName); // Good: TButtonEx PatchInstanceClass(wwDBLookupComboDlg1,TwwDBLookupComboDlgEx); showmessage(wwDBLookupComboDlg1.ClassName); // Bad: TwwDBLookupComboDlg (should be TwwDBLookupComboDlgEx) end; end. 这个工作(唯一的区别是重新定义了TwwDBookupComboDlg) type TwwDBLookupComboDlg = class(wwdbdlg.TwwDBLookupComboDlg); // <------ added! procedure TForm1.FormCreate(Sender: TObject); begin PatchInstanceClass(wwDBLookupComboDlg1,TwwDBLookupComboDlgEx); showmessage(wwDBLookupComboDlg1.ClassName); // shows TwwDBLookupComboDlgEx :-) end; end. 在这个例子中,我发现这种现象只发生在TwwDBookupComboDlg,但不是与TButton。我不知道为什么不幸的是,wwdbdlg.pas不是免费的。 更新: 我发现:如果我比较TButton和TButtonEx,这两个值都是608。 如果我比较wwdlg.TwwDBLookupComboDlg和TwwDBookupComboDlgEx,那么大小是940和944。 如果我比较Unit1.TwwDBLookupComboDlg和TwwDBookupComboDlgEx,那么大小是944和944。 所以…实际的问题是:如果我定义了TwwDBookupComboDlg = class(Wwdbdlg.TwwDBLookupComboDlg); ,实例大小增长4字节! 一个简单的示范。这个程序: {$APPTYPE CONSOLE} uses Dialogs; type TOpenDialog = class(Vcl.Dialogs.TOpenDialog); TOpenDialogEx = class(TOpenDialog); begin Writeln(Vcl.Dialogs.TOpenDialog.InstanceSize); Writeln(TOpenDialog.InstanceSize); Writeln(TOpenDialogEx.InstanceSize); Readln; end. 发射 188 192 192 当用Delphi 2007编译时。但是,使用XE7的输出是: 220 220 220 虽然此问题发生在TOpenDialog上,但TCommonDialog不会发生此问题。 更新2:最小的例子 program Project1; {$APPTYPE CONSOLE} uses Classes,Dialogs; type TOpenDialog = class(TCommonDialog) private FOptionsEx: TOpenOptionsEx; end; TOpenDialogEx = class(Project1.TOpenDialog); begin Writeln(Project1.TOpenDialog.InstanceSize); // 100 Writeln(TOpenDialogEx.InstanceSize); // 104 Readln; end. 解决方法
对于旧版本的编译器而言,这似乎是编译器行为的奇怪(也许是一个错误)。我已经把它删除了以下代码:
{$APPTYPE CONSOLE} type TClass1 = class FValue1: Double; FValue2: Integer; end; TClass2 = class(TClass1); begin Writeln(TClass1.InstanceSize); Writeln(TClass2.InstanceSize); Writeln; Writeln(Integer(@TClass1(nil).FValue1)); Writeln(Integer(@TClass1(nil).FValue2)); Writeln; Writeln(Integer(@TClass2(nil).FValue1)); Writeln(Integer(@TClass2(nil).FValue2)); Readln; end. 在Delphi 6上的输出是: 20 24 8 16 8 16 编译器似乎对于两个类声明来处理对齐方式不同。该类包含一个双字节,具有8字节对齐,后面是一个4字节的整数。所以类最终应该有4个字节的填充,使其大小为8的倍数。第一个类没有这个填充,第二个类没有这个填充。 这里的代码证明了对字段的偏移量没有改变,并且差异仅在于存在以实现对齐的类型的结尾处的填充。 显然你不会得到Delphi 2007编译器的补丁。我的怀疑是,您可以删除检查NewClass.InstanceSize = Instance.InstanceSize和您的修补程序代码仍将行为正确。然后,您的职责是确保您不要在修补课中添加任何数据成员。 另一种方法可能是使用不同的机制来修补代码。没有更多关于原始问题的知识,我很难说出可能是什么。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |