How to access private methods without helpers?

If there is extended RTTI info generated for the class private members - fields and/or methods you can use it to gain access to them.

Of course, accessing through RTTI is way slower than it was through class helpers.

Accessing methods:

var
  Base: TBase2;
  Method: TRttiMethod;

  Method := TRttiContext.Create.GetType(TBase2).GetMethod('UsefullButHidden');
  Method.Invoke(Base, []);

Accessing variables:

var
  Base: TBase;
  v: TValue;

  v := TRttiContext.Create.GetType(TBase).GetField('FMemberVar').GetValue(Base);

Default RTTI information generated for RTL/VCL/FMX classes is following

  • Fields - private, protected, public, published
  • Methods - public, published
  • Properties - public, published

Unfortunately, that means accessing private methods via RTTI for core Delphi libraries is not available. @LU RD's answer covers hack that allows private method access for classes without extended RTTI.

Working with RTTI


There is still a way to use class helpers for access of private methods in Delphi 10.1 Berlin:

type
  TBase2 = class(TObject) 
  private
    procedure UsefullButHidden;  
    procedure VirtualHidden; virtual;
    procedure PreviouslyProtected; override;
  end;

  TBase2Helper = class helper for TBase2
    procedure OpenAccess;
  end;

  procedure TBase2Helper.OpenAccess;
  var
    P : procedure of object;
  begin
    TMethod(P).Code := @TBase2.UsefullButHidden;
    TMethod(P).Data := Self;
    P; // Call UsefullButHidden;
    // etc
  end;

Unfortunately there is no way to access strict private/private fields by class helpers with Delphi 10.1 Berlin. RTTI is an option, but can be considered slow if performance is critical.

Here is a way to define the offset to a field at startup using class helpers and RTTI:

type 
  TBase = class(TObject)
  private  // Or strict private
    FMemberVar: integer;
  end;

type
  TBaseHelper = class helper for TBase
  private
    class var MemberVarOffset: Integer;
    function GetMemberVar: Integer;
    procedure SetMemberVar(value: Integer);
  public
    class constructor Create;  // Executed at program start
    property MemberVar : Integer read GetMemberVar write SetMemberVar;
  end;

class constructor TBaseHelper.Create;
var
  ctx: TRTTIContext;
begin
  MemberVarOffset := ctx.GetType(TBase).GetField('FMemberVar').Offset;
end;

function TBaseHelper.GetMemberVar: Integer;
begin
  Result := PInteger(Pointer(NativeInt(Self) + MemberVarOffset))^;
end;

procedure TBaseHelper.SetMemberVar(value: Integer);
begin
  PInteger(Pointer(NativeInt(Self) + MemberVarOffset))^ := value;
end;

This will have the benefit that the slow RTTI part is only executed once.


Note: Using RTTI for access of protected/private methods

The RTL/VCL/FMX have not declared visibility for access of protected/private methods with RTTI. It must be set with the local directive {$RTTI}.

Using RTTI for access of private/protected methods in other code requires for example setting :

{$RTTI EXPLICIT METHODS([vcPublic, vcProtected, vcPrivate])}