Jump to content
Eric Grange

Gaining access to private class vars of an implementation class

Recommended Posts

Hi, this is definitely on the hacky side.

 

Is there a way to gain access to private class var of a class defined in an implementation section ?

More precisely, I'm trying to gain access to TDX11Context private class vars, which is defined in the implementation section of FMX.Context.DX11, and the vertex & pixel shaders more specifically.

 

My best attempt so far is to obtain the address of TDX11Context.DoSetShaderVariable (easy, it's virtual and just protected), and there the first line is just

 

    if (CurrentVertexShader <> nil) and (Length(FVSBuf) > 0) then begin

 

which is simple enough to "disassemble", get the FVSBuf address, from which the other vars can be inferred.

It's all quite fragile though 🙂
 

  • Like 1

Share this post


Link to post

I guess you already know the answer. Enter a ticket on the Quality Portal to request access to the fields you want. Give a good explanation why whould you want those fields to be public or protected in order to reach MFGA (Make Firemonkey Great Again). 

  • Haha 1

Share this post


Link to post
program Project1104;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  System.Classes,
  System.Types,
  Winapi.D3D11,
  FMX.Types3D,
  FMX.Context.DX11;

type
  TDX11Context = class(TCustomDX11Context)
  private class var
    FResources: IInterfaceList;
    FVSSlot: ID3D11Buffer;
    FPSSlot: ID3D11Buffer;
    FVSSlotModified, FPSSlotModified: Boolean;
    FVSBuf, FPSBuf: array of Byte;
    FInputLayout: ID3D11InputLayout;
    FResourceViews: array [0..16] of ID3D11ShaderResourceView;
    FSampleStates: array [0..16] of ID3D11SamplerState;
    FBlendDesc: TD3D11_BLEND_DESC;
    FBlendState: ID3D11BlendState;
    FBlendStateModified: Boolean;
    FRasterizerDesc: TD3D11_RASTERIZER_DESC;
    FRasterizerState: ID3D11RasterizerState;
    FRasterizerStateModified: Boolean;
    FDepthStencilDesc: TD3D11_DEPTH_STENCIL_DESC;
    FDepthStencilState: ID3D11DepthStencilState;
    FDepthStencilModified: Boolean;
    FStencilRef: Integer;
    FBufferSize: TSize;
  end;
  TDX11ContextClass = class of TDX11Context;

begin
  try
    RegisterContextClasses;
    if TContextManager.DefaultContextClass.ClassNameIs('TDX11Context') then
      Writeln(Length(TDX11ContextClass(TContextManager.DefaultContextClass).FVSBuf))
    else
      Writeln('Oops!');
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  ReadLn;
end.

 

  • Like 1

Share this post


Link to post

@Uwe Raabe this doesn't seem to work, the compiler is creating different variables, class private vars don't appear to be relative to the TClass ?

@Lajos Juhász this would be a long term ticket, and given then number of private stuff one needs to crack (not just in TDX11Context, but also in TContext3D), a major undertaking for EMBT to refactor everything

 

Share this post


Link to post
1 minute ago, Eric Grange said:

@Lajos Juhász this would be a long term ticket, and given then number of private stuff one needs to crack (not just in TDX11Context, but also in TContext3D), a major undertaking for EMBT to refactor everything

 

I am aware. Unfortunately that is thr only right way to do it. You can try to hack the system. Unfortunately that depends heavily on the memory layout and the classes current definitions as a result most probably will break with every version of the Delphi.

Share this post


Link to post

@Eric Grange How about hooking TDX11Context.DoSetShaders ?

 

Wait a second !, TDX11Context is private and non accessible, at least on my XE8, that is bad design to hide a critical and OS dependent class, was the designer so confident it is bug free and it does include everything could be needed from DirectX11 even in the future.

From the name DX11, there will be DX12 and even DX13, and they will break compatibility in huge way, so a private one is like, NO update your IDE framework and wait for our updated design...

Edited by Kas Ob.
  • Like 1

Share this post


Link to post

@Kas Ob. yes, but the code has not changed in a long while AFAICT.

On practical approach might be for EMBT to "officially" allow FMX.Context.XXX open-source forks.

This would allow reimplementation projects to be kickstarted, and after a few iterations, there would probably be little left from the original EMBT source code anyway (at least from what I can see in the TDX11Context).
The OpenGL context seems less "private", but it still has a lot of private vars in key areas.

One of the first things forks would do would probably be to turn those private vars into fields, and support multiple contexts (and eventually multiple threads)

  • Like 2

Share this post


Link to post
4 hours ago, Lajos Juhász said:

to reach MFGA (Make Firemonkey Great Again). 

I ignored that organization exists ! Where I sign up ? 😉

 

Share this post


Link to post

The moral here is that private variables are almost as evil as threads. No one knows, forever and for all time, what anyone else might need to access in the future.

  • Like 1

Share this post


Link to post
On 9/8/2024 at 10:01 AM, Joseph MItzen said:

The moral here is that private variables are almost as evil as threads.

That's a strange statement. Why are threads evil?

Eric is trying to do something with a class that just wasn't designed for what he needs. That in itself doesn't make the design wrong or bad.

 

 

On 9/8/2024 at 10:01 AM, Joseph MItzen said:

No one knows, forever and for all time, what anyone else might need to access in the future.

The counter-argument is that no one knows how the functionality of a class' public API will be implemented in future. Class members are private in order to not lock a public API to a specific implementation bound to those private members; They shield the API from implementation details. You are basically arguing against the use of encapsulation.

Share this post


Link to post

Encapsulation can be frustrating, but exposing everything means you're making promises. People's code will gain dependencies to everything that can be accessed or overridden.
Which means you won't be able to change much (or fix) without breaking user code. Opening too much means code will sediment and become untouchable...

In the case of the DX11 driver, it's obvious it was locked in the implementation section because whoever was working on it wasn't satisfied with it. Likely because he/she did not have time to tidy it up. It's essentially DX9 code with a light rewrite to DX11.

I was able to hack it, but it was brittle. I'm now starting down the path of reimplementing it, which in the long term will open more possibilities (and hoping Delphi 12.2 doesn't wreak havoc on TContext3D, haha)

  • Like 3

Share this post


Link to post
1 hour ago, Anders Melander said:

The counter-argument is that no one knows how the functionality of a class' public API will be implemented in future. Class members are private in order to not lock a public API to a specific implementation bound to those private members; They shield the API from implementation details. You are basically arguing against the use of encapsulation.

The problem is that Embarcadero provides base framework classes that satisfy very narrow usage and are not properly open for extension. Sometimes you need to change literally one line, to get the needed behavior, but there is no way to do that properly. So you need to resort to hacking into those classes.

 

Protected opens up the class for needed extensions and still protects casual users from using implementation details and does not break encapsulation. Yes, if the implementation changes, you may need to update your code to match the changes, but you would need to do that regardless. Private is major PITA.

  • Like 3

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×