Подтвердить что ты не робот

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

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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

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

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

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

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

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

Ответ 2

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

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

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

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

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

Ответ 3

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

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

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

Ответ 4

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

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

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

Ответ 5

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

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

Ответ 6

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

Ответ 7

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

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

Ответ 8

Есть ли у вас какие-либо прототипы функции typedef'd (например, int (* fn) (int a, int b))

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

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

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

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

Ответ 9

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

int a, b[2];

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

Ответ 10

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

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

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

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

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

Ответ 11

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

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

Ответ 12

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

Ответ 13

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

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

Ответ 14

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

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

Ответ 15

Здесь урезанная программа на С++, которая производит эту ошибку. Скомпилированная с использованием (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 не было должным образом сохранено в вызове функции. Обычно это результат вызова функции, объявленной с одним вызовом, с указателем функции, объявленным с другим соглашением о вызове".

Ответ 16

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

Ответ 17

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

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

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

Ответ 18

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