2013-07-13 3 views
4

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

+0

Попробуйте использовать функцию внешней интерфейс (FFI) библиотеки. –

+0

FFI все еще должен знать вызывающее соглашение – shoosh

+0

@HansPassant порядок аргументов один и тот же – shoosh

ответ

3

Это можно сделать следующим образом:

  mov  esi, esp 

      push arg3 
      push arg2 
      push arg1 
      call [SomeExternalProc] 

      mov  esp, esi ; now the stack is always properly cleaned 

Внешняя процедура должна сохранить ЕУИ. Или вы можете использовать любой другой регистр, сохраненный внешней процедурой, или даже переменную памяти - локальную или глобальную.

Хорошо, порядок аргументов одинаковый для CDECL и STDCALL - в обратном порядке.

+0

Спасибо! это потрясающе – shoosh

0

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

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

+0

Ну, есть ли надежный способ проверить эту ситуацию и просто исправить ESP, если это необходимо? Что вообще делает checkEsp? – shoosh

+0

ESP - это указатель стека. Он отслеживает, где находится ваш стек. Теоретически, вы могли бы сохранить ESP в переменной в куче и получить ее после вызова, но это было бы очень грязно ... и я никогда не пробовал этого, поэтому я даже не уверен, что это сработает. Я не уверен в проверке механизма ESP, чтобы убедиться, что ESP правильно поддерживается до и после вызова. Лучшей стратегией было бы проверить соглашение о вызове и настроить ваш вызов на основе этого, хотя я не видел согласованного метода для программной проверки соглашения о вызове во время выполнения. – Enkid

1

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

{ 
    alloca((uintptr_t)callback & 2); 
    callback(); 
} 
Смежные вопросы