Ponto de entrada do DllMain

Um ponto de entrada opcional numa biblioteca de ligação dinâmica (DLL). Quando o sistema inicia ou termina um processo ou thread, chama a função de ponto de entrada para cada DLL carregado usando o primeiro thread do processo. O sistema também chama a função de ponto de entrada para uma DLL quando esta é carregada ou descarregada, utilizando as funções LoadLibrary e FreeLibrary .

Warning

Há limites significativos sobre o que você pode fazer com segurança em um ponto de entrada DLL. Consulte as Boas Práticas Gerais para APIs específicas do Windows que não são seguras para usar no DllMain. Se você precisar de algo além da inicialização mais simples, faça isso em uma função de inicialização para a DLL. Podes exigir que as aplicações chamem a função de inicialização depois de o DllMain ter sido executado e antes de chamarem quaisquer outras funções na DLL.

Exemplos:

BOOL WINAPI DllMain(
    HINSTANCE hinstDLL,  // handle to DLL module
    DWORD fdwReason,     // reason for calling function
    LPVOID lpvReserved )  // reserved
{
    // Perform actions based on the reason for calling.
    switch( fdwReason ) 
    { 
        case DLL_PROCESS_ATTACH:
         // Initialize once for each new process.
         // Return FALSE to fail DLL load.
            break;

        case DLL_THREAD_ATTACH:
         // Do thread-specific initialization.
            break;

        case DLL_THREAD_DETACH:
         // Do thread-specific cleanup.
            break;

        case DLL_PROCESS_DETACH:
        
            if (lpvReserved != nullptr)
            {
                break; // do not do cleanup if process termination scenario
            }
            
         // Perform any necessary cleanup.
            break;
    }
    return TRUE;  // Successful DLL_PROCESS_ATTACH.
}

Este é um exemplo da Dynamic-Link Biblioteca Entry-Point Função.

Syntax

BOOL WINAPI DllMain(
  _In_ HINSTANCE hinstDLL,
  _In_ DWORD     fdwReason,
  _In_ LPVOID    lpvReserved
);

Parameters

hinstDLL [em]

Um handle para o módulo DLL. O valor é o endereço base da DLL. O HINST de uma DLL é o mesmo que o HMODULE da DLL, pelo que o hinstDLL pode ser usado em chamadas a funções que requerem um handle de módulo.

fdwRazão [em]

O código de razão que indica porque é que a função de entrada DLL está a ser chamada. Este parâmetro pode ser um dos seguintes valores.

Valor Meaning
DLL_PROCESS_ATTACH
1
A DLL está a ser carregada no espaço de endereçamento virtual do processo atual como resultado do arranque do processo ou como resultado de uma chamada à LoadLibrary. As DLLs podem aproveitar esta oportunidade para inicializar quaisquer dados de instância ou para usar a função TlsAlloc para alocar um índice de armazenamento local de thread (TLS).
O parâmetro lpvReserved indica se a DLL está a ser carregada de forma estática ou dinâmica.
DLL_PROCESS_DETACH
0
A DLL está a ser descarregada do espaço de endereçamento virtual do processo que chama porque foi carregada sem sucesso ou porque a contagem de referências chegou a zero (o processo terminou ou chamou FreeLibrary uma vez por cada vez que chamou LoadLibrary).
O parâmetro lpvReserved indica se a DLL está a ser descarregada como resultado de uma chamada FreeLibrary , falha no carregamento ou terminação do processo.
A DLL pode aproveitar esta oportunidade para chamar a função TlsFree e libertar quaisquer índices TLS atribuídos através do TlsAlloc e para libertar quaisquer dados locais do thread.
Note que o tópico que recebe a notificação DLL_PROCESS_DETACH não é necessariamente o mesmo que recebeu a notificação DLL_PROCESS_ATTACH .
DLL_THREAD_ATTACH
2
O processo atual é criar um novo tópico. Quando isto acontece, o sistema chama a função de ponto de entrada de todas as DLLs atualmente ligadas ao processo. A decisão é feita no contexto do novo tópico. As DLLs podem aproveitar esta oportunidade para inicializar um slot TLS para o thread. Um thread que chama a função de ponto de entrada DLL com DLL_PROCESS_ATTACH não chama a função de ponto de entrada DLL com DLL_THREAD_ATTACH.
Note que a função de ponto de entrada de uma DLL é chamada com este valor apenas por threads criados após a DLL ser carregada pelo processo. Quando uma DLL é carregada usando a LoadLibrary, as threads existentes não chamam a função de ponto de entrada da DLL recém-carregada.
DLL_THREAD_DETACH
3
Um fio está a sair limpo. Se a DLL armazenou um ponteiro para a memória alocada num slot TLS, deve aproveitar esta oportunidade para libertar a memória. O sistema chama a função de ponto de entrada de todas as DLLs atualmente carregadas com este valor. A decisão é feita no contexto do tópico que se encerra.

