12

The System.pas file contains a fair amount of information on hard-coded VMT offsets, but it doesn't seem to actually say much about the structure of the VMT itself. What I'd really like to know is, is there any way to find out the size of a VMT at runtime, or in other words, how many virtual methods exist for a given class?

Mason Wheeler
  • 82,511
  • 50
  • 270
  • 477

5 Answers5

14

What about the VMT structure are you wanting to know? You also do know that it is an internal implementation detail that is subject to change (and has changed over time).

To answer your specific question, here is a simple way to find the number of virtual methods for a given class:

function GetVirtualMethodCount(AClass: TClass): Integer;
begin
  Result := (PInteger(Integer(AClass) + vmtClassName)^ - 
    (Integer(AClass) + vmtParent) - SizeOf(Pointer)) div SizeOf(Pointer);
end;

This works because I happen to know that the string representing the class name is placed immediately following all the virtual method vectors in the VMT.

I also know that there are 11 virtual methods (for D2009, 9 for D2007 and prior) on all TObjects that are negatively offset from the VMT pointer itself.

That is the reason for the vmtParent reference.

Finally, by using a TClass class reference, you can pass any TObject derived class into this function and get the number of virtual methods.

menjaraz
  • 7,551
  • 4
  • 41
  • 81
Allen Bauer
  • 16,657
  • 2
  • 56
  • 74
  • VmtEquals? I've never heard of that before. Is Equals one of the two new virtual methods in TObject for Delphi 2009? – Rob Kennedy Apr 17 '09 at 16:27
  • Yep. It's necessary for getting certain Generics-related things to work, I believe. – Mason Wheeler Apr 17 '09 at 16:36
  • Allen: That's very nice! Just what I was looking for. You say that the structure can and does change. So what version(s) of Delphi does that trick work for? – Mason Wheeler Apr 17 '09 at 16:40
  • I edited it to make it work for all versions at least back to D5. – Allen Bauer Apr 17 '09 at 16:58
  • Thanks! Sounds like it's been pretty stable for quite a while. How likely is this to change in the future? – Mason Wheeler Apr 17 '09 at 17:13
  • 2
    Shouldn't Integer(AClass) casts be Cardinal(AClass) casts to make the app compatible with the 3GB mode? Hmmm, do Delphi apps even work in 3GB mode_ – gabr Apr 18 '09 at 06:31
  • I finally got around to testing this, and there's a problem. TVirtualObject = class(TObject) public Name: string; procedure virt(sender: TObject); virtual; end; I run your algorithm on it, in D2009, and it gives a result of 16. Sure looks to me like the correct answer is 12. Any idea what's going on? – Mason Wheeler May 07 '09 at 13:45
  • I think this was caused by the addition of some extra RTTI data pointers after the VMT entries but before the classname entry. So it looks like my little trick won't work for all versions of Delphi. Will need to look at it some more. – Allen Bauer Mar 29 '11 at 04:18
  • In addition to gabr, I would like to mention x64 compatibility too, and suggest a cast to NativeInt instead! – PatrickvL Apr 16 '12 at 12:57
  • 1
    @AllenBauer I think your answer is wrong for XE and higher (did not check any version below). The string representing the class name is not the first thing placed after the virtual method pointers. There is also the vmtInitTable, vmtFieldTable and vmtMethodTable that have a lower address than the vmtClassName but their address is still is not directly after the virtual method pointers according to a quick test I did. – Stefan Glienke Jun 25 '14 at 06:52
  • All of these `Integer()` type-casts will not work in 64-bit builds, they need to use `Native(U)Int` instead. – Remy Lebeau May 06 '23 at 00:08
10

I was pretty sure Hallvard had something on the VMT. Sure enough, he has Hack #8: Explicit VMT calls which references Ray Lischner Secrets of Delphi 2 and Delphi in a Nutshell.

Here is his hacked up version of the 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;

You will need to read his article for more on the hack though.

Jim McKeeth
  • 38,225
  • 23
  • 120
  • 194
3

I'll plug my own site for this one:

What is the virtual-method table?

It's accurate as of Delphi 2005, I think.

The VMT does not have any value giving the number of virtual-method pointers it holds. Nothing but the compiler needs to know that information, so there's no reason to record it for use at run time.

Rob Kennedy
  • 161,384
  • 21
  • 275
  • 467
  • Migrated server with new settings, plus lost access to my account, @Server. Gonna get it fixed, but when I have time to work on it often corresponds with when the student support staff are on break. Thanks for reminding me, though. – Rob Kennedy Dec 13 '22 at 22:20
2

Googling :-P for "delphi vmt" yields this. Maybe this gives you a start.

Uli Gerhardt
  • 13,748
  • 1
  • 45
  • 83
  • 2
    *grin* I love Hallvard! He's good at tricks like this. Unfortunately, he defines the user-defined virtuals as an "array[0..999]", or in other words "bigger than it would ever need to be." I'm looking for a way to find the actual size. – Mason Wheeler Apr 17 '09 at 16:29
0

i remember there was some information about delphi vmt in "delphi in a nutshell" book. u can start from delphi in a nutshell chapter 2 or this

avar
  • 1,180
  • 2
  • 13
  • 28