Pergunta

Digamos que eu escreva uma DLL em C++ e declare um objeto global de uma classe com um destruidor não trivial.O destruidor será chamado quando a DLL for descarregada?

Foi útil?

Solução

Em uma DLL do Windows C++, todos os objetos globais (incluindo membros estáticos de classes) serão construídos logo antes da chamada do DllMain com DLL_PROCESS_ATTACH e serão destruídos logo após a chamada do DllMain com DLL_PROCESS_DETACH.

Agora, você deve considerar três problemas:

0 - Claro, objetos globais não-const são maus (mas você já sabe disso, então evitarei mencionar multithreading, locks, god-objects, etc.)

1 - A ordem de construção dos objetos ou diferentes unidades de compilação (ou seja,CPP) não é garantido, então você não pode esperar que o objeto A seja construído antes de B se os dois objetos forem instanciados em dois CPPs diferentes.Isto é importante se B depende de A.A solução é mover todos os objetos globais no mesmo arquivo CPP, pois dentro da mesma unidade de compilação, a ordem de instanciação dos objetos será a ordem de construção (e o inverso da ordem de destruição)

2 - Tem coisas que é proibido fazer no DllMain.Essas coisas provavelmente também são proibidas nos construtores.Portanto, evite bloquear algo.Veja o excelente blog de Raymond Chen sobre o assunto:

http://blogs.msdn.com/oldnewthing/archive/2004/01/27/63401.aspx

http://blogs.msdn.com/oldnewthing/archive/2004/01/28/63880.aspx

Neste caso, a inicialização lenta pode ser interessante:As classes permanecem em um estado "não inicializado" (os ponteiros internos são NULL, os booleanos são falsos, o que for) até que você chame um de seus métodos, momento em que eles serão inicializados.Se você usar esses objetos dentro do main (ou de uma das funções descendentes do main), você ficará bem porque eles serão chamados após a execução de DllMain.

3 - Claro, se alguns objetos globais na DLL A dependem de objetos globais na DLL B, você deve ter muito cuidado com a ordem de carregamento da DLL e, portanto, com as dependências.Nesse caso, DLLs com dependências circulares diretas ou indiretas causarão uma quantidade absurda de dores de cabeça.A melhor solução é quebrar as dependências circulares.

P.S.:Observe que em C++, o construtor pode lançar, e você não deseja uma exceção no meio do carregamento de uma DLL, portanto, certifique-se de que seus objetos globais não usarão exceção sem um motivo muito bom.Como destruidores escritos corretamente não estão autorizados a lançar, o descarregamento da DLL deve estar ok neste caso.

Outras dicas

Esta página da Microsoft detalha a inicialização de DLL e destruição de globais:
http://msdn.microsoft.com/en-us/library/988ye33t.aspx

Se você quiser ver o código real que é executado ao vincular um .dll, dê uma olhada em %ProgramFiles%\Visual Studio 8\vc\crt\src\dllcrt0.c.

A partir da inspeção, os destruidores serão chamados via _cexit() quando a contagem de referência interna mantida pela dll CRT atinge zero.

Ele deve ser chamado quando o aplicativo terminar ou a DLL for descarregada, o que ocorrer primeiro.Observe que isso depende um pouco do tempo de execução real em que você está compilando.

Além disso, tome cuidado com destruidores não triviais, pois há problemas de tempo e ordem.Sua DLL pode ser descarregada depois uma DLL na qual seu destruidor depende, o que obviamente causaria problemas.

Quando DllMain com o parâmetro fdwReason = DLL_PROCESS_DETACH é chamado, significa que a DLL foi descarregada pela aplicação.Este é o tempo antes que o destruidor de objetos globais/estáticos seja chamado.

Nos arquivos de imagem binária do Windows com extensão *.exe, *.dll estão em Formato PEEsses arquivos possuem Ponto de Entrada.Você pode visualizá-lo com a ferramenta dumpbin como

dumpbin /cabeçalhos nomedll.dll

Se você usar o tempo de execução C da Microsoft, seu ponto de entrada será algo como *Crtstartup ou *dllMaincrtstartup

Essas funções executam a inicialização do tempo de execução c e c++ e delegam a execução para (main, WinMain) ou para DllMain respectivamente.

Se você usar o compilador VC da Microsoft, poderá observar o código-fonte dessas funções em seu diretório VC:

  • crt0.c
  • dllcrt0.c

DllMainCRTStartup processa todas as coisas necessárias para iniciar/desiniciar suas variáveis ​​​​globais das seções .data no cenário normal, quando ele recupera a notificação DLL_PROCESS_DETACH durante o descarregamento da dll.Por exemplo:

  • main ou WinMain do thread de inicialização do programa retorna o fluxo de controle
  • você chama explicitamente FreeLibrary e use-dll-counter é zero
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top