__stdcall Functions and Variable Arguments

Dear Dr. GUI:

I've spent the day on this. I'm fed up and in desperate need of some kind soul to point out the error of my ways. I'm trying to call a COM object interface function (somewhat indirectly) from a piece of code in a DLL built with Visual C++. The function I want to call uses the __stdcall convention (that is, it cleans up the stack before returning to the caller). What I'm seeing is that both the caller and the callee are cleaning up the stack—hence, kaboom!

Forgive the somewhat primitive nature of the code here, but I have resorted to very small steps in order to see what's going on in the debugger:

typedef HRESULT (__stdcall METHOD)(...);
typedef METHOD* PMETHOD;
typedef PMETHOD* PVTABLE;
typedef PVTABLE* POBJIFACE;
STDAPI comCallStdMember3(IUnknown* pInterface, DWORD dwIndex, DWORD dwArg1,
  DWORD dwArg2, DWORD dwArg3)
{
POBJIFACE pObjIface = (POBJIFACE)pInterface;
PVTABLE pVtable = *pObjIface;
PMETHOD pMethod = pVtable[dwIndex];
HRESULT hr = pMethod(pInterface, dwArg1, dwArg2, dwArg3);
return hr;
}

This code is called from Visual Basic (not that it matters). The call executes correctly, but on return the stack gets an additional cleanup, which should not happen.

Mark Thompson, age 2 (his father was ashamed to admit that he did this)

Dr. GUI replies:

I must say you have an extremely advanced understanding of coding—and of the English language—for someone your age, Mark. I must consult Piaget about this. But to answer your question: The problem is with your definition of METHOD. There is no such thing as a __stdcall variable argument function as you have defined it, so the __stdcall is silently ignored. Variable argument functions are always cleaned up by the caller (__cdecl), since the caller is the only one that knows at compile time how many arguments were really passed.

So, you call comCallStdMember3, which in turn calls METHOD. METHOD returns and cleans up the stack. Then comCallStdMember3 returns and cleans up the stack. Hence, kaboom!

You'll have to use individual, nonvariable argument function pointer typedefs instead.