Странная ошибка MSC 8.0: «Значение ESP не было правильно сохранено при вызове функции ...» - PullRequest
45 голосов
/ 27 сентября 2008

Недавно мы попытались разбить некоторые из наших проектов Visual Studio на библиотеки, и, похоже, все компилировалось и строилось нормально в тестовом проекте с одним из библиотечных проектов в качестве зависимости. Однако, попытка запустить приложение дала нам следующее неприятное сообщение об ошибке во время выполнения:

Ошибка проверки времени выполнения # 0 - значение ESP не было должным образом сохранено при вызове функции. Обычно это результат вызова указателя функции, объявленного с другим соглашением о вызовах.

Мы никогда даже не указывали соглашения о вызовах (__cdecl и т. Д.) Для наших функций, оставляя все переключатели компилятора по умолчанию. Я проверил, и настройки проекта соответствуют правилам вызова для библиотеки и тестовых проектов.

Обновление: один из наших разработчиков изменил настройку проекта «Базовые проверки времени выполнения» с «Оба (/ RTC1, эквивалентно / RTCsu)» на «По умолчанию», и время выполнения исчезло, оставив программу работающей, по-видимому, правильно. Я не доверяю этому вообще. Это было правильное решение или опасный взлом?

Ответы [ 19 ]

47 голосов
/ 26 августа 2009

Эта ошибка отладки означает, что регистр указателя стека не возвращается к своему первоначальному значению после вызова функции, то есть, что число толкает до вызова функции не сопровождалось равным числом всплывает после звонка.

Мне известны две причины (обе с динамически загружаемыми библиотеками). # 1 - это то, что VC ++ описывает в сообщении об ошибке, но я не думаю, что это самая частая причина ошибки (см. # 2).

1) Несоответствующие правила вызова:

Звонящий и вызываемый не имеют надлежащего соглашения о том, кто и что собирается делать. Например, если вы вызываете функцию DLL, которая является _stdcall, но вы по какой-то причине объявили ее как _cdecl (по умолчанию в VC ++) в вашем вызове. Это часто случается, если вы используете разные языки в разных модулях и т. Д.

Вы должны проверить объявление функции-нарушителя и убедиться, что она объявлена ​​не дважды, а по-другому.

2) Несоответствующие типы:

Вызывающий и вызываемый не скомпилированы с одними и теми же типами. Например, общий заголовок определяет типы в API и недавно изменился, и один модуль был перекомпилирован, а другой - нет, т.е. некоторые типы могут иметь разные размеры в вызывающем и вызываемом объектах.

В этом случае вызывающий объект выдвигает аргументы одного размера, но вызываемый (если вы используете _stdcall, когда вызываемый объект очищает стек) отображает другой размер. Таким образом, ESP не возвращается к правильному значению.

(Конечно, эти и другие аргументы, представленные ниже, могут показаться искаженными в вызываемой функции, но иногда вы можете пережить это без видимого сбоя.)

Если у вас есть доступ ко всему коду, просто перекомпилируйте его.

18 голосов
/ 26 июля 2010

Я читал это на другом форуме

У меня была такая же проблема, но я просто ИСПРАВЛЕНО. Я получаю ту же ошибку из следующего кода:

HMODULE hPowerFunctions = LoadLibrary("Powrprof.dll");
typedef bool (*tSetSuspendStateSig)(BOOL, BOOL, BOOL);

tSetSuspendState SetSuspendState = (tSuspendStateSig)GetProcAddress(hPowerfunctions, "SetSuspendState");

result = SetSuspendState(false, false, false); <---- This line was where the error popped up. 

После некоторого расследования я изменил одну из строк на:

typedef bool (WINAPI*tSetSuspendStateSig)(BOOL, BOOL, BOOL);

который решил проблему. Если вы посмотрите в заголовочный файл, где находится SetSuspendState (powrprof.h, часть SDK), вы увидите, что прототип функции определен как:

BOOLEAN WINAPI SetSuspendState(BOOLEAN, BOOLEAN, BOOLEAN);

Так что у вас, ребята, похожая проблема. Когда вы вызываете данную функцию из .dll, ее подпись, вероятно, отключена. (В моем случае это было пропущенное ключевое слово WINAPI).

Надеюсь, это поможет любым будущим людям! : -)

Приветствие.

11 голосов
/ 27 сентября 2008

Отключение чека не является правильным решением. Вы должны выяснить, что смешалось с вашими соглашениями о вызовах.

Существует довольно много способов изменить вызывающую конвекцию функции без ее явного указания. extern "C" сделает это, STDMETHODIMP / IFACEMETHODIMP также сделает это, другие макросы также могут это сделать.

