Adjustor thunks

Yesterday we learned about the layout of COM objects and I hinted at "adjustor thunks".

If you find yourself debugging in disassembly, you'll sometimes find strange little functions called "adjustor thunks". Let's take another look at the object we laid out last time:

class CSample : public IPersist, public IServiceProvider
  // *** IUnknown ***
  STDMETHODIMP QueryInterface(REFIID riid, void** ppv);

  // *** IPersist ***

  // *** IQueryService ***
  STDMETHODIMP QueryService(REFGUID guidService,
                  REFIID riid, void** ppv);
  LONG m_cRef;
p    lpVtbl    QueryInterface (1)
q    lpVtbl    QueryInterface (2) AddRef (1)
m_cRef AddRef (2) Release (1)
... Release (2) GetClassID (1)
QueryService (2)

In the diagram, p is the pointer returned when the IPersist interface is needed, and q is the pointer for the IQueryService interface.

Now, there is only one QueryInterface method, but there are two entries, one for each vtable. Remember that each function in a vtable receives the corresponding interface pointer as its "this" parameter. That's just fine for QueryInterface (1); its interface pointer is the same as the object's interface pointer. But that's bad news for QueryInterface (2), since its interface pointer is q, not p.

This is where the adjustor thunks come in.

The entry for QueryInterface (2) is a stub function that changes q to p, and then lets QueryInterface (1) do the rest of the work. This stub function is the adjustor thunk.

  sub     DWORD PTR [esp+4], 4 ; this -= sizeof(lpVtbl)
  jmp     CSample::QueryInterface

The adjustor thunk takes the "this" pointer and subtracts 4, converting q into p, then it jumps to the QueryInterface (1) function to do the real work.

Whenever you have multiple inheritance and a virtual function is implemented on multiple base classes, you will get an adjustor thunk for the second and subsequent base class methods in order to convert the "this" pointer into a common format.

Comments (15)
  1. Anonymous says:

    Turn away from the darkside young skywalker. Walk into the light ;)

  2. Anonymous says:

    Note: Diagram only visible in Internet Explorer. Mozilla and Opera won’t display any arrows nor the right structure.

    Please, Raymond… next time, use a format everybody can see?

  3. Anonymous says:

    Hm. I always wondered how that worked.

  4. Anonymous says:

    Raymond’s diagrams work just fine in Mozilla Firebird; maybe you need to upgrade your browser?

  5. Anonymous says:

    Works fine in Mozilla 1.6 too.

  6. Anonymous says:

    No arrows in Mozilla 1.6 here (tested Win98 and Mac OS X). Positioning of the cells looks okay, but it’s kind of unclear without the arrows.

    For one thing I suspect that Mozilla doesn’t believe it’s kosher to slip XML into a document which isn’t parsable as well-formed XML and is labeled clearly as HTML 4.0 Transitional to boot. ;)

  7. Anonymous says:

    Sorry, I thought it was well-formed XML. Aside from a missing </TD> in the first table, everything seems to parse okay. What did I mess up?

    <v:shapetype id="arrow" coordsize="1,1"

    strokecolor="black" strokeweight="1pt">

    <v:stroke endarrow="classic" />

    <v:path v="m0,0 l 100,0 e" />


  8. Anonymous says:

    The page as a whole declares itself to be HTML 4.0 Transitional, but doesn’t validate as either HTML 4.0 or XML. (Try… it’s very nitpicky!)

    However, as far as I can tell nothing supports VML except IE/Win anyway.

  9. Anonymous says:

    Alas, the claim to be HTML 4.0 Transitional is coming from the blog software, not from me.

  10. Anonymous says:

    No arrows here in Internet Explorer,

    Version: 6.0.2800.1106.xpsp2.030422-1633


    ???????:; SP1; 3823; Q330994; Q824145; Q832894;

    (Also I really love how Internet Explorer lets me read that information and type it back in myself at the keyboard. If I had to use the mouse to copy and paste, it would be too easy. Internet Explorer is as helpful as Visual Studio .NET 2003 is in this regard.)

  11. Anonymous says:

    If one is interested in the underlying COM mechanisms it is useful to read the ‘COM Programmer’s Cookbook’ as samples are largely provided in C. This removes the fog of the underlying compiler tricks.

    The layout of CSample as a C struct would be:

    typedef struct {

    IPersist IPersistIFace;

    IQueryService IQueryServiceIFace

    LONG m_cRef;

    } CSample;

    The author provides an ‘adjustor thunk macro’. So to implement the thunk function for IQueryService::QueryInterface:

    STDMETHODIMP IQS_QueryInterface(IQueryService * This, REFIID riid, void ** ppv)


    CSample * ThisObj = IMPL (CSample, IQueryServiceIFace, This);

    return CSBase_QueryInterface(ThisObj, riid, ppv);


    The definition for IMPL is essentially:

    #define IMPL(class, member, pointer)

    ((class *) (((long) pointer) – offsetof (class, member)))


    CSample * ThisObj = This – 4;

    The author also provides a FindImpl function which relieves us from having to include these thunk functions by ‘searching’ for the base class.

  12. Anonymous says:

    Aw phooey. I edited this entry to add the link, and in the process I destroyed the arrows. Let me fix them.

  13. Anonymous says:

    Use the vtable.

  14. Anonymous says:

    A co-worker came by to ask what he thought was a coding "style" question that turned into a correctness

Comments are closed.