【问题标题】:Add a property on TWinControl Class在 TWinControl 类上添加属性
【发布时间】:2011-01-06 09:10:27
【问题描述】:

我想将发布的属性添加到 TWinControl。 有没有办法在不需要重新编译基础源代码的情况下做到这一点?

如果没有,有什么方法可以重新编译基础源代码而不用太麻烦?

Tks 建议...

编辑“新想法的原因”

好的,我想做什么我正在尝试覆盖 System.pas 中的 _GetMem 用于类 继承自 TWinControl。 为什么 ?因为我会为对象分配一些额外的空间,足够一个整数。 为什么是整数?因为这样我可以添加任何指向对象的指针。 所以在 TWinControl 的辅助类上,我可以创建一个 Get an Set 函数来访问这个内存空间。 很好不是吗?这个怎么做 ? 覆盖 GetMem 过程我可以使用 FastCode 上使用的相同策略,创建一个到新过程的跳线。

我现在需要的是了解这个内存分配如何通过 InstanceSize 来覆盖它。 我一直在研究 Delphi 是如何做到这一点的......并且要在 DFM 上添加它,我也会这样做,我将创建一个到文件管理器的跳线。

有人想在对象中添加新空间吗?我需要重写什么方法?我知道该怎么做的跳线。

再次感谢。

编辑 = 进化

我认为我做了内存注入。 我需要做更多的测试。 我刚刚做了,我现在不关心优化,如果有人想测试它,这里有代码。 只需将该单元添加为项目的第一个单元即可。

unit uMemInjection;


interface

uses
  Controls;

type
  THelperWinControl = class Helper for TWinControl
  private
    function RfInstanceSize: Longint;
    function GetInteger: Integer;
    procedure SetInteger(const Value: Integer);
  public
    property RfInteger: Integer read GetInteger write SetInteger;
  end;

implementation

uses
  Windows;

procedure SInstanceSize;
asm
  call TWinControl.InstanceSize
end;

function THelperWinControl.GetInteger: Integer;
begin
  Result := Integer(PInteger(Integer(Self) + (Self.InstanceSize - SizeOf(Integer)))^);
end;

function THelperWinControl.RfInstanceSize: Longint;
begin
  Result := PInteger(Integer(Self) + vmtInstanceSize)^;
  Result := Result + SizeOf(Integer);
end;

/////////////////////////////////////////////// FastCode ///////////////////////////////////////////////
type
  PJump = ^TJump;
  TJump = packed record
    OpCode: Byte;
    Distance: Pointer;
  end;

function FastcodeGetAddress(AStub: Pointer): Pointer;
begin
  if PBYTE(AStub)^ = $E8 then
  begin
    Inc(Integer(AStub));
    Result := Pointer(Integer(AStub) + SizeOf(Pointer) + PInteger(AStub)^);
  end
  else
    Result := nil;
end;

procedure FastcodeAddressPatch(const ASource, ADestination: Pointer);
const
  Size = SizeOf(TJump);
var
  NewJump: PJump;
  OldProtect: Cardinal;
begin
  if VirtualProtect(ASource, Size, PAGE_EXECUTE_READWRITE, OldProtect) then
  begin
    NewJump := PJump(ASource);
    NewJump.OpCode := $E9;
    NewJump.Distance := Pointer(Integer(ADestination) - Integer(ASource) - 5);

    FlushInstructionCache(GetCurrentProcess, ASource, SizeOf(TJump));
    VirtualProtect(ASource, Size, OldProtect, @OldProtect);
  end;
end;

/////////////////////////////////////////////// FastCode /////////////////////////////////////////////// 


{ THelperWinControl }
procedure THelperWinControl.SetInteger(const Value: Integer);
begin
  PInteger(Integer(Self) + (Self.InstanceSize - SizeOf(Integer)))^ := Value;
end;

initialization
  FastcodeAddressPatch(FastcodeGetAddress(@SInstanceSize), @TWinControl.RfInstanceSize);


end.