Я полагаю, что если запустить вашу программу под WinDBG (http://www.microsoft.com/whdc/devtools/debugging/default.mspx),), среда выполнения должна прерваться в тот момент, когда вы столкнулись с этой проблемой. Вы можете посмотреть на стек вызовов и выяснить, какая функция имеет проблему, а затем посмотреть его определение и объявление, которое использует вызывающий.

5 голосов
/ 17 июня 2012

Я получал похожую ошибку для API AutoIt, которую я вызывал из программы VC ++.

    typedef long (*AU3_RunFn)(LPCWSTR, LPCWSTR);

Однако, когда я изменил объявление, которое включает WINAPI, как предлагалось ранее в потоке, проблема исчезла.

Код без ошибок выглядит так:

typedef long (WINAPI *AU3_RunFn)(LPCWSTR, LPCWSTR);

AU3_RunFn _AU3_RunFn;
HINSTANCE hInstLibrary = LoadLibrary("AutoItX3.dll");
if (hInstLibrary)
{
  _AU3_RunFn = (AU3_RunFn)GetProcAddress(hInstLibrary, "AU3_WinActivate");
  if (_AU3_RunFn)
     _AU3_RunFn(L"Untitled - Notepad",L"");
  FreeLibrary(hInstLibrary);
}
5 голосов
/ 14 мая 2009

Я видел эту ошибку, когда код пытался вызвать функцию для объекта, который не был ожидаемого типа.

Итак, иерархия классов: родитель с детьми: Child1 и Child2

Child1* pMyChild = 0;
...
pMyChild = pSomeClass->GetTheObj();// This call actually returned a Child2 object
pMyChild->SomeFunction();          // "...value of ESP..." error occurs here
2 голосов
/ 13 марта 2018

Стоит отметить, что это также может быть ошибка Visual Studio.

Я получил эту проблему на VS2017, Win10 x64. Сначала это имело смысл, так как я делал странные вещи, приводя это к производному типу и упаковывая его в лямбду. Тем не менее, я вернул код к предыдущему коммиту и все равно получил ошибку, хотя ее раньше не было.

Я попытался перезапустить, а затем пересобрать проект, а затем ошибка исчезла.

2 голосов
/ 21 мая 2014

Я получал эту ошибку, вызывая функцию в DLL, которая была скомпилирована с версией Visual C ++ до 2005 года из более новой версии VC (2008). Функция имела такую ​​подпись:

LONG WINAPI myFunc( time_t, SYSTEMTIME*, BOOL* );

Проблема заключалась в том, что размер time_t составляет 32 бита в версии до 2005 года, но 64 бита начиная с VS2005 (определяется как _time64_t). Вызов функции ожидает 32-битную переменную, но получает 64-битную переменную при вызове из VC> = 2005. Поскольку параметры функций передаются через стек при использовании WINAPI соглашения о вызовах, это повреждает стек и генерирует вышеупомянутое выше. сообщение об ошибке («Ошибка проверки времени выполнения # 0 ...»).

Чтобы исправить это, можно

#define _USE_32BIT_TIME_T

перед включением файла заголовка DLL или - лучше - изменить сигнатуру функции в файле заголовка в зависимости от версии VS (версии до 2005 года не знают _time32_t!):

#if _MSC_VER >= 1400
LONG WINAPI myFunc( _time32_t, SYSTEMTIME*, BOOL* );
#else
LONG WINAPI myFunc( time_t, SYSTEMTIME*, BOOL* );
#endif

Обратите внимание, что вам нужно использовать _time32_t вместо time_t в вызывающей программе.

1 голос
/ 26 августа 2016

В моем приложении MFC C ++ возникла та же проблема, что и в Странная ошибка MSC 8.0: «Значение ESP не было правильно сохранено при вызове функции…» . Публикация имеет более 42K просмотров и 16 ответов / комментариев, ни один из которых не обвинял компилятор в проблеме. По крайней мере, в моем случае я могу показать, что виноват компилятор VS2015.

Мои настройки dev и test следующие: у меня 3 компьютера, на каждом из которых установлена ​​версия 10.0.10586 Win10. Все компилируются с VS2015, но здесь есть разница. У двух из VS2015 есть Обновление 2, а у другого - Обновление 3. ПК с обновлением 3 работает, но два других с обновлением 2 выходят из строя с той же ошибкой, о которой сообщалось в публикации выше. Мой код приложения MFC C ++ одинаков на всех трех ПК.

Вывод: по крайней мере, в моем случае для моего приложения версия компилятора (обновление 2) содержала ошибку, которая нарушала мой код. Мое приложение интенсивно использует std :: packaged_task, поэтому я ожидаю, что проблема была в этом довольно новом коде компилятора.

1 голос
/ 10 января 2015

У меня была точно такая же ошибка после перемещения функций в dll и динамической загрузки dll с помощью LoadLibrary и GetProcAddress. Я объявил extern "C" для функции в DLL из-за украшения. Это изменило соглашение о вызовах на __cdecl. Я объявил указатели на функции __stdcall в коде загрузки. Как только я изменил указатель функции с __stdcall на __cdecl в коде загрузки, ошибка времени выполнения исчезла.

1 голос
/ 27 сентября 2008

Вы создаете статические библиотеки или библиотеки DLL? Если DLL, как определяется экспорт; как создаются библиотеки импорта?

Являются ли прототипы функций в библиотеках точно такими же, как объявления функций, в которых определены функции?

...