Вызов функции, которая может быть либо cdecl, либо stdcall

Мне нужно написать код, который вызывает внешнюю функцию, которая может быть вызовом stdcall или cdecl в 32-битном приложении Windows.
Мой код, вызывающий объект, не может знать заранее, какой из них будет. Прямо сейчас, если я попытаюсь вызвать функцию cdecl с места вызова, которое было определено как stdcall, я получу диалоговое окно исключения checkEsp, и я предполагаю, что оно существует по уважительной причине.
Есть ли способ сделать это? ?


person shoosh    schedule 13.07.2013    source источник
comment
Попробуйте использовать библиотеку интерфейса внешних функций (FFI).   -  person Kerrek SB    schedule 13.07.2013
comment
FFI по-прежнему необходимо знать соглашение о вызовах   -  person shoosh    schedule 13.07.2013
comment
@HansPassant порядок аргументов такой же   -  person shoosh    schedule 13.07.2013


Ответы (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 указывает на возврат. Оба соглашения согласны с тем, какие регистры сохраняются при вызове, а какие затираются.

person johnfound    schedule 13.07.2013
comment
Спасибо! это потрясающе - person shoosh; 13.07.2013

Вы также можете использовать alloca(), который имеет побочный эффект сохранения и восстановления указателя стека:

{
    alloca( (uintptr_t)callback & 2 );
    callback();
}
person Sam Lantinga    schedule 09.05.2014

cdecl и stdcall по определению несовместимы. В cdecl вызывающий объект очищает стек, в stdcall вызываемый объект очищает стек. Если считать stdcall, а на самом деле это cdecl, никто не чистит стек. Это означает, что ваш ESP (указатель стека) будет испорчен после вызова. Возможно, если вы дадите более подробную информацию, может быть, есть обходной путь, но нет способа вызвать функцию, не зная ее соглашения о вызовах, не испортив ваш стек.

См. : http://en.wikipedia.org/wiki/X86_calling_conventions для определения разницы .

person Enkid    schedule 13.07.2013
comment
Итак, есть ли надежный способ проверить эту ситуацию и просто исправить ESP, если это необходимо? Что вообще делает checkEsp? - person shoosh; 13.07.2013
comment
ESP — это ваш указатель стека. Он отслеживает, где в настоящее время находится ваш стек. Теоретически вы могли бы сохранить ESP в переменную в куче и получить ее после вызова, но это было бы очень грязно... и я никогда не пробовал это, поэтому я даже не уверен, что это сработает. Я не уверен в механизме проверки, используемом ESP, чтобы убедиться, что ESP правильно поддерживается до и после вызова. Лучшей стратегией было бы проверить соглашение о вызовах и скорректировать ваш вызов на основе этого, хотя я не видел последовательного метода для программной проверки соглашения о вызовах во время выполнения. - person Enkid; 13.07.2013