lpvReservado [em]

Se fdwReason for DLL_PROCESS_ATTACH, lpvReserved é NULL para cargas dinâmicas e non-NULL para cargas estáticas.

Se fdwReason for DLL_PROCESS_DETACH, lpvReserved é NULL se FreeLibrary tiver sido chamado ou o carregamento DLL falhou e non-NULL se o processo estiver a terminar.

Valor de retorno

Quando o sistema chama a função DllMain com o valor DLL_PROCESS_ATTACH , a função retorna TRUE se tiver sucesso ou FALSE se a inicialização falhar. Se o valor de retorno for FALSE quando o DllMain for chamado porque o processo usa a função LoadLibrary , o LoadLibrary devolve NULL. (O sistema chama imediatamente a sua função de ponto de entrada com DLL_PROCESS_DETACH e descarrega a DLL.) Se o valor de retorno for FALSE quando o DllMain é chamado durante a inicialização do processo, o processo termina com um erro. Para obter informações detalhadas sobre erros, ligue para o GetLastError.

Quando o sistema chama a função DllMain com qualquer valor que não seja DLL_PROCESS_ATTACH, o valor de retorno é ignorado.

Remarks

DllMain é um marcador de posição para o nome da função definida pela biblioteca. Tens de especificar o nome real que usas quando constróis a tua DLL. Para mais informações, consulte a documentação incluída com as suas ferramentas de desenvolvimento.

Durante o arranque inicial do processo ou após uma chamada para a LoadLibrary, o sistema analisa a lista de DLLs carregadas do processo. Para cada DLL que ainda não tenha sido chamada com o valor DLL_PROCESS_ATTACH , o sistema chama a função de entrada da DLL. Esta chamada é feita no contexto da thread que causou a alteração do espaço de endereçamento do processo, como a thread principal do processo ou a thread que chamou LoadLibrary. O acesso ao ponto de entrada é serializado pelo sistema a nível de processo. As threads no DllMain mantêm o bloqueio do loader para que nenhuma DLL adicional possa ser carregada ou inicializada dinamicamente.

Se a função de entrada da DLL devolver FALSE após uma notificação de DLL_PROCESS_ATTACH , recebe uma notificação DLL_PROCESS_DETACH e a DLL é imediatamente descarregada. No entanto, se o código DLL_PROCESS_ATTACH lançar uma exceção, a função de entrada não receberá a notificação DLL_PROCESS_DETACH .

Existem casos em que a função de ponto de entrada é chamada para uma thread terminante mesmo que a função de ponto de entrada nunca tenha sido chamada com DLL_THREAD_ATTACH para a thread:

  • A thread era a thread inicial no processo, por isso o sistema chamava a função de ponto de entrada com o valor DLL_PROCESS_ATTACH .
  • O thread já estava a correr quando foi feita uma chamada para a função LoadLibrary , por isso o sistema nunca chamou a função de ponto de entrada para ela.

