29

This is a follow-up question to: How to hide a protected procedure of an object?
(I'm a bit fuzzy on the whole class helper concept)

Suppose I have an class like:

type 
TShy = class(TObject) 
strict private
  procedure TopSecret;
private
  procedure DirtyLaundry;  
protected 
  procedure ResistantToChange;
end; 

I know I can access the private method if I have the source code by adding a descendent class in the same unit.

I have 2 questions:
- How do I employ a class helper to access the strict private member?
- Can I use a class helper in a separate unit to access (strict) private members?

Community
  • 1
  • 1
Johan
  • 74,508
  • 24
  • 191
  • 319
  • See also this SO question: [access-a-strict-protected-property-of-a-delphi-class](http://stackoverflow.com/questions/8330003/access-a-strict-protected-property-of-a-delphi-class) – LU RD Feb 23 '12 at 16:46

2 Answers2

38

Up to, and including Delphi 10.0 Seattle, you can use a class helper to access strict protected and strict private members, like this:

unit Shy;

interface

type
  TShy = class(TObject)
  strict private
    procedure TopSecret;
  private
    procedure DirtyLaundry;
  protected
    procedure ResistantToChange;
  end;

unit NotShy;

interface

uses
  Shy;

type
  TNotShy = class helper for TShy
  public
    procedure LetMeIn;
  end;

implementation

procedure TNotShy.LetMeIn;
begin
  Self.TopSecret;
  Self.DirtyLaundry;
  Self.ResistantToChange;
end;

end.

uses
  ..., Shy, NotShy;

procedure TestShy;
var
  Shy: TShy;
begin
  Shy := TShy.Create;
  Shy.LetMeIn;
  Shy.Free;
end;

However, starting with Delphi 10.1 Berlin, this no longer works! Class helpers can no longer access strict protected, strict private or private members. This "feature" was actually a compiler bug that Embarcadero has now fixed in Berlin. You are out of luck.

Johan
  • 74,508
  • 24
  • 191
  • 319
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 2
    JFTR: In D2007, the `Self.DirtyLaundry;` and `Self.ResistantToChange;` lines don't compile while `Self.TopSecret;` **does**. I.e. you have access to `strict private` members but not to `private` or `protected` members. :-D – Uli Gerhardt Feb 23 '12 at 10:41
  • Thanks, just to be clear on this, in D2007 the helper will only let me access `strict private` members like this, in Delphi 2010 and up the helper will let me access all class members. Correct? – Johan Feb 23 '12 at 10:55
  • @Johan, I just copy&pasted Remy's code into a D2007 test project and had to comment out the `DirtyLaundry` and `ResistantToChange` lines. The `TopSecret` line compiled fine. – Uli Gerhardt Feb 23 '12 at 11:26
  • @Remy, I didn't want to imply otherwise. Some changes in this area seem to have happened, probably from D2007 to D2009. – Uli Gerhardt Feb 24 '12 at 07:52
  • 3
    This does not work anymore (for private members) in Delphi 10.1 Berlin as it has been classified as a bug in the compiler and was fixed. – Stefan Glienke Apr 19 '16 at 12:18
  • 4
    See [RSB-1146 Fix for "Class helpers allow cross-unit private violations" breaks existing code](https://quality.embarcadero.com/browse/RSB-1146) and Marco's comments on [RSB-1254 can't access to private fields in helpers](https://quality.embarcadero.com/browse/RSB-1254), such as "*[access to privates in helpers] was filed as a bug and rightfully so. R&D was not aware of the hack. We thought about leaving the bug open, but decided against. It is too much of a fundamental problem*". – Remy Lebeau Apr 19 '16 at 14:16
  • @Johan: both links work fine for me. Though now that Berlin is released, and all RSB tickets will soon be moved to RSP, the links may or may not break. I'll update them if they do. – Remy Lebeau Apr 19 '16 at 17:16
  • You say: `strict protected and strict private` what about `private`? That one is also verboten in Berlin right? – Johan Apr 19 '16 at 17:19
  • @Johan: This question talks about `strict` access, but yes, in general `private` data cannot be accessed outside of its declaring unit. The compiler now enforces this for class helpers. Embarcadero considered class helpers accessing private data to be a bug, and the bug has now been fixed and will not be reversed (despite people asking for it). – Remy Lebeau Apr 19 '16 at 17:29
  • 1
    And if you define private method in your class helper you also cannot access it in your own code. Nice... – Z.B. Apr 26 '16 at 07:55
  • This is total mistake to remove code worked through xe2, xe3, xe4, xe5, ex6, xe7, xe8, 10 - really nice move - but when they loos money because many people forgot to upgrade to newer version - then maybe they chenge decision. This is the same like car 200km/h but in new version it can only drive 30km/h - mayby someone buy it but my company not. If you need back this feature then vote on https://quality.embarcadero.com/browse/RSP-14711 – Livius May 05 '16 at 06:27
  • All indications are this "feature" (bug) is gone now and is never coming back. I depend on it too, and I'm left without a reasonable alternative. RTTI is my current best "unreasonable" alternative, followed by code hooking, and evil assembler hacks. Your link is dead now (closed as duplicate). Current discussion is at: https://quality.embarcadero.com/browse/RSP-14347 – Warren P May 05 '16 at 16:10
  • Another reason to move to Lazarus entirely and once for good! – tk_ May 27 '16 at 11:57
  • Access to strict protected members is still allowed. – LU RD Jun 11 '16 at 08:28
  • Current "open" proposition on QualityCentral is: https://quality.embarcadero.com/browse/RSP-15273 but still not fixed - you can vote for fix about access to private fields – Livius Mar 13 '17 at 13:21
8

Access to private and strict private members of a class with class helpers was removed in Delphi 10.1 Berlin. See Closing the Class Helpers Private Access Loophole.

But there is still an open loophole:

unit Shy;

interface

type
  TShy = class(TObject)
  strict private
    procedure TopSecret;
  private
    procedure DirtyLaundry;
  protected
    procedure ResistantToChange;
  end;

implementation

procedure TShy.DirtyLaundry;
begin
  WriteLn('DirtyLaundry');
end;

procedure TShy.ResistantToChange;
begin
  WriteLn('ResistantToChange');
end;

procedure TShy.TopSecret;
begin
  WriteLn('TopSecret');
end;

end.

Program TestClassHelpers;

{$APPTYPE CONSOLE}

Uses
  Shy;

type
  TNotShy = class helper for TShy
  public
    procedure LetMeIn;
  end;

procedure TNotShy.LetMeIn;
var
  P : procedure of object;
begin
  TMethod(P).Code := @TShy.TopSecret;
  TMethod(P).Data := Self;
  P; // Call TopSecret
  TMethod(P).Code := @TShy.DirtyLaundry;
  TMethod(P).Data := Self;
  P; // Call DirtyLaundry;
  Self.ResistantToChange;  // Protected access works without problems
end;

var
  myObj: TShy;
begin
  myObj := TShy.Create;
  try
    myObj.LetMeIn;
    ReadLn;
  finally
    myObj.Free;
  end;
end.
LU RD
  • 34,438
  • 5
  • 88
  • 296
  • 1
    You just made my day. That's brilliant. – Johan Jun 11 '16 at 08:46
  • 3
    I'm afraid this doesn't replace the old "feature" completely, because it only gives you access to methods, not fields. Before Delphi 10.1 Berlin we could also access private fields of that class. – Alexandre M Jun 12 '16 at 00:54
  • 1
    For a way to access private fields, see [How to access private methods without helpers?](http://stackoverflow.com/a/37761852/576719). By using a combination of class helpers and RTTI, it is possible to retain the performance. – LU RD Jun 16 '16 at 05:47