tags:

views:

350

answers:

5

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.
+1  A: 

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"...

AlexV
Hun... I imagined.The problem that I faced is 'cause I need to have an a particular property to every VCL object.The only way I thought to do this is changing the TWinControl, can you think in other solution ?Tks
SaCi
What is this property? What's is it used for?
AlexV
I'm working with permissions in my software.And I'm thought in a method to set the visible/enable of the VCL just using a procedure with a TWinControl as parameter. There I'll check the value of it's property and set the access to it.This way I can create a form send it as parameter get all the component in it and set the correspondent access to the itens. If i didn't do this I need to check for every item on code and set the access.
SaCi
Saci, there might be other solutions to your problem (design-time configuration for "categories" of control visibility). Try posting a new question about it and see what ideas other people have.
Rob Kennedy
+2  A: 

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.

seletit
I need it in design time. :/
SaCi
Tag ?I need a property to set the kind of the access I'll use "Admin, Basic, Advanced etc"And other to the kind of the permissions segment "Client report, Vehicle edit etc...."
SaCi
Ok, make it bitwise:1 - admin2 - basic4 - advanced8 - Client report16 - vehicle editetcSo set specific bits and get resulting integer to go to property tag. You have whole 31 bits there!
seletit
+4  A: 

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?

André
Yea, the problem is the fields. Tag can be used in other places.Other object ? but where to link it ? I need other field anyway.
SaCi
@SaCi: You cannot introduce fields without recompiling because this changes the object layout. You will have to subclass `TWinControl`, add your custom fields and the recompile.
Smasher
class helpers don't work at design time, only at run-time
Jeroen Pluimers
A: 

(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

Jeroen Pluimers
What about the fact that class helpers cannot add fields? The OP states that he needs to add a field in the comment to André's answer.
Smasher
Smasher thanks for your comment, it made me remind what the Delphi team did to make Delphi 2007 binary compatible with Delphi 2006. See my other answer that I'll post shortly.
Jeroen Pluimers
Jeroen, in the slim chance that you were looking for the article how TFlowPanel adds the ControlIndex property: "Adding a 'fake' property to a component" By: Tjipke van der Plaats, http://edn.embarcadero.com/article/33448
Sertac Akyuz
+1; Yes, I was!. Thanks!
Jeroen Pluimers
+5  A: 

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

Jeroen Pluimers
Very very very very good.Tks too much!Very good hint!
SaCi
@SaCi: you are welcome; let us know if it works for you.
Jeroen Pluimers
I'm thinking about some workarounds to complete my task.I'll post it later to you take a look on the idea.
SaCi
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 ?To override the GetMem procedure I can use the same strategy used on FastCode, create a jumper to the new procedure.
SaCi
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.
SaCi
SaCi: fiddling with the memory manager is not the correct way to do it. There might be someone else doing the same thing (since memory managers are somewhat pluggable), it is virtually impossible to coordinate stuff like that. Same for InstanceSize: a memory bounds checker might do the same. It is better to use the owner of the control to inject a temporary storage location. It is going to be quite hard, but not as hard as fiddling with the memory manager or with InstanceSize, so it can be done.
Jeroen Pluimers