2008-09-27 6 views
40

Недавно мы попытались разбить некоторые из наших проектов Visual Studio на библиотеки, и все, казалось, скомпилировалось и построилось в тестовый проект с одним из проектов библиотеки как зависимость. Тем не менее, попытка запустить приложение дало нам следующее неприятное время выполнения сообщений об ошибке:Ошибка Weird MSC 8.0: «Значение ESP не было должным образом сохранено в вызове функции ...»

Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function pointer declared with a different calling convention.

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

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

+4

Будь так рад, что время исполнения поймало это для вас. Если бы этого не произошло, следующая вещь, которую должен был сделать компьютер, - это уничтожить содержимое стека и провалиться ужасно. (Отладка коррупции стека не для слабонервных.) – 2010-07-26 08:39:29

+0

RE ваше обновление: Нет, это не правильное решение. Все, что вы сделали, это отключить проверки. Это похоже на похоронить голову в песке. Проблема все еще существует и, несомненно, взорвется вам позже, когда будет еще сложнее отследить. – 2016-12-30 13:37:48

ответ

0

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

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

11

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

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

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

1

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

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

1

у вас есть какие-либо функции typedef'd прототипы (например, Int (* п) (Int а, б) ИНТ)

если вы Dóm вы могли бы получили прототип неправильно.

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

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

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

1

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

int a, b[2]; 

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

0

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

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

1

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

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

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

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

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

5

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

Таким образом, иерархия классов: Родитель с детьми: ребенка1 и ребенка2

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

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

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

1) Несогласованные соглашения о вызове:

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

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

2) Несовпадение типов:

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

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

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

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

+0

+1 хорошее объяснение, было бы идеально, если бы вы поставили некоторые примеры кода, чтобы вести его – jyz 2013-07-23 03:37:42

+0

У меня было то же исключение, но ни один из вышеперечисленных случаев не был. Я боролся с ним в течение нескольких часов, пока я, наконец, не сузил проблему до функции, у которой есть аргумент, указывающий на функцию члена другого класса. Вызов этой функции вызвал повреждение стека. Решение этой проблемы можно найти здесь: http://stackoverflow.com/questions/8676879/member-function-pointer-runtime-error-the-value-of-esp-was-not-properly-saved? rq = 1 – Sushi271 2014-12-31 01:36:34

+0

возможность 3 - несогласованные имена при получении указателя на функцию (возможно, через вызов getProcAddress («theWrongFuntionName»). Это то, что я сделал! Что случилось: я привязал указатель на именованную функцию к прототипу функции-указателя (через typedef). Все выглядит правильно - никаких ошибок компиляции, но вы вызываете неправильную функцию во время выполнения. Думаю, вам не повезло, чтобы ошибочно ввести имя, которое на самом деле существует в вашей DLL, но это не тот вы хотите, иначе вы будете сохранены и получите null обратно из getProcAddress(). – Freya301 2015-11-25 20:11:38

17

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

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

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

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

Cheers.

0

Вот урезанная программа на C++, которая производит эту ошибку. Скомпилированная с использованием (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(); 
} 

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

5

Я получал схожую ошибку для 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); 
} 
1

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

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

2

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

0

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

1

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

0

В моем приложении MFC C++ я испытываю ту же проблему, что и в Weird MSC 8.0 error: “The value of ESP was not properly saved across a function call…”. Сообщение имеет более 42K просмотров и 16 ответов/комментариев, ни одна из которых не обвинила компилятор в качестве проблемы. По крайней мере, в моем случае я могу показать, что компилятор VS2015 виноват.

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

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

0

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

 Смежные вопросы

  • Нет связанных вопросов^_^