Erro Weird MSC 8.0: “O valor do ESP não foi salvo corretamente em uma chamada de function…”

Recentemente, tentamos separar alguns dos nossos projetos do Visual Studio em bibliotecas, e tudo parecia compilar e construir bem em um projeto de teste com um dos projetos da biblioteca como uma dependência. No entanto, a tentativa de executar o aplicativo nos deu a seguinte mensagem de erro desagradável em tempo de execução:

Falha de verificação de tempo de execução # 0 – O valor de ESP não foi salvo corretamente em uma chamada de function. Isso geralmente é resultado de chamar um ponteiro de function declarado com uma convenção de chamada diferente.

Nós nunca especificamos convenções de chamada (__cdecl etc.) para nossas funções, deixando todos os switches do compilador no padrão. Eu verifiquei e as configurações do projeto são consistentes para convenção de chamada em toda a biblioteca e projetos de teste.

Atualização: Um de nossos desenvolvedores alterou a configuração do projeto “Verificações de tempo de execução básicas” de “Ambos (/ RTC1, equiv. Para / RTCsu)” para “Padrão” e o tempo de execução desapareceu, deixando o programa em execução aparentemente corretamente. Eu não confio em nada disso. Essa era uma solução adequada ou um hack perigoso?

Eu li isso em outro fórum

Eu estava tendo o mesmo problema, mas eu apenas corrigi-lo. Eu estava recebendo o mesmo erro do código a seguir:

 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. 

Depois de alguma investigação, mudei uma das linhas para:

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

que resolveu o problema. Se você der uma olhada no arquivo de header onde SetSuspendState está localizado (powrprof.h, parte do SDK), você verá que o protótipo de function é definido como:

 BOOLEAN WINAPI SetSuspendState(BOOLEAN, BOOLEAN, BOOLEAN); 

Então vocês estão tendo um problema semelhante. Quando você está chamando uma determinada function de um .dll, sua assinatura provavelmente está desativada. (No meu caso, foi a palavra-chave WINAPI ausente).

Espero que ajude as pessoas futuras! 🙂

Felicidades.

Silenciar o cheque não é a solução correta. Você tem que descobrir o que está confuso com suas convenções de chamada.

Existem algumas maneiras de alterar a convenção de chamada de uma function sem explicitamente especificá-la. extern “C” irá fazê-lo, STDMETHODIMP / IFACEMETHODIMP também irá fazê-lo, outras macros podem fazê-lo também.