【问题讨论】:

    标签: delphi vcl


    【解决方案1】:

    感谢Smasher,我记得 Delphi 团队如何使用类帮助器和设计技巧向 Delphi 2007 添加属性,而不会破坏与 Delphi 2006 的二进制兼容性。

    请参阅Hallvard Vassbotngreat article,了解如何执行此操作。

    我认为它可以解决大部分问题,如果不是全部的话。

    在文章中寻找这些东西:

    • TCustomFormHelper = TCustomForm 的类助手
    • FPixelsPerInch 存储技巧
    • 注入设计时属性
    • 定义流属性

    但是,当您从外部世界连接到 TWinControl 时,您必须以自己的方式进行流式传输,但这也可能是可能的。

    --杰罗恩

    【讨论】:

    • 我正在考虑一些解决方法来完成我的任务。我稍后会发布给你看看这个想法。
    • 好吧,我想做什么我正在尝试覆盖 System.pas 中的 _GetMem,用于从 TWinControl 继承的类。为什么 ?因为我会为对象分配一些额外的空间,足够一个整数?为什么是整数?因为这样我可以添加任何指向对象的指针。所以在 TWinControl 的辅助类上,我可以创建一个 Get an Set 函数来访问这个内存空间。很好不是吗?这个怎么做 ?要覆盖 GetMem 过程,我可以使用在 FastCode 上使用的相同策略,创建一个到新过程的跳线。
    • 我现在需要的是了解这个内存分配是如何工作的 InstanceSize 来覆盖它。我正在研究 Delphi 是如何做到这一点的......并且要在 DFM 上添加它,我将做同样的事情,我将创建一个到文件管理器的跳线。
    • SaCi:摆弄内存管理器不是正确的做法。可能有其他人在做同样的事情(因为内存管理器在某种程度上是可插拔的),几乎不可能协调这样的事情。 InstanceSize 也一样:内存边界检查器可能会做同样的事情。最好使用控件的所有者注入一个临时存储位置。这将是相当困难的,但不像摆弄内存管理器或 InstanceSize 那样难,所以可以做到。
    • 不幸的是,该文章链接了另一篇不再存在的文章:-/
    【解决方案2】:

    Delphi2007 及更高版本具有“类助手”。

    您可以引入新的函数和属性,但不能引入字段/变量。因此,您必须将新属性的值存储在 .Tag 属性中的额外对象中(通过工厂或其他方式)或(非常丑陋)...

    不知道类助手是否也适用于包/设计时?

    【讨论】:

    • 是的,问题出在字段上。标签可以用在其他地方。其他对象?但是在哪里链接呢?反正我需要其他字段。
    • @SaCi:你不能在不重新编译的情况下引入字段,因为这会改变对象布局。您必须继承 TWinControl,添加您的自定义字段并重新编译。
    【解决方案3】:

    如果您仅在应用程序级别使用此属性,则可以使用以下方法:

    • 组合:将对 TWinControl 对象的引用与其他属性捆绑到新类中,并在调用中传递/操作此类对象
    • 类字典函数:GetMyPropertyFor(AWinControl:TWinControl): 和 SetMyPropertyFor(AWinControl:TWinControl:AValue:),它们在内部为每个调用的 TWinControl 对象维护附加属性

    补充:根据您的补充评论,现有的 Tag 属性应该可以很好地满足您的需求。您甚至可以在此处使用不同的值来定义“级别”。

    【讨论】:

    • 标签?我需要一个属性来设置我将使用“管理员、基本、高级等”的访问权限类型以及“客户报告、车辆编辑等......”权限段的类型。
    • 好的,按位设置:1 - 管理员 2 - 基本 4 - 高级 8 - 客户报告 16 - 车辆编辑等因此设置特定位并获取结果整数以转到属性标签。你有整个 31 位!
    【解决方案4】:

    不,如果不重新编译 VCL,就无法修改 TWinControl。此外,我不建议更改 VCL(因为拥有“自定义”VCL 会影响项目的可移植性——至少在 Delphi 安装之间)。我的目标是创建另一个继承自 TWinControl 的类,然后将您发布的属性添加到这个新类中。

    如果您仍想更改 VCL,请参阅以下帖子: http://www.delphigroups.info/2/6/744173.html

    请注意,“您将无法再使用运行时进行编译 包”...

    【讨论】:

    • 哼...我想象中的。我面临的问题是因为我需要对每个 VCL 对象都有一个特定的属性。我想这样做的唯一方法是改变 TWinControl,你能想到其他解决方案吗? Tks
    • 这个属性是什么?它是干什么用的?
    • 我正在使用我的软件中的权限。我被认为是一种方法来设置 VCL 的可见/启用,只需使用带有 TWinControl 作为参数的过程。在那里我将检查它的属性值并设置对它的访问。这样,我可以创建一个表单,将其作为参数发送,获取其中的所有组件并设置对 itens 的对应访问权限。如果我没有这样做,我需要检查代码上的每个项目并设置访问权限。
    • Saci,您的问题可能还有其他解决方案(控制可见性“类别”的设计时配置)。尝试发布一个关于它的新问题,看看其他人有什么想法。
    【解决方案5】:

    (我知道答案有点密集,评论一下您需要更多信息的详细信息)

    您可以做的就是例如 TGridPanel 所做的:它将 Column、Row、ColumnSpan 和 RowSpan“属性”添加到 GridPanel 上所有组件的对象检查器。 这将解决您的设计时支持问题。

    我以为我有关于 TGridPanel 如何做到这一点的参考资料(和 TFlowPanel 做类似的事情),但我现在找不到它。 Ray Konopka 可能在一次会议上解释了这一点,但该信息可能不在网上。

    对于运行时支持,您可以使用类助手。 使用类助手时,请注意,只有最近的可见类才会应用。

    您可能遵循的另一条路线是使用 Tag 属性(它是一个 Integer,但您可以将其强制转换为 Pointer 或 TObject),但您也可能会被其他人使用它。 不过,您必须为这些标签属性创建自己的设计时支持。

    --杰罗恩

    【讨论】:

    • 类助手不能添加字段怎么办? OP 声明他需要在 André 的答案的评论中添加一个字段。
    • Smasher 感谢您的评论,这让我想起了 Delphi 团队为使 Delphi 2007 二进制文件与 Delphi 2006 兼容所做的工作。请参阅我将很快发布的其他答案。
    • Jeroen,很有可能您正在寻找 TFlowPanel 如何添加 ControlIndex 属性的文章:“向组件添加 'fake' 属性” 作者:Tjipke van der Plaats,edn.embarcadero.com/article/33448
    猜你喜欢
    • 1970-01-01
    • 2021-03-08
    • 2020-06-17
    • 1970-01-01
    • 2020-06-08
    • 1970-01-01
    • 1970-01-01
    • 2011-07-26
    • 1970-01-01
    相关资源
    最近更新 更多