Как получить доступ к частным методам без помощников?
в Delphi 10 Seattle я мог бы использовать следующий код для работы с чрезмерно строгими ограничениями видимости.
как получить доступ к приватным переменным?
type
TBase = class(TObject)
private
FMemberVar: integer;
end;
и как получить доступ к простым или виртуальным частным методам?
type
TBase2 = class(TObject)
private
procedure UsefullButHidden;
procedure VirtualHidden; virtual;
procedure PreviouslyProtected; override;
end;
ранее я бы использовал помощник класса для взлома базового класса.
type
TBaseHelper = class helper for TBase
function GetMemberVar: integer;
в Delphi 10.1 Berlin помощники классов больше не имеют доступа к частным членам предметный класс или запись.
есть ли альтернативный способ доступа к частным членам?
5 ответов
если есть расширенная информация RTTI, генерируемая для частных членов класса - полей и/или методов, которые вы можете использовать для получения доступа к ним.
конечно, доступ через RTTI намного медленнее, чем через помощников класса.
доступа к методам:
var
Base: TBase2;
Method: TRttiMethod;
Method := TRttiContext.Create.GetType(TBase2).GetMethod('UsefullButHidden');
Method.Invoke(Base, []);
доступ к переменным:
var
Base: TBase;
v: TValue;
v := TRttiContext.Create.GetType(TBase).GetField('FMemberVar').GetValue(Base);
информация RTTI по умолчанию генерируется для классов RTL / VCL/FMX после
- поля -
private
,protected
,public
,published
- методы
public
,published
- "свойства" -
public
,published
к сожалению, это означает, что доступ к частным методам через RTTI для основных библиотек Delphi недоступен. ответ@LU RD охватывает взлом, который позволяет Частный доступ к методу для классов без расширенного RTTI.
есть еще способ использовать class helpers
для доступа частные методы в 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;
к сожалению, нет способа получить доступ строгие частные / частные поля помощниками класса с Delphi 10.1 Berlin. RTTI является вариантом, но может считаться медленным, если производительность критична.
вот способ определить смещение в поле при запуске с помощью помощников классов и 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;
это имейте преимущество, что медленная часть RTTI выполняется только один раз.
Примечание: использование RTTI для доступа защищенных / частных методов
RTL/VCL/FMX не объявили видимость для доступа защищенных / частных методов с RTTI. Он должен быть установлен с локальной директивой {$RTTI}.
используя RTTI для доступа частных / защищенных методов в другом коде требуется, например, установка:
{$RTTI EXPLICIT METHODS([vcPublic, vcProtected, vcPrivate])}
Если вы хотите чистый способ, который не влияет на производительность, вы все еще можете получить доступ к частным полям из помощника записи с помощью оператора with.
function TValueHelper.GetAsInteger: Integer;
begin
with Self do begin
Result := FData.FAsSLong;
end;
end;
Я надеюсь, что они сохранят этот метод открытым, потому что у нас есть код с высокими требованиями к производительности.
предполагая, что расширенный RTTI недоступен, то, не прибегая к тому, что будет считаться взломом, вы не можете получить доступ к частным членам из кода в другом блоке. Конечно, если RTTI доступен, его можно использовать.
насколько я понимаю, возможность взлома частных членов с помощью помощников была непреднамеренной случайностью. Цель состоит в том, чтобы частные члены были видны только из кода в том же блоке, а строгие частные члены были видны только из кода в тот же класс. Это изменение исправляет аварию.
без возможности компилятора взломать класс для вас, вам нужно будет прибегнуть к другим способам сделать это. Например, вы можете повторно объявить достаточно TBase
class, чтобы иметь возможность обмануть компилятор, чтобы сказать вам, где жил член.
type
THackBase = class(TObject)
private
FMemberVar: integer;
end;
теперь вы можете написать
var
obj: TBase;
....
MemberVar := THackBase(obj).FMemberVar;
но это ужасно хрупко и сломается, как только макет TBase
is измененный.
это будет работать для членов данных, но для не виртуальных методов вам, вероятно, придется использовать методы разборки среды выполнения, чтобы найти местоположение кода. Для виртуальных членов этот метод можно использовать для поиска смещения VMT.
Читать далее:
Если вам не нужна поддержка компилятора ARM, вы можете найти другое решение здесь.
С встроенным asembler, вы можете получить доступ к частному полю или методу, легко.
Я думаю Дэвид лучше, в большинстве случаев, но если вам нужен быстрый решение на огромный класса, этот метод может быть более полезным.
Обновление(17 Июня): Я только что заметил, я забыл поделиться своим пример кода для доступ к закрытым полям из его в должности. извиняюсь.
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;
вот его пример кода для вызов частного метода.
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.