delphi - una - ¿Cómo acceder a métodos privados sin ayudantes?
llamar variable de otra clase java (5)
Si desea una forma limpia que no afecte el rendimiento, aún puede acceder a campos privados desde un asistente de registro utilizando la instrucción with.
function TValueHelper.GetAsInteger: Integer;
begin
with Self do begin
Result := FData.FAsSLong;
end;
end;
Espero que mantengan este método abierto, porque tenemos código con demandas de alto rendimiento.
En Delphi 10 Seattle podría usar el siguiente código para evitar restricciones de visibilidad demasiado estrictas.
¿Cómo obtengo acceso a variables privadas?
type
TBase = class(TObject)
private
FMemberVar: integer;
end;
¿Y cómo obtengo acceso a métodos privados simples o virtuales?
type
TBase2 = class(TObject)
private
procedure UsefullButHidden;
procedure VirtualHidden; virtual;
procedure PreviouslyProtected; override;
end;
Anteriormente, usaría un ayudante de clase para abrir la clase base.
type
TBaseHelper = class helper for TBase
function GetMemberVar: integer;
En Delphi 10.1 Berlín, los ayudantes de clase ya no tienen acceso a miembros privados de la clase o registro de la asignatura.
¿Hay alguna forma alternativa de acceder a miembros privados?
Si se genera información RTTI extendida para los miembros privados de la clase: campos y / o métodos, puede usarla para obtener acceso a ellos.
Por supuesto, acceder a través de RTTI es mucho más lento que a través de los ayudantes de clase.
Métodos de acceso:
var
Base: TBase2;
Method: TRttiMethod;
Method := TRttiContext.Create.GetType(TBase2).GetMethod(''UsefullButHidden'');
Method.Invoke(Base, []);
Acceso a variables:
var
Base: TBase;
v: TValue;
v := TRttiContext.Create.GetType(TBase).GetField(''FMemberVar'').GetValue(Base);
La información RTTI predeterminada generada para las clases RTL / VCL / FMX está siguiendo
-
Campos:
private
,protected
,public
,published
-
Métodos:
public
,published
-
Propiedades
public
published
Desafortunadamente, eso significa que el acceso a métodos privados a través de RTTI para las bibliotecas principales de Delphi no está disponible. La respuesta de @LU RD cubre el pirateo que permite el acceso a métodos privados para clases sin RTTI extendido.
Suponiendo que RTTI extendido no está disponible, entonces, sin recurrir a lo que se consideraría piratería, no puede acceder a miembros privados desde el código en una unidad diferente. Por supuesto, si RTTI está disponible, se puede usar.
Tengo entendido que la capacidad de descifrar miembros privados usando ayudantes fue un accidente no intencional. La intención es que los miembros privados solo sean visibles desde el código en la misma unidad, y los miembros privados estrictos solo sean visibles desde el código en la misma clase. Este cambio corrige el accidente.
Sin la capacidad de que el compilador descifre la clase por usted, necesitaría recurrir a otras formas de hacerlo.
Por ejemplo, puede volver a declarar suficiente de la clase
TBase
para poder engañar al compilador para que le diga dónde vivía un miembro.
type
THackBase = class(TObject)
private
FMemberVar: integer;
end;
Ahora puedes escribir
var
obj: TBase;
....
MemberVar := THackBase(obj).FMemberVar;
Pero esto es terriblemente frágil y se romperá tan pronto como se cambie el diseño de
TBase
.
Eso funcionará para los miembros de datos, pero para los métodos no virtuales, probablemente necesitará usar técnicas de desmontaje en tiempo de ejecución para encontrar la ubicación del código. Para los miembros virtuales, esta técnica se puede utilizar para encontrar el desplazamiento de VMT.
Otras lecturas:
Todavía hay una manera de usar
class helpers
para acceder a
métodos privados
en Delphi 10.1 Berlín:
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;
Desafortunadamente, no hay forma de acceder a campos privados / privados estrictos por los ayudantes de clase con Delphi 10.1 Berlín. RTTI es una opción, pero puede considerarse lento si el rendimiento es crítico.
Aquí hay una manera de definir el desplazamiento de un campo en el inicio utilizando ayudantes de clase y 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;
Esto tendrá el beneficio de que la parte RTTI lenta solo se ejecuta una vez.
Nota: Uso de RTTI para acceder a métodos protegidos / privados
El RTL / VCL / FMX no ha declarado visibilidad para acceder a métodos protegidos / privados con RTTI. Debe establecerse con la directiva local {$RTTI} .
El uso de RTTI para acceder a métodos privados / protegidos en otro código requiere, por ejemplo, la configuración:
{$RTTI EXPLICIT METHODS([vcPublic, vcProtected, vcPrivate])}
Si no necesita compatibilidad con el compilador ARM , puede encontrar otra solución here .
Con el ensamblador en línea, puede acceder fácilmente a un campo o método privado.
Creo que la respuesta de David es mejor en la mayoría de los casos, pero si necesita una solución rápida para una gran clase, este método podría ser más útil.
Actualización (17 de junio): Acabo de darme cuenta, olvidé compartir su código de muestra para acceder a campos privados desde su here . lo siento.
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;
Aquí está su código de muestra para llamar al método privado .
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.