Quando uma DLL é descarregada de um processo como resultado de um carregamento não bem-sucedido da DLL, terminação do processo ou chamada para FreeLibrary, o sistema não chama a função de entrada da DLL com o valor DLL_THREAD_DETACH para os threads individuais do processo. O DLL recebe apenas uma notificação DLL_PROCESS_DETACH . As DLLs podem aproveitar esta oportunidade para limpar todos os recursos de todos os threads conhecidos pela DLL.

Ao lidar com DLL_PROCESS_DETACH, uma DLL deve libertar recursos como memória heap apenas se a DLL estiver a ser descarregada dinamicamente (o parâmetro lpvReserved é NULL). Se o processo estiver a terminar (o parâmetro lpvReserved não é NULL), todas as threads do processo, exceto a thread atual, já saíram ou foram explicitamente terminadas por uma chamada à função ExitProcess , o que pode deixar alguns recursos do processo, como heaps, num estado inconsistente. Neste caso, não é seguro para a DLL limpar os recursos. Em vez disso, a DLL deve permitir que o sistema operativo recupere a memória.

Se terminar um processo chamando TerminateProcess ou TerminateJobObject, as DLLs desse processo não recebem notificações DLL_PROCESS_DETACH . Se terminar um thread ao chamar o TerminateThread, as DLLs desse thread não recebem notificações DLL_THREAD_DETACH .

A função de ponto de entrada deve realizar apenas tarefas simples de inicialização ou terminação. Não deve chamar a função LoadLibrary ou LoadLibraryEx (nem uma função que chame estas funções), pois isso pode criar ciclos de dependência na ordem de carregamento DLL. Isto pode resultar na utilização de uma DLL antes de o sistema executar o seu código de inicialização. De forma semelhante, a função de ponto de entrada não deve chamar a função FreeLibrary (ou uma função que chame FreeLibrary) durante a terminação do processo, pois isso pode resultar na utilização de uma DLL após o sistema ter executado o seu código de terminação.

Como Kernel32.dll é garantido que será carregado no espaço de endereçamento do processo quando a função de entrada é chamada, chamar funções em Kernel32.dll não resulta na utilização da DLL antes de o seu código de inicialização ter sido executado. Portanto, a função de ponto de entrada pode chamar funções em Kernel32.dll que não carregam outras DLLs. Por exemplo, o DllMain pode criar objetos de sincronização como secções críticas e mutexes, e usar TLS. Infelizmente, não existe uma lista abrangente de funções seguras em Kernel32.dll.

Chamar funções que requerem DLLs diferentes de Kernel32.dll pode resultar em problemas difíceis de diagnosticar. Por exemplo, chamar funções de Utilizador, Shell e COM pode causar erros de violação de acesso, porque algumas funções carregam outros componentes do sistema. Por outro lado, chamar funções como estas durante a terminação pode causar erros de violação de acesso porque o componente correspondente pode já ter sido descarregado ou não inicializado.

Como as notificações DLL são serializadas, as funções de ponto de entrada não devem tentar comunicar com outros threads ou processos. Podem ocorrer impasses como consequência.

Para informações sobre as melhores práticas ao escrever uma DLL, consulte as melhores práticas de bibliotecas de ligação dinâmica.

Se a sua DLL estiver ligada à biblioteca de tempo de execução C (CRT), o ponto de entrada fornecido pelo CRT chama os construtores e destruidores para objetos C++ globais e estáticos. Portanto, estas restrições para o DllMain também se aplicam a construtores e destruidores e a qualquer código que seja chamado deles.

Considere chamar DisableThreadLibraryCalls ao receber DLL_PROCESS_ATTACH, a menos que a sua DLL esteja ligada a uma biblioteca estática de tempo de execução C (CRT).

Requisitos

Requisito Valor
Cliente mínimo suportado
Windows XP [apenas aplicações de ambiente de trabalho]
Servidor mínimo suportado
Windows Server 2003 [apenas aplicações de ambiente de trabalho]
Cabeçalho
Process.h

Ver também

Dynamic-Link Função Entry-Point Biblioteca

Dynamic-Link Funções da Biblioteca

FreeLibrary

GetModuleFileName

LoadLibrary

TlsAlloc

TlsFree

DisableThreadLibraryCalls