How to access private field from class helper in Delphi 10.1 Berlin?

I would like to use Gabriel Corneanu jpegex , a class helper for jpeg.TJPEGImage. Reading this and this I found out that outside of Delphi Seattle you cannot access private fields more than jpegex (FData in the example below). Invented with VMT, as David Heffernan suggested, is far superior to me. Is there an easier way to do this?

type // helper to access TJPEGData fields TJPEGDataHelper = class helper for TJPEGData function Data: TCustomMemoryStream; inline; procedure SetData(D: TCustomMemoryStream); procedure SetSize(W,H: integer); end; // TJPEGDataHelper function TJPEGDataHelper.Data: TCustomMemoryStream; begin Result := self.FData; end; 
+8
delphi class-helpers delphi-10.1-berlin
source share
3 answers

Caution! This is an unpleasant hack and may fail when the internal structure of the field of the hacked class changes.

 type TJPEGDataHack = class(TSharedImage) FData: TCustomMemoryStream; // must be at the same relative location as in TJPEGData! end; // TJPEGDataHelper function TJPEGDataHelper.Data: TCustomMemoryStream; begin Result := TJPEGDataHack(self).FData; end; 

This will only work if the parent class of the hack class matches the parent class of the source class. So, in this case, TJPEGData is inherited from TSharedImage, as well as a hack class. The positions should also match, so if there was a field in the list before FData, then the equivalent field should be in the "hack" class, even if it is not used.

A full description of how this works can be found here:

Hack # 5: access to private fields

+11
source share

Today I found a neat way around this error using the with statement.

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

In addition, Embarcadero did a good job of building walls to protect private parts, and probably why they called it 10.1 Berlin.

+11
source share

Using a combination of class helper and RTTI, you can use the same performance as previous versions of Delphi using class helpers.

The trick is to allow private field offset at startup using RTTI and save it inside the helper as the var class .

 type TBase = class(TObject) private // Or strict private FMemberVar: integer; end; type TBaseHelper = class helper for TBase // Can be declared in a different unit private class var MemberVarOffset: Integer; function GetMemberVar: Integer; procedure SetMemberVar(value: Integer); public class constructor Create; // Executed automatically 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; 

As you can see, this requires a bit of extra typing, but compared to the patch for the entire block, it is quite simple.

+4
source share

All Articles