После некоторого исследования:
Помощником, который спасает ситуацию от сбоя, является другой регистр - EBP, базовый указатель, который указывает на начало кадра стека.Весь доступ к локальным переменным функции осуществляется через этот указатель (кроме оптимизированного кода, см. Редактирование ниже).Перед возвратом функции указатель стека сбрасывается до значения базового указателя.
Перед тем, как функция (скажем, PInvoke) вызывает другую функцию (импортированная функция DLL), указатель стека указывает на конец локальной функции вызывающей функциипеременные.Затем вызывающая сторона передает параметры в стек и вызывает эту другую функцию.
В описанной ситуации, когда функция вызывает другую функцию как __stdcall, в то время как она фактически является __cdecl, никто не очищает стек от этих параметров.Итак, после возврата из вызываемого, указатель стека указывает на конец блока передаваемых параметров.Это как функция вызывающего (PInvoke) только что получила еще несколько локальных переменных.
Поскольку доступ к локальным переменным вызывающего абонента осуществляется через базовый указатель, он ничего не нарушает.Единственная плохая вещь, которая может случиться, - это если вызываемая функция будет вызываться много раз одновременно.В этом случае стек будет расти и может переполниться.Но поскольку PInvoke вызывает функцию DLL только один раз, а затем возвращает ее, указатель стека просто сбрасывается на базовый указатель, и все в порядке. Редактировать: Как отмечается здесь , код также может быть оптимизирован для хранения локальных переменных только в регистрах ЦП.В этом случае EBP не используется, и, следовательно, недействительный ESP может привести к возврату на неверный адрес.