我正在一个dll中编写一些插件代码,这个dll由一个我无法控制的主机调用。
主机假定插件导出为uu stdcall函数。主机被告知函数的名称和它所期望的参数的详细信息,并通过LoadLibrary、GetProcAddress和手动将参数推送到堆栈上动态地对其进行调用。
每个内部函数可能采用不同的参数,但这将与物理入口点名称一起传递给主机。我的所有物理dll入口点都被定义为使用一个void*指针,我自己通过处理第一个参数和已与主机通信的已知参数列表的偏移量,从堆栈封送后续参数。
主机可以用正确的参数成功地调用我插件中的函数,并且一切都很好。。。但是,我知道a)我的函数没有像它们被定义为u stdcall函数那样清理堆栈,它们使用4字节指针,因此即使调用方将更多参数推到堆栈上,它们也总是在最后执行“ret 4”。b)我不能处理不带参数的函数,因为ret4在返回时会从堆栈中弹出太多的4字节。
因为我知道刚刚调用的函数所使用的参数空间量,所以我希望它能简单到:
extern "C" __declspec(naked) __declspec(dllexport) void * __stdcall EntryPoint(void *pArg1)
{
size_t argumentSpaceUsed;
{
void *pX = RealEntryPoint(
reinterpret_cast<ULONG_PTR>(&pArg1),
argumentSpaceUsed);
__asm
{
mov eax, dword ptr pX
}
}
__asm
{
ret argumentSpaceUsed
}
}
但这不起作用,因为ret需要一个编译时常量。。。有什么建议吗?
更新:
extern "C" __declspec(naked) __declspec(dllexport) void * __stdcall EntryPoint(void *pArg1)
{
__asm {
push ebp // Set up our stack frame
mov ebp, esp
mov eax, 0x0 // Space for called func to return arg space used, init to 0
push eax // Set up stack for call to real Entry point
push esp
lea eax, pArg1
push eax
call RealEntryPoint // result is left in eax, we leave it there for our caller....
pop ecx
mov esp,ebp // remove our stack frame
pop ebp
pop edx // return address off
add esp, ecx // remove 'x' bytes of caller args
push edx // return address back on
ret
}
}
这个看起来对吗?