System.pas ファイルには、ハードコードされた VMT オフセットに関するかなりの量の情報が含まれていますが、実際には VMT 自体の構造についてはあまり語っていないようです。私が本当に知りたいのは、実行時の VMT のサイズ、つまり、特定のクラスに存在する仮想メソッドの数を調べる方法はありますか?
5 に答える
知りたいVMTの構造はどうですか?また、変更される可能性がある(そして時間の経過とともに変更された)内部実装の詳細であることもご存知でしょう。
特定の質問に答えるために、特定のクラスの仮想メソッドの数を見つける簡単な方法を次に示します。
function GetVirtualMethodCount(AClass: TClass): Integer;
begin
Result := (PInteger(Integer(AClass) + vmtClassName)^ -
(Integer(AClass) + vmtParent) - SizeOf(Pointer)) div SizeOf(Pointer);
end;
これは、クラス名を表す文字列がVMT内のすべての仮想メソッドベクトルの直後に配置されていることを知っているために機能します。
また、VMTポインター自体から負にオフセットされているすべてのTObjectに11個の仮想メソッド(D2009の場合は9個、D2007以前の場合は9個)があることも知っています。
これがvmtParent参照の理由です。
最後に、TClassクラス参照を使用することにより、任意のTObject派生クラスをこの関数に渡して、仮想メソッドの数を取得できます。
Hallvard が VMT に何かを持っていると確信していました。案の定、彼はHack #8: Explicit VMT callsを持っています。これは Ray Lischner Secrets of Delphi 2とDelphi in a Nutshellを参照しています。
これが彼のハッキングされた VMT のバージョンです
type
PClass = ^TClass;
PSafeCallException = function (Self: TObject; ExceptObject:
TObject; ExceptAddr: Pointer): HResult;
PAfterConstruction = procedure (Self: TObject);
PBeforeDestruction = procedure (Self: TObject);
PDispatch = procedure (Self: TObject; var Message);
PDefaultHandler = procedure (Self: TObject; var Message);
PNewInstance = function (Self: TClass) : TObject;
PFreeInstance = procedure (Self: TObject);
PDestroy = procedure (Self: TObject; OuterMost: ShortInt);
PVmt = ^TVmt;
TVmt = packed record
SelfPtr : TClass;
IntfTable : Pointer;
AutoTable : Pointer;
InitTable : Pointer;
TypeInfo : Pointer;
FieldTable : Pointer;
MethodTable : Pointer;
DynamicTable : Pointer;
ClassName : PShortString;
InstanceSize : PLongint;
Parent : PClass;
SafeCallException : PSafeCallException;
AfterConstruction : PAfterConstruction;
BeforeDestruction : PBeforeDestruction;
Dispatch : PDispatch;
DefaultHandler : PDefaultHandler;
NewInstance : PNewInstance;
FreeInstance : PFreeInstance;
Destroy : PDestroy;
{UserDefinedVirtuals: array[0..999] of procedure;}
end;
ただし、ハックの詳細については、彼の記事を読む必要があります。
私はこれのために自分のサイトを接続します:
Delphi2005の時点では正確だと思います。
VMTには、保持する仮想メソッドポインタの数を示す値はありません。コンパイラだけがその情報を知る必要があるので、実行時に使用するためにそれを記録する理由はありません。