Adjustor thunks

Date:February 6, 2004 / year-entry #51
Orig Link:
Comments:    15
Summary: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 { public: // *** IUnknown...

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. Turn away from the darkside young skywalker. Walk into the light ;)

  2. geraldH 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. jeffdav says:

    Hm. I always wondered how that worked.

  4. Jim Causey says:

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

  5. rfredell says:

    Works fine in Mozilla 1.6 too.

  6. brion 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. Raymond Chen 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. brion 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. Raymond Chen says:

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

  10. 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. Fuggles 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. Raymond Chen says:

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

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

Comments are closed.

*DISCLAIMER: I DO NOT OWN THIS CONTENT. If you are the owner and would like it removed, please contact me. The content herein is an archived reproduction of entries from Raymond Chen's "Old New Thing" Blog (most recent link is here). It may have slight formatting modifications for consistency and to improve readability.

WHY DID I DUPLICATE THIS CONTENT HERE? Let me first say this site has never had anything to sell and has never shown ads of any kind. I have nothing monetarily to gain by duplicating content here. Because I had made my own local copy of this content throughout the years, for ease of using tools like grep, I decided to put it online after I discovered some of the original content previously and publicly available, had disappeared approximately early to mid 2019. At the same time, I present the content in an easily accessible theme-agnostic way.

The information provided by Raymond's blog is, for all practical purposes, more authoritative on Windows Development than Microsoft's own MSDN documentation and should be considered supplemental reading to that documentation. The wealth of missing details provided by this blog that Microsoft could not or did not document about Windows over the years is vital enough, many would agree an online "backup" of these details is a necessary endeavor. Specifics include:

<-- Back to Old New Thing Archive Index