在 TWinControl 类上添加属性

发布于 2024-08-18 09:41:34 字数 2753 浏览 4 评论 0原文

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

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

谢谢建议...

编辑新想法

好吧,我想做的就是尝试覆盖 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.

I want to add a published property into TWinControl.
Is there someway to do this without the necessity of recompiling the base source code ?

If not, some way to recompile the base source code without too much troubles ?

Tks in advice...

EDIT 'CAUSE OF NEW IDEAS

Alright, What I'm thinking to do I'm trying to override the _GetMem from System.pas for classes
inherited from TWinControl.
Why ? 'Cause I'll alloc some extra space to the objects enough to an integer.
Why an integer ? 'Cause this way I can add any pointer to object.
So on the helper class to TWinControl I can make a Get an Set function to access this space of memory.
Good isn't it ? How to do this ?
Overrideing the GetMem procedure I can use the same strategy used on FastCode, create a jumper to the new procedure.

What I need now is understand how this memory alloc works InstanceSize to override this.
At all I'm studding how do Delphi do this... And to add this on DFM I will do the same way, I'll create a jumper to the filer.

Someone have some idea to add the new space in objects ? What method I need to override ? The jumper I know how to do.

Tks Again.

EDIT = Evolution

I think that I did the injection of memory.
I need to do more tests.
I've just did it, I'm not caring about optimizations at the moment, if some one would like to test it, here goes the code.
Just add the unit as the first unit of your project.

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

℡Ms空城旧梦 2024-08-25 09:41:34

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

请参阅很棒Hallvard Vassbotn 撰写的文章介绍了如何执行此操作。

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

在文章中查找以下内容:

  • TCustomFormHelper = TCustomForm 的类帮助
  • 程序 FPixelsPerInch 存储黑客
  • 注入设计时属性
  • 定义流属性

不过,当您从外部世界挂钩时,您必须以自己的方式进行流处理进入 TWinControl,但这也是可能的。

——杰罗恩

Thanks to Smasher, I remembered how the Delphi team used class helpers and a designer trick to add properties to Delphi 2007 without breaking binary compatibility with Delphi 2006.

See this great article by Hallvard Vassbotn on how to do this.

I think it solves most, if not all, of your problems.

look for these things in the article:

  • TCustomFormHelper = class helper for TCustomForm
  • The FPixelsPerInch storage hack
  • Injecting design-time properties
  • Defining the streaming properties

You'll have to work your own way to do the streaming, though, as you hook from the outside world into TWinControl, but that might be possible too.

--jeroen

缪败 2024-08-25 09:41:34

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

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

不知道类助手是否也可以在包/设计时工作?

Delphi2007 and higher have "class helpers".

You can introduce new functions and properties, but no fields/variables. So you have to store the value of you new property in a extra object (via factory or whatever) or (very ugly) in the .Tag property...

Don't know if class helper also work in packages/design time?

变身佩奇 2024-08-25 09:41:34

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

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

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

If you are using this property only on the application level, you may use the following approaches:

  • composition: bundle a reference to TWinControl object with other properties into new class, and pass/operate objects this class in your calls
  • dictionary-like functions: GetMyPropertyFor( AWinControl: TWinControl): and SetMyPropertyFor( AWinControl: TWinControl: AValue: ), which internally maintain additional property for each called TWinControl object

ADDITION: Based on your additional comment, existing Tag property should play well for your needs. You can even define 'levels' by using different values there.

私野 2024-08-25 09:41:34

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

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

请注意“您将不再能够使用运行时进行编译
包”...

No, there is no way to modify TWinControl without recompiling the VCL. Also I don't recommend changing the VCL (since having a "custom" VCL can impact the portability of your project - at the very least between Delphi installations). I would aim at making another class that inherit from TWinControl and then add your published property to this new class.

If you still want to change the VCL see the following post:
http://www.delphigroups.info/2/6/744173.html

Note that "you will no longer be able to compile using runtime
packages"...

悍妇囚夫 2024-08-25 09:41:34

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

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

我以为我有关于 TGridPanel 如何执行此操作的参考(并且 TFlowPanel 执行类似的操作),但我现在找不到它。雷·科诺普卡 (Ray Konopka) 可能在一次会议上解释了这一点,但该信息可能不在网上。

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

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

——杰罗恩

(I know the answer is a bit dense, comment on it what details you need more info about)

What you could do is what for instance TGridPanel does: it adds the Column, Row, ColumnSpan and RowSpan 'properties' to the object inspector for all components that are on the GridPanel.
That will solve your design-time support.

I thought I had a reference on how the TGridPanel does this (and TFlowPanel does similar things), but I can't find it right now. Probably Ray Konopka explained this during a conference, but that info might not be on-line.

For run-time support, you could go with class helpers.
When using class helpers, note that only the nearest visible one for a class will apply.

Another route you might follow is to use the Tag property (which is an Integer, but you can cast it to a Pointer or a TObject), but you might be bitten by others using that too.
You'd have to create your own design-time support for those tag properties though.

--jeroen

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文