Ошибка Weird MSC 8.0: «Значение ESP не было должным образом сохранено в вызове функции …»

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

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

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

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

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

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

1) Несоответствующие соглашения о вызовах:

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

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

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

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

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

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

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

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

У меня была такая же проблема, но я просто исправил ее. Я получал ту же ошибку из следующего кода:

 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).

Надеюсь, что это поможет любому будущему людям! 🙂

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

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

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

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

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

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

 Child1* pMyChild = 0; ... pMyChild = pSomeClass->GetTheObj();// This call actually returned a Child2 object pMyChild->SomeFunction(); // "...value of ESP..." error occurs here 

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

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

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

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

 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); } 

Я получал эту ошибку, вызывая функцию в 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 в вызывающей программе, конечно.

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

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

у вас есть прототипы функций typedef’d (например, int (* fn) (int a, int b))

если вы доминируете, возможно, вы ошиблись в прототипе.

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

Вы также можете получить это, если загружаете функции C ++, которые должны быть объявлены extern. C – C использует cdecl, C ++ использует стандартное условное соглашение stdcall по умолчанию (IIRC). Поместите некоторые внешние оболочки C вокруг импортированных прототипов функций, и вы можете исправить их.

Если вы можете запустить его в отладчике, вы сразу увидите функцию. Если нет, вы можете установить DrWtsn32 для создания minidump, который вы можете загрузить в windbg, чтобы увидеть столбец во время ошибки (вам понадобятся символы или файл карты, чтобы увидеть имена функций).

Другой случай, когда esp можно перепутать, – это непреднамеренное переполнение буфера, обычно ошибочное использование указателей для работы за границей массива. Скажем, у вас есть функция C, которая выглядит как

 int a, b[2]; 

Запись в b[3] , вероятно, изменит значение a и где-нибудь в прошлом, что, вероятно, будет выталкивать сохраненный esp в стеке.

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

Visual Studio использует настройку условного соглашения по умолчанию, которая декалирована в параметрах проекта. Проверьте, одинаково ли это значение в настройках проекта orignal и в новых библиотеках. Более амбициозный разработчик мог бы установить это в _stdcall / pascal в оригинале, так как он уменьшает размер кода по сравнению с Cdecl по умолчанию. Таким образом, базовый процесс будет использовать этот параметр, а новые библиотеки получают по умолчанию cdecl, который вызывает проблему

Поскольку вы сказали, что не используете специальные соглашения о вызовах, это кажется хорошей вероятностью.

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

ps: Устранение предупреждения – BAAAD. основная ошибка все еще сохраняется.

Это произошло со мной при доступе к COM-объекту (Visual Studio 2010). Я передал GUID для другого интерфейса A в моем вызове QueryInterface, но затем я передал извлеченный указатель как интерфейс B. Это привело к вызову функции для одного с полной сигнатурой, которая учитывает стек (и ESP), который перепутались.

Проблема с идентификатором GUID для интерфейса B.

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

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

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

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

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

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

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

ESP – указатель стека. Поэтому, согласно компилятору, ваш указатель стека становится испорченным. Трудно сказать, как (или если) это могло произойти, не видя какого-либо кода.

Каков самый маленький сегмент кода, который вы можете воспроизвести?

Если вы используете функции обратного вызова в Windows API, они должны быть объявлены с использованием CALLBACK и / или WINAPI . Это применит соответствующие декорации, чтобы компилятор сгенерировал код, который правильно очищает стек. Например, в компиляторе Microsoft он добавляет __stdcall .

Windows всегда использовала соглашение __stdcall поскольку оно приводит к (слегка) меньшему коду, причем очистка происходит в вызываемой функции, а не на каждом сайте вызова. Однако он не совместим с функциями varargs (потому что только вызывающий абонент знает, сколько аргументов они нажали).

Вот урезанная программа на С ++, которая производит эту ошибку. Скомпилированная с использованием (Microsoft Visual Studio 2003) выдает вышеупомянутую ошибку.

 #include "stdafx.h" char* blah(char *a){ char p[1]; strcat(p, a); return (char*)p; } int main(){ std::cout << blah("a"); std::cin.get(); } 

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

У меня была такая же проблема здесь на работе. Я обновлял очень старый код, который вызывал указатель на функцию FARPROC. Если вы не знаете, FARPROC – это указатели на функции с безопасностью типа ZERO. Это эквивалент C для указателя функции typdef’d без проверки типа компилятора. Например, скажем, у вас есть функция, которая принимает 3 параметра. Вы указываете на него FARPROC, а затем вызываете его с 4 параметрами вместо 3. Дополнительный параметр выталкивает лишний мусор в стек, а когда он всплывает, ESP теперь отличается от того, когда он запускался. Поэтому я решил это, удалив дополнительный параметр для вызова вызова функции FARPROC.

Не лучший ответ, но я просто перекомпилировал свой код с нуля (перестроил в VS), а затем проблема исчезла.

  • Вызов удаления по переменной, выделенной в стеке
  • Анализ Escape в Java
  • где найти значение XSS по умолчанию для Sun / Oracle JVM?
  • Как увеличить размер стека Java?
  • c ++ Vector, что происходит, когда он расширяется / перераспределяется в стеке?
  • Почему термины «автоматический» и «динамический» предпочтительнее терминов «стек» и «куча» в управлении памятью C ++?
  • В чем разница между parent.frame () и parent.env () в R; как они отличаются при вызове по ссылке?
  • Создание объекта в стеке / куче?
  • Java / Android - Как распечатать полную трассировку стека?
  • Есть ли стековая или безвыходная реализация C ++?
  • что может привести к сбросу стоп-кадра (я использую «throw», а не «throw ex»)
  • Давайте будем гением компьютера.