How do I get the number of entries (virtual methods) in the VMT?

691 Views Asked by At

At positive offsets the VMT stores pointers to all user defined virtual methods.
I need to write some code to hook the VMT. The way I do this is to get a pointer to a virtual method in an ancestor class.
Let's say: TCustomForm.ShowModal. I then look up the offset in the VMT of TCustomForm. With this offset in hand I go to TMyForm and alter its VMT to point to the function I need.

I would like to generalize the approach and in order to do so I would like to know the total number of entries the VMT holds so I don't search past the end.

How do I obtain the size of the (user definable part of) the VMT?

2

There are 2 best solutions below

0
On

Digging through the RTL source I think this is the way to get the count:

function GetVMTCount(AClass: TClass): integer;
var
  p: pointer;
  VirtualMethodCount: word;
begin
  p := PPointer(PByte(AClass) + vmtMethodTable)^;
  VirtualMethodCount:= PWord(p)^;
  //Size of the VMT in bytes
  Result:= VirtualMethodCount * SizeOf(Pointer) - vmtSelfPtr;
  //Number of entries in the VMT
  Result:= Result div SizeOf(Pointer);
end;

Feel free to correct me if needed.

0
On

A way to do this without much actual knowledge of the VMT structure, and hence less prone to breaking when the VMT structure changes again, is using the Rtti for this. TRttiInstanceType knows the VmtSize of the associated class.

So using VmtSize and a VMT entry being a Pointer

function GetVirtualMethodCount(AClass: TClass): Integer;
var
  AContext: TRttiContext;
  AType: TRttiType;
begin
  AType := AContext.GetType(AClass);
  Result := (AType as TRttiInstanceType).VmtSize div SizeOf(Pointer);
end;

This will however include all entries inherited from the base class(es) too. Including the ones from TObject at negative offsets. But it is possible to subtract all entries from a given base class, e.g. TObject. Here is an approach with a variable base class provided:

function GetVirtualMethodCountMinusBase(AClass: TClass; ABaseClass: TClass): Integer;
var
  AContext: TRttiContext;
  AType, ABaseType: TRttiType;
begin
  AType := AContext.GetType(AClass);
  ABaseType := AContext.GetType(ABaseClass);
  Result := ((AType as TRttiInstanceType).VmtSize - (ABaseType as TRttiInstanceType).VmtSize) div SizeOf(Pointer);
end;

And: When using Jedi there is a function in JclSysUtils called GetVirtualMethodCount. Although I'm not sure if this is up-to-date and correct.