Welcome to MSDN Blogs Sign in | Join | Help

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
{
public:
  // *** IUnknown ***
  STDMETHODIMP QueryInterface(REFIID riid, void** ppv);
  STDMETHODIMP_(ULONG) AddRef();
  STDMETHODIMP_(ULONG) Release();
  // *** IPersist ***
  STDMETHODIMP GetClassID(CLSID* pClassID);
  // *** IQueryService ***
  STDMETHODIMP QueryService(REFGUID guidService,
                  REFIID riid, void** ppv);
private:
  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.

[thunk]:CSample::QueryInterface`adjustor{4}':
  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.
Published Friday, February 06, 2004 7:00 AM by oldnewthing
Filed under:

Comments

# re: Adjustor thunks

Friday, February 06, 2004 7:33 AM by Mathew Nolton
Turn away from the darkside young skywalker. Walk into the light ;)

# re: Adjustor thunks

Friday, February 06, 2004 9:14 AM by geraldH
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?

# re: Adjustor thunks

Friday, February 06, 2004 10:33 AM by jeffdav
Hm. I always wondered how that worked.

# re: Adjustor thunks

Friday, February 06, 2004 11:24 AM by Jim Causey
Raymond's diagrams work just fine in Mozilla Firebird; maybe you need to upgrade your browser?

# re: Adjustor thunks

Friday, February 06, 2004 12:44 PM by rfredell
Works fine in Mozilla 1.6 too.

# Arrows in diagrams

Friday, February 06, 2004 4:35 PM by brion
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. ;)

# well-formed XML

Saturday, February 07, 2004 11:24 PM by Raymond Chen
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" />
</v:shapetype>

# re: well-formed XML

Sunday, February 08, 2004 2:55 AM by brion
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 validator.w3.org... it's very nitpicky!)

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

# re: Adjustor thunks

Sunday, February 08, 2004 7:56 AM by Raymond Chen
Alas, the claim to be HTML 4.0 Transitional is coming from the blog software, not from me.

# re: Adjustor thunks

Sunday, February 08, 2004 7:30 PM by Norman Diamond
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.)

# re: Adjustor thunks

Sunday, February 08, 2004 9:15 PM by Fuggles
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.

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncomg/html/msdn_com_co.asp

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)))

So:

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.

# re: Adjustor thunks

Sunday, February 08, 2004 10:42 PM by Raymond Chen
Aw phooey. I edited this entry to add the link, and in the process I destroyed the arrows. Let me fix them.

# re: Pointers to virtual functions with adjustors

Thursday, March 24, 2005 12:15 PM by The Old New Thing

# What is the underlying object behind a COM interface pointer?

Tuesday, April 24, 2007 12:12 PM by The Old New Thing

Use the vtable.

# Casting from one interface to another...

Friday, February 15, 2008 6:11 PM by Larry Osterman's WebLog

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

New Comments to this post are disabled
 
Page view tracker