Eu acredito que se executar seu programa em WinDBG ( http://www.microsoft.com/whdc/devtools/debugging/default.mspx ), o tempo de execução deve quebrar no ponto em que você acertar esse problema. Você pode examinar a pilha de chamadas e descobrir qual function tem o problema e, em seguida, examinar sua definição e a declaração que o chamador usa.

Eu vi esse erro quando o código tentou chamar uma function em um object que não era do tipo esperado.

Portanto, hierarquia de classs: pai com filhos: filho1 e filho2

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

Eu estava recebendo erro semelhante para APIs AutoIt que eu estava chamando do programa VC + +.

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

No entanto, quando eu mudei a declaração que inclui WINAPI, como sugerido anteriormente no segmento, o problema desapareceu.

Código sem nenhum erro se parece com:

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

Eu estava recebendo esse erro chamando uma function em uma DLL que foi compilada com uma versão pré-2005 do Visual C ++ de uma versão mais recente do VC (2008). A function tinha essa assinatura:

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

O problema era que o tamanho do time_t é de 32 bits na versão anterior a 2005, mas 64 bits desde o VS2005 (é definido como _time64_t ). A chamada da function espera uma variável de 32 bits, mas obtém uma variável de 64 bits quando chamada de VC> = 2005. Como parâmetros de funções são passados ​​pela pilha ao usar a WINAPI chamada WINAPI , isso corrompe a pilha e gera a mensagem de erro mencionada acima (“Falha na verificação de tempo de execução # 0 …”).

Para corrigir isso, é possível

 #define _USE_32BIT_TIME_T 

antes de include o arquivo de header da DLL ou – melhor – alterar a assinatura da function no arquivo de header dependendo da versão do VS (versões anteriores a 2005 não sabem _time32_t !):

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

Note que você precisa usar _time32_t vez de time_t no programa de chamada, é claro.

Você está criando bibliotecas estáticas ou DLLs? Se DLLs, como são as exportações definidas; como as bibliotecas de importação são criadas?

Os protótipos das funções nas libs são exatamente iguais às declarações de function onde as funções são definidas?

você tem algum protótipo de function typedef’d (por exemplo, int (* fn) (int a, int b))

Se você dom, pode ter errado o protótipo.

ESP é um erro na chamada de uma function (você pode dizer qual no depurador?) Que tem uma incompatibilidade nos parâmetros – ou seja, a pilha foi restaurada para o estado em que começou quando você chamou a function.

Você também pode obter isso se estiver carregando funções C ++ que precisam ser declaradas extern C – C usa cdecl, C ++ usa a convenção de chamada stdcall por padrão (IIRC). Coloque alguns wrappers externos em torno dos protótipos de function importados e você pode consertá-lo.

Se você puder executá-lo no depurador, verá a function imediatamente. Caso contrário, você pode definir o DrWtsn32 para criar um minidump que pode ser carregado no windbg para ver o callstack no momento do erro (você precisará de símbolos ou um arquivo de mapeamento para ver os nomes das funções).

Outro caso em que esp pode ficar confuso é com um estouro de buffer inadvertido, geralmente por meio do uso incorreto de pointers para trabalhar além do limite de uma matriz. Digamos que você tenha alguma function C que se parece com

 int a, b[2]; 

Escrever para b[3] provavelmente mudará a , e em qualquer lugar que seja provável que a mangueira esp salva na pilha.

Você obteria esse erro se a function fosse invocada com uma convenção de chamada diferente daquela para a qual ela é compilada.

O Visual Studio usa uma configuração de convenção de chamada padrão que é decalredada nas opções do projeto. Verifique se esse valor é o mesmo nas configurações do projeto original e nas novas bibliotecas. Um devedor excessivamente ambicioso poderia ter configurado isto para _stdcall / pascal no original, já que reduz o tamanho do código comparado ao cdecl padrão. Assim, o processo base estaria usando essa configuração e as novas bibliotecas receberão o cdecl padrão que causa o problema

Como você disse que não usa nenhuma convenção de chamada especial, esta parece ser uma boa probabilidade.

Também faça um diff nos headers para ver se as declarações / arquivos que o processo vê são os mesmos com os quais as bibliotecas são compiladas.

ps: Fazer o aviso desaparecer é o BAAAD. o erro subjacente ainda persiste.

Isso aconteceu comigo ao acessar um object COM (Visual Studio 2010). Eu passei o GUID para outra interface A para na minha chamada para QueryInterface, mas depois eu casting o ponteiro recuperado como interface B. Isso resultou em fazer uma chamada de function para um com uma assinatura inteira, que representa a pilha (e ESP) sendo bagunçado.

Passar o GUID para a interface B resolveu o problema.

Eu estava tendo exatamente o mesmo erro depois de mover funções para uma dll e carregar dinamicamente a dll com LoadLibrary e GetProcAddress. Eu tinha declarado extern “C” para a function na dll por causa da decoração. Então, isso mudou a convenção de chamada para __cdecl também. Eu estava declarando pointers de function para ser __stdcall no código de carregamento. Depois que mudei o ponteiro de function de __stdcall para__cdecl no código de carregamento, o erro de runtime desapareceu.

No meu aplicativo MFC C ++ estou tendo o mesmo problema relatado no erro Weird MSC 8.0: “O valor do ESP não foi salvo corretamente em uma chamada de function…” . A postagem tem mais de 42 mil visualizações e 16 respostas / comentários, nenhum dos quais culpou o compilador como o problema. Pelo menos no meu caso, posso mostrar que o compilador VS2015 está com defeito.

Minha configuração de teste e desenvolvimento é a seguinte: Eu tenho 3 PCs, todos executando o Win10 versão 10.0.10586. Todos estão compilando com VS2015, mas aqui está a diferença. Dois dos VS2015s possuem Atualização 2, enquanto o outro possui a Atualização 3 aplicada. O PC com a Atualização 3 funciona, mas os outros dois com a Atualização 2 falham com o mesmo erro relatado na postagem acima. Meu código de aplicativo do MFC C ++ é exatamente o mesmo em todos os três computadores.

Conclusão: pelo menos no meu caso para o meu aplicativo, a versão do compilador (Update 2) continha um bug que quebrou meu código. Meu aplicativo faz uso pesado de std :: packaged_task, então espero que o problema esteja no código do compilador relativamente novo.

Vale a pena ressaltar que isso também pode ser um bug do Visual Studio.

Eu tenho esse problema no VS2017, Win10 x64. No começo fazia sentido, já que eu estava fazendo coisas estranhas lançando isso para um tipo derivado e envolvendo-o em um lambda. No entanto, reverti o código para um commit anterior e ainda recebi o erro, mesmo que ele não estivesse lá antes.

Eu tentei reiniciar e, em seguida, recriar o projeto e, em seguida, o erro foi embora.

ESP é o ponteiro da pilha. Então, de acordo com o compilador, o ponteiro da pilha está ficando bagunçado. É difícil dizer como (ou se) isso poderia estar acontecendo sem ver algum código.

Qual é o menor segmento de código que você pode conseguir para reproduzir isso?

Se você estiver usando qualquer function de retorno de chamada com a API do Windows, elas deverão ser declaradas usando CALLBACK e / ou WINAPI . Isso aplicará decorações apropriadas para fazer o compilador gerar código que limpa a pilha corretamente. Por exemplo, no compilador da Microsoft, ele adiciona __stdcall .

O Windows sempre utilizou a convenção __stdcall , pois isso leva a códigos (ligeiramente) menores, com a limpeza ocorrendo na function chamada e não em todos os sites de chamada. Não é compatível com funções varargs, embora (porque apenas o chamador sabe quantos argumentos eles empurraram).

Aqui está um programa C ++ despojado que produz esse erro. Compilado usando (Microsoft Visual Studio 2003) produz o erro mencionado acima.

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

ERRO: "Falha de verificação de tempo de execução # 0 - O valor de ESP não foi salvo corretamente em uma chamada de function. Isso geralmente é resultado de chamar uma function declarada com uma convenção de chamada com um ponteiro de function declarado com uma convenção de chamada diferente."

Eu tive esse mesmo problema aqui no trabalho. Eu estava atualizando algum código muito antigo que estava chamando um ponteiro de function FARPROC. Se você não sabe, os FARPROC são pointers de function com segurança do tipo ZERO. É o equivalente C de um ponteiro de function typdef’d, sem a verificação do tipo de compilador. Por exemplo, digamos que você tenha uma function que leve 3 parâmetros. Você aponta um FARPROC para ele e, em seguida, o chama com 4 parâmetros em vez de 3. O parâmetro extra empurrou o lixo extra para a pilha e, quando ele sai, o ESP agora é diferente de quando foi iniciado. Então eu resolvi isso removendo o parâmetro extra para a invocação da chamada de function FARPROC.

Não é a melhor resposta, mas acabei de recompilar meu código do zero (reconstruir no VS) e, em seguida, o problema foi embora.