it-swarm-pt.tech

O que acontece com variáveis ​​globais declaradas em uma DLL?

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

38
Dima

Em uma DLL do Windows C++, todos os objetos globais (incluindo membros estáticos das classes) serão construídos imediatamente 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 que objetos não const globais são ruins (mas você já sabe disso, então evitarei mencionar multithreading, bloqueios, objetos divinos etc.)

1 - A ordem de construção de objetos ou unidades de compilação diferentes (ou seja, arquivos CPP) não é garantida, portanto, 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. Isso é 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 - Há coisas que são proibidas de fazer no DllMain. Essas coisas provavelmente também são proibidas nos construtores. Portanto, evite trancar alguma coisa. 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

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

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

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

37
paercebal

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

6
Mark Ransom

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

Na inspeção, os destruidores serão chamados via _cexit() quando a contagem de referência interna mantida pela DLL CRT atingir zero.

4
MSN

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

Além disso, tenha cuidado com destruidores não triviais, pois existem problemas de tempo e de pedido. Seu DLL pode ser descarregado depois a DLL) seu destruidor depende, o que obviamente causaria problemas.

3
Philip Rieck

Quando DllMain com o parâmetro fdwReason = DLL_PROCESS_DETACH é chamado, significa que o DLL é descarregado pelo aplicativo. Este é o tempo antes do destruidor de objetos globais/estáticos ser chamado.

1
INS

No Windows, os arquivos de imagem binária com extensão * .exe, * .dll estão em formato PE Esses arquivos têm ponto de entrada. Você pode visualizá-lo com a ferramenta dumpbin como

dumpbin/headers dllname.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 DllMain, respectivamente.

Se você usar o microsofts VC, poderá assistir ao código fonte dessas funções no seu diretório VC:

  • crt0.c
  • dllcrt0.c

O processo DllMainCRTStartup precisa iniciar/desabilitar 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:

  • principal ou WinMain do thread de inicialização do programa retorna o fluxo de controle
  • você chama explicitamente o FreeLibrary e o contador-dll é zero
1
bruziuz