VC++ 2005 e Windows 95 não casam
Caloni, 2006-05-26 computer blogHoje foi mais um dia de surpresas com a nova versão do ambiente de programação da Microsoft. De cara descobrimos que programas que usam a nova CRT não rodam no Windows 95. Eu quero dizer: não é um projeto específico; QUALQUER projeto não irá rodar. Tentamos executar um projeto vazio e existe uma dependência da função IsDebuggerPresent nos executáveis gerados. Essa função API inexiste nessa versão do SO.
Obs: solução não funciona na execução. Veja detalhes no final do artigo.
Bem, esse é o tipo de coisa que pára o desenvolvimento. De repente temos um problema que pode comprometer todo o processo de migração. Uma pesquisa inicial no google e google groups não revelou muita coisa, só uma discussão em alemão sem links interessantes. Mas, alguns minutos de fuçação e busca depois e achamos este link (2026-04-02 onde está?), que documenta a tragédia: no Visual C++ 2005, nem o Windows 95 nem o Windows NT 4 são suportados.
O que é muito justo, sabendo que a Microsoft encerrou o suporte técnico destes dois sistemas já faz algum tempo. Mas, cá entre nós, não fazer rodar no 95 só por causa do IsDebuggerPresent parece uma limitação desnecessária. Tudo bem que não é mais suportado (nós mesmos programadores já não suportamos mais esse sistema) e que talvez muito mais coisa não funcione. Mas, diabos, compile e deixe rodar. Afinal, a estabilidade já não é um forte nem do sistema operacional. O hipotético usuário de Windows 95 no século XXI não iria nem esquentar muito se um de seus programas compilados em VC8 eventualmente explodisse de vez em quando. Ele de cara já iria botar a culpa no Windows e apertar o botão de reset. O instinto Microsoft. Lembram?
Mas, voltando ao tema do artigo:
// gs_report.c
__declspec(noreturn) void __cdecl __report_gsfailure(void)
{
/*
...
muito código
..
*/
#if defined (_CRTBLD) && !defined (_SYSCRT)
DebuggerWasPresent = IsDebuggerPresent(); // AQUI!! MALEDETO!!
_CRT_DEBUGGER_HOOK(_CRT_DEBUGGER_GSFAILURE);
#endif /* defined (_CRTBLD) && !defined (_SYSCRT) */
/*
...
mais código
..
*/
}
// enquanto isso, no invarg.c ...
_CRTIMP void __cdecl _invoke_watson(
const wchar_t *pszExpression,
const wchar_t *pszFunction,
const wchar_t *pszFile,
unsigned int nLine,
uintptr_t pReserved
)
{
/*
...
lá lá lá...
..
*/
ExceptionPointers.ExceptionRecord = &ExceptionRecord;
ExceptionPointers.ContextRecord = &ContextRecord;
wasDebuggerPresent = IsDebuggerPresent(); // AH, MUUUULEEEEKEEE!!!
/* Make sure any filter already in place is deleted. */
SetUnhandledExceptionFilter(NULL);
/*
...
mais código
..
*/
}
Essas duas funções de tratamento de erros e exceções presentes nos fontes da biblioteca C é que estão zoando o barraco. Nesse caso, saber onde está o problema não resolve muito. Temos os fontes, podemos alterá-los. Mas isso não significa que podemos usar uma nova CRT modificada e compilada por nós. Isso seria impor uma dependência muito severa na portabilidade do projeto. Ele mal compilaria fora daquela sala!
Como modificar os fontes da Microsoft é uma gambi acima dos níveis a que estávamos dispostos a aceitar naquele momento (era de manhã ainda), resolvemos que as coisas precisariam ficar mais feias para chegarmos a esse ponto. Algumas alternativas sugeridas foram (por ordem de desespero):
Até que veio a solução mágica (tudo bem, não tão mágica; mas boa o suficiente para ser implementada):
//
//solucaonaotaomagicamasboaosuficienteparaserimplementada.cpp
//
#include <windows.h>
BOOL
WINAPI
_imp__IsDebuggerPresent(VOID)
{
BOOL ret = FALSE;
// aqui podemos chamar a função de verdade, mas
// sem usar link estático. e rodar no 95 =)
return ret;
}
Uau! Isso que eu chamo de sobrecarga de função "na marra". Se nossas mães vissem isso (e entendessem isso) iriam ficar orgulhosas.
Vamos entender o que acontece durante o processo de link:
Esse é o fim da história. Com isso conseguimos enganar o linker. Ele encontra nosso símbolo antes da kernel32.lib e usa ele no lugar. E com isso resolvemos mais um dos problemas que ocorre nos processos de migração de versão dos ambientes Microsoft. Que tal?
PS: com essa pequena "armação" foi corrigido o problema de dependência, porém não o de execução. O programa roda quando não chamadas as funções sobrepostas. Aliás, é interessante notar que a CRT do vc referencia a função, mas acaba não chamando em uma compilação default, o que conseqüentemente aumenta o nível de revolta. Estou analisando o problema agora e, caso ache uma solução simples e plausível, colocarei em um futuro artigo. No momento peço desculpas pela publicação precipitada de uma solução não testada.
2006-05-27 Anderson Moreira:
Sensacional :D
2006-05-28 Thiago Adams:
Na opção "Runtime Library" vocês estavão usando "Multi-threaded (/MT)" ou "Multi-threaded DLL (/MD)"?
Não sei se faz diferença.. mas gostaria de saber se usando "Multi-threaded (/MT)" também não funciona no 95?
2006-05-29 Caloni:
Sim, Thiago. Em ambas as configurações não roda no 95 por conta da função IsDebuggerPresent. Se você usar a gamb... adaptação técnica que mostrei acima, ele roda, mas de qualquer forma a Microsoft não suporta.
[]s