Я пишу некоторый код плагина в dll, который вызывается хостом, который я не могу контролировать.
Хост предполагает, что плагины экспортируются как функции __stdcall. Хосту сообщается имя функции и подробности ожидаемых аргументов, и он динамически обрабатывает вызов к нему через LoadLibrary, GetProcAddress и вручную помещает аргументы в стек.
Обычно dll плагина предоставляет постоянный интерфейс. Мой плагин предоставляет интерфейс, который настраивается во время загрузки DLL. Чтобы достичь этого, мой плагин предоставляет набор стандартных точек входа, которые определяются во время компиляции dll, и распределяет их по мере необходимости для раскрываемой внутренней функциональности.
Каждая из внутренних функций может принимать разные аргументы, но это сообщается хосту вместе с именем физической точки входа. Все мои физические точки входа в dll определены для получения одного указателя void *, и я сам собираю последующие параметры из стека, работая со смещениями из первого аргумента и списка известных аргументов, которые были переданы хосту.
Хост может успешно вызывать функции в моем плагине с правильными аргументами, и все работает хорошо ... Однако я знаю, что а) мои функции не очищают стек, как они должны, поскольку они Определяются как функции __stdcall, которые принимают 4-байтовый указатель, и поэтому они всегда делают 'ret 4' в конце, даже если вызывающий объект выдвинул больше аргументов в стек. и б) я не могу иметь дело с функциями, которые не принимают аргументов, так как ret 4 вытащит 4 байта слишком много из стека при моем возвращении.
Проследив из моего плагина в вызывающий код хоста, я вижу, что на самом деле а) не так уж и много; хост теряет некоторое место в стеке, пока не вернется из диспетчерского вызова, и в этот момент он очищает свой кадр стека, который очищает мой мусор; однако ...
Я могу решить б), переключившись на __cdecl и вообще не убирая. Я предполагаю, что могу решить а), переключившись на обнаженные функции и написав свой собственный общий код очистки аргументов.
Поскольку я знаю количество аргументов, используемых только что вызванной функцией, я надеялся, что это будет так просто:
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
}
}
Это выглядит правильно?