我遇到的情况是我必须使用C ++和内联ASM来模拟_stdcall函数,但是它使用了可变数量的参数。通常它不会知道当它将控制权返回给它的父级时从堆栈中弹出多少个参数,所以不起作用,但我希望通过一个全局变量告诉它它应该有多少个参数然后得到它像那样弹出它们。
这实际上可行吗?如果是这样,有人可以让我朝着正确的方向前进吗?我特别坚持使用我需要的epilog代码。
我的目标是创建一个函数,可以用作任何需要一个函数(如EnumWindows)的回调函数,只要用户在运行时告诉它args列表必须有多长。这个想法是它与其他地方的代码集成,因此每次调用回调时它基本上都会运行一个触发器,并提供一个链接,指向用户可以读取和查看返回的变量的地方。
这有意义吗?
答案 0 :(得分:4)
没有意义。 __stdcall
不允许使用可变参数,因为所有参数的总大小被装饰到函数名称(from msdn)中:
名称装饰惯例
下划线(_)以名称为前缀。名称后跟at符号(@),后跟参数列表中的字节数(十进制)。因此,声明为
int func( int a, double b )
的函数按如下方式进行修饰:_func@12
此引用告诉您如何实现可变__stdcall
函数:
__stdcall调用约定用于调用Win32 API函数。 被调用者清理堆栈,因此编译器生成vararg函数__cdecl 。使用此调用约定的函数需要函数原型。
(强调我的)
因此,没有带有可变参数的__stdcall
函数,它们会默默地更改为__cdecl
。 :)
答案 1 :(得分:0)
您可以执行以下操作(黑客代码):
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;
}