Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Re-size Instance Class Memory Allocation

I Use This Sample for Change Class at run-time :

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;

type
  TMyButton = class(TButton)
  Private
    FLogEvent : TNotifyEvent;
  public
    Property  Log : TNotifyEvent Read FLogEvent Write FLogEvent;
    procedure Click; override;
  end;

procedure TMyButton.Click;
begin
  Inherited;
  if Assigned(FLogEvent) then
     FLogEvent(Self);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  PatchInstanceClass(Button1, TMyButton);
end;

My Problem is that TMyButton Size is different with TButton Because I Add this a NotifyEvent to TMyButton. My Question is how can I Re-size memory Allocation for NewClass same Instance. :-)

like image 527
Farzad Fun Avatar asked Jul 07 '26 20:07

Farzad Fun


1 Answers

While I agree with the others - you should rethink your approach, as this smells a bit hacky. However I like hacky things - so I am going to show you how you can achieve what you are asking for.

The key is to not put anything inside your new class (as the InstanceSize needs to be the same) put it somewhere else - if you are using a newer Delphi version (2010 or higher) you can do it like this. Otherwise you need to modify the code a bit but I guess you get the idea:

uses
  Generics.Collections;

procedure PatchInstanceClass(Instance: TObject; NewClass: TClass);
begin
  if Assigned(Instance) and Assigned(NewClass)
    and NewClass.InheritsFrom(Instance.ClassType)
    and (NewClass.InstanceSize = Instance.InstanceSize) then
  begin
    PPointer(Instance)^ := NewClass;
  end;
end;

type
  TMyButton = class(TButton)
  private
    function GetLogEvent: TNotifyEvent;
    procedure SetLogEvent(const Value: TNotifyEvent);
  public
    destructor Destroy; override;
    procedure Click; override;

    property LogEvent: TNotifyEvent read GetLogEvent write SetLogEvent;
  end;

  TMyButtonHelper = class helper for TMyButton
  private
    class var fLogEvents: TDictionary<TObject, TNotifyEvent>;
  public
    class constructor Create;
    class destructor Destroy;
  end;

{ TMyButtonHelper }

class constructor TMyButtonHelper.Create;
begin
  fLogEvents := TDictionary<TObject, TNotifyEvent>.Create;
end;

class destructor TMyButtonHelper.Destroy;
begin
  fLogEvents.Free;
end;

{ TMyButton }

destructor TMyButton.Destroy;
begin
  fLogEvents.Remove(Self);
  inherited;
end;

procedure TMyButton.Click;
begin
  inherited;
  if Assigned(LogEvent) then
     LogEvent(Self);
end;

function TMyButton.GetLogEvent: TNotifyEvent;
begin
  fLogEvents.TryGetValue(Self, Result);
end;

procedure TMyButton.SetLogEvent(const Value: TNotifyEvent);
begin
  fLogEvents.AddOrSetValue(Self, Value);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  PatchInstanceClass(Button1, TMyButton);
  TMyButton(Button1).LogEvent := Button1Click;
end;
like image 70
Stefan Glienke Avatar answered Jul 09 '26 11:07

Stefan Glienke