Мне нужно написать код, который вызывает внешнюю функцию, которая может быть вызовом stdcall или cdecl в 32-битном приложении Windows.
Мой код, вызывающий объект, не может знать заранее, какой из них будет. Прямо сейчас, если я попытаюсь вызвать функцию cdecl с места вызова, которое было определено как stdcall, я получу диалоговое окно исключения checkEsp, и я предполагаю, что оно существует по уважительной причине.
Есть ли способ сделать это? ?
Вызов функции, которая может быть либо cdecl, либо stdcall
Ответы (3)
Это можно сделать следующим образом:
mov esi, esp
push arg3
push arg2
push arg1
call [SomeExternalProc]
mov esp, esi ; now the stack is always properly cleaned
Внешняя процедура сохранит esi. Или вы можете использовать любой другой регистр, сохраняемый внешней процедурой, или даже переменную памяти — локальную или глобальную.
Хорошо, порядок аргументов для CDECL и STDCALL одинаков — в обратном порядке. (Самый левый аргумент по наименьшему адресу.) Таким образом, они совместимы, за исключением того, где ESP указывает на возврат. Оба соглашения согласны с тем, какие регистры сохраняются при вызове, а какие затираются.
Вы также можете использовать alloca(), который имеет побочный эффект сохранения и восстановления указателя стека:
{
alloca( (uintptr_t)callback & 2 );
callback();
}
cdecl и stdcall по определению несовместимы. В cdecl вызывающий объект очищает стек, в stdcall вызываемый объект очищает стек. Если считать stdcall, а на самом деле это cdecl, никто не чистит стек. Это означает, что ваш ESP (указатель стека) будет испорчен после вызова. Возможно, если вы дадите более подробную информацию, может быть, есть обходной путь, но нет способа вызвать функцию, не зная ее соглашения о вызовах, не испортив ваш стек.
См. : http://en.wikipedia.org/wiki/X86_calling_conventions для определения разницы .