33 votes

Comment accéder aux méthodes privées sans helpers?

Dans Delphi 10 Seattle je pouvais utiliser le code suivant pour contourner trop strictes restrictions de visibilité.

Comment puis-je accéder aux variables privées?

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

Et comment puis-je obtenir l'accès à la plaine ou privé virtuel méthodes?

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

Auparavant, je voudrais utiliser une classe helper de briser la classe de base.

type
  TBaseHelper = class helper for TBase
    function GetMemberVar: integer;

Dans Delphi 10.1 Berlin, classe les aides n'ont plus accès aux membres privés de la classe du sujet ou de l'enregistrement.

Est-il un autre moyen pour accéder aux membres privés?

24voto

Dalija Prasnikar Points 17396

Si il est étendu RTTI info généré pour la classe de membres privés - champs et/ou de méthodes que vous pouvez utiliser pour avoir accès à eux.

Bien sûr, l'accès par le biais de RTTI est beaucoup plus lent que c'était grâce à la classe des aides.

Accéder méthodes:

var
  Base: TBase2;
  Method: TRttiMethod;

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

L'accès aux variables:

var
  Base: TBase;
  v: TValue;

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

Par défaut RTTI de l'information générée pour RTL/VCL/FMX classes suivantes

  • Champs - private, protected, public, published
  • Méthodes - public, published
  • Propriétés - public, published

Malheureusement, cela signifie que l'accès privé méthodes via RTTI pour le noyau Delphi bibliothèques n'est pas disponible. @LU RD réponse couvre hack qui permet de méthode privée d'accès pour les classes sans étendue RTTI.

Travailler avec RTTI

22voto

LU RD Points 13180

Il y a toujours un moyen d'utiliser class helpers pour l'accès de méthodes privées en 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;

Malheureusement, il n'existe aucun moyen d'accéder à de strictes privé/champs privés de la classe helpers avec Delphi 10.1 Berlin. RTTI est une option, mais peut être considérée comme lente si la performance est critique.

Ici est une façon de définir le décalage d'un champ au démarrage à l'aide de la classe d'aides et de 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;

Cela aura l'avantage de la lenteur de la RTTI partie n'est exécuté qu'une fois.


Remarque: l'Utilisation de RTTI pour l'accès de l'protected/private méthodes

Le RTL/VCL/FMX n'ont pas déclaré de la visibilité pour l'accès de l'protected/private méthodes avec RTTI. Il doit être défini avec les acteurs locaux de la directive {$RTTI}.

À l'aide de RTTI pour l'accès privé/protégé méthodes dans d'autres code exige par exemple de réglage :

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

16voto

Toon Krijthe Points 36327

Si vous voulez une méthode propre qui n'affecte pas les performances, vous pouvez toujours accéder aux champs privés à partir d'un assistant d'enregistrement en utilisant l'instruction with.

 function TValueHelper.GetAsInteger: Integer;
begin
  with Self do begin
    Result := FData.FAsSLong;
  end;
end;
 

J'espère qu'ils garderont cette méthode ouverte, car nous avons du code avec des exigences de performances élevées.

11voto

David Heffernan Points 292687

En supposant que l'étendue de RTTI n'est pas disponible, sans avoir à recourir à ce qui serait considéré comme du piratage, de vous ne peut pas accéder aux membres privés de code dans une unité différente. Bien sûr, si RTTI est disponible, il peut être utilisé.

C'est ma compréhension que la capacité à se fissurer privé des membres à l'aide les aides a été un accident involontaire. L'intention est que les membres privés ne seront visibles à partir du code dans la même unité, et des membres privés ne seront visibles à partir du code dans la même classe. Cette modification corrige l'accident.

Sans la possibilité d'avoir le compilateur fissure de la classe pour vous, vous auriez besoin de recourir à d'autres moyens de le faire. Par exemple, vous pourriez re-déclarer assez de l' TBase classe pour être en mesure de tromper le compilateur en vous indiquant où un membre vécu.

type
  THackBase = class(TObject)
  private
    FMemberVar: integer;
  end;

Maintenant, vous pouvez écrire

var
  obj: TBase;
....
MemberVar := THackBase(obj).FMemberVar;  

Mais c'est très fragile et se brisera dès que la mise en page de l' TBase est changé.

Que de travail pour les membres de données, mais pour les non-méthodes virtuelles, vous auriez probablement besoin d'utiliser d'exécution démontage techniques pour trouver l'emplacement du code. Pour les membres virtuels cette technique peut être utilisée pour trouver le VMT décalage.

Pour en savoir plus:

5voto

benok Points 169

Si vous n'avez pas besoin de BRAS de prise en charge du compilateur, vous pouvez trouver une autre solution ici.

Avec inline asembler, vous pouvez accéder à la zone privée ou de la méthode, facilement.

Je pense que David réponse est meilleure dans la plupart des cas, mais si vous avez besoin d'une solution rapide pour une vaste classe, cette méthode pourrait être plus utile.

Mise à jour(17 juin): je viens de remarqué, j'ai oublié de partager son exemple de code pour accéder aux champs privés de son poste. désolé.

unit UnitA;

type
  THoge = class
  private
    FPrivateValue: Integer;
    procedure PrivateMethod;
  end;
end.

unit UnitB;

type
  THogeHelper = class helper for THoge
  public
    function GetValue: Integer;
    procedure CallMethod;
  end;

function THogeHelper.GetValue: Integer;
asm
  MOV EAX,Self.FPrivateValue
end;

procedure THogeHelper.CallMethod;
asm
  CALL THoge.PrivateMethod
end;

En voici un exemple de code pour l'appel de méthode privée.

type
  THoge = class
  private
    procedure PrivateMethod (Arg1, Arg2, Arg3 : Integer);
  end;

// Method 1
// Get only method pointer (if such there is a need to assign a method pointer to somewhere)
type
  THogePrivateProc = procedure (Self: THoge; Arg1, Arg2, Arg3: Integer);
  THogePrivateMethod = procedure (Arg1, Arg2, Arg3: Integer) of object;

function THogeHelper.GetMethodAddr: Pointer;
asm
  {$ifdef CPUX86}
  LEA EAX, THoge.PrivateMethod
  {$else}
  LEA RAX, THoge.PrivateMethod
  {$endif}
end;

var
  hoge: THoge;
  proc: THogePrivateProc;
  method: THogePrivateMethod;
begin
  // You can either in here of the way,
  proc := hoge.GetMethodAddr;
  proc (hoge, 1, 2, 3);
  // Even here of how good
  TMethod (method) .Code := hoge.GetMethodAddr;
  TMethod (method) .Data := hoge;
  method (1, 2, 3) ;
end;

// Method 2
// To jump (here is simple if you just simply call)
procedure THogeHelper.CallMethod (Arg1, Arg2, Arg3 : Integer);
asm
  JMP THoge.PrivateMethod
end;

unit UnitA;

type
  THoge = class
  private
    FPrivateValue: Integer;
    procedure PrivateMethod;
  end;
end.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X