Variable arguments in _stdcall, C++ / Inline ASM
I'm in a situation where I have to mock up a _stdcall function using C++ and inline ASM, but which uses a variable number of arguments. Normally it wouldn't know how many arguments to pop from the stack when it returns control to its parent, so wouldn't work, but I'm hoping to tell it via a global variable how many params it should have and then get it to pop them off like that.
Is that actually possible? If so, can someone start me off in the right direction? I'm开发者_JAVA百科 specifically stuck with the epilog code I would need.
My objective is to make a function which can be used as a callback for any function that requires one (like EnumWindows), so long as the user tells it at runtime how long the args list has to be. The idea is for it to integrate with some code elsewhere so it basically runs a trigger each time the callback is called and provides a link to a place where the variables that were returned can be read and viewed by the user.
Does that make sense?
Doesn't make sense. __stdcall
doesn't allow variadic parameters, as the total size of all parameters is decorated into the function name (from msdn):
Name-decoration convention
An underscore (_) is prefixed to the name. The name is followed by the at sign (@) followed by the number of bytes (in decimal) in the argument list. Therefore, the function declared as
int func( int a, double b )
is decorated as follows:_func@12
This quote tells you how variadic __stdcall
functions are implemented:
The __stdcall calling convention is used to call Win32 API functions. The callee cleans the stack, so the compiler makes vararg functions __cdecl. Functions that use this calling convention require a function prototype.
(emphasis mine)
So, there are no __stdcall
functions with variadic parameters, they silently get changed to __cdecl
. :)
You can do something like the following (hacked up code):
static int NumberOfParameters = 0;
__declspec(naked) void GenericCallback()
{
// prologue
__asm push ebp
__asm mov ebp, esp
// TODO: do something with parameters on stack
// manual stack unwinding for 2 parameters
// obviously you would adjust for the appropriate number of parameters
// (e.g. NumberOfParameters) instead of hard-coding it for 2
// fixup frame pointer
__asm mov eax, [ebp + 0]
__asm mov [ebp + 8], eax // NumberOfParameters * 4 (assuming dword-sized parameters)
// fixup return address
__asm mov eax, [ebp + 4]
__asm mov [ebp + 12], eax // (NumberOfParameters + 1) * 4
// return TRUE
__asm mov eax, 1
// epilogue
__asm mov esp, ebp
__asm pop ebp
// fixup stack pointer
__asm add esp, 8 // NumberOfParameters * 4
__asm ret 0
}
int main(int argc, _TCHAR* argv[])
{
NumberOfParameters = 2;
EnumWindows((WNDENUMPROC)GenericCallback, NULL);
return 0;
}
精彩评论