Ponto de entrada DllMain

Um ponto de entrada opcional em uma DLL (biblioteca de vínculo dinâmico). Quando o sistema inicia ou encerra um processo ou thread, ele chama a função de ponto de entrada para cada DLL carregada usando o primeiro thread do processo. O sistema também chama a função de ponto de entrada para uma DLL quando ela é carregada ou descarregada usando as funções LoadLibrary e FreeLibrary .

Exemplo

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 Função Entry-Point biblioteca de link dinâmico.

Aviso

Há limites significativos sobre o que você pode fazer com segurança em um ponto de entrada de DLL. Consulte as Práticas Recomendadas Gerais para APIs específicas do Windows que não são seguras de chamar 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. Você pode exigir que os aplicativos chamem a função de inicialização após a execução do DllMain e antes que eles chamem outras funções na DLL.

Sintaxe

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

Parâmetros

hinstDLL [in]

Um identificador para o módulo DLL. O valor é o endereço base da DLL. O HINSTANCE de uma DLL é o mesmo que o HMODULE da DLL, portanto , hinstDLL pode ser usado em chamadas para funções que exigem um identificador de módulo.

fdwReason [in]

O código de motivo que indica por que a função de ponto de entrada da DLL está sendo chamada. Esse parâmetro pode usar um dos valores a seguir.

Valor Significado
DLL_PROCESS_ATTACH
1
A DLL está sendo carregada no espaço de endereço virtual do processo atual como resultado da inicialização do processo ou como resultado de uma chamada para LoadLibrary. As DLLs podem usar essa oportunidade para inicializar qualquer dado de instância ou usar a função TlsAlloc para alocar um índice TLS (armazenamento local de thread).
O parâmetro lpvReserved indica se a DLL está sendo carregada estaticamente ou dinamicamente.
DLL_PROCESS_DETACH
0
A DLL está sendo descarregada do espaço de endereço virtual do processo de chamada porque foi carregada sem sucesso ou a contagem de referência atingiu zero (os processos foram encerrados ou chamados de FreeLibrary uma vez para cada vez que ele chamou LoadLibrary).
O parâmetro lpvReserved indica se a DLL está sendo descarregada como resultado de uma chamada FreeLibrary , uma falha ao carregar ou o encerramento do processo.
A DLL pode usar essa oportunidade para chamar a função TlsFree para liberar todos os índices TLS alocados usando TlsAlloc e liberar quaisquer dados locais de thread.
Observe que o thread que recebe a notificação DLL_PROCESS_DETACH não é necessariamente o mesmo thread que recebeu a notificação de DLL_PROCESS_ATTACH .
DLL_THREAD_ATTACH
2
O processo atual está criando um novo thread. Quando isso ocorre, o sistema chama a função de ponto de entrada de todas as DLLs atualmente anexadas ao processo. A chamada é feita no contexto do novo thread. As DLLs podem usar essa 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.
Observe que a função de ponto de entrada de uma DLL é chamada com esse valor somente por threads criados depois que a DLL é carregada pelo processo. Quando uma DLL é carregada usando LoadLibrary, os threads existentes não chamam a função de ponto de entrada da DLL recém-carregada.
DLL_THREAD_DETACH
3
Um thread está saindo de forma limpa. Se a DLL tiver armazenado um ponteiro para a memória alocada em um slot TLS, ela deverá usar essa oportunidade para liberar a memória. O sistema chama a função de ponto de entrada de todas as DLLs carregadas no momento com esse valor. A chamada é feita no contexto do thread de saída.

lpvReserved [in]

Se fdwReason for DLL_PROCESS_ATTACH, lpvReserved será NULL para cargas dinâmicas e não NULL para cargas estáticas.

Se fdwReason for DLL_PROCESS_DETACH, lpvReserved será NULL se FreeLibrary tiver sido chamado ou a carga DLL falhar e não FOR NULL se o processo estiver sendo encerrado.

Valor retornado

Quando o sistema chama a função DllMain com o valor DLL_PROCESS_ATTACH , a função retorna TRUE se for bem-sucedida ou FALSE se a inicialização falhar. Se o valor retornado for FALSE quando DllMain for chamado porque o processo usa a função LoadLibrary , LoadLibrary retornará NULL. (O sistema chama imediatamente sua função de ponto de entrada com DLL_PROCESS_DETACH e descarrega a DLL.) Se o valor retornado for FALSE quando DllMain for chamado durante a inicialização do processo, o processo terminará com um erro. Para obter informações de erro estendidas, chame GetLastError.

Quando o sistema chama a função DllMain com qualquer valor diferente de DLL_PROCESS_ATTACH, o valor retornado é ignorado.

Comentários

DllMain é um espaço reservado para o nome da função definida pela biblioteca. Você deve especificar o nome real usado ao compilar sua DLL. Para obter mais informações, consulte a documentação incluída com suas ferramentas de desenvolvimento.

Durante a inicialização inicial do processo ou após uma chamada ao LoadLibrary, o sistema examina a lista de DLLs carregadas para o processo. Para cada DLL que ainda não foi chamada com o valor DLL_PROCESS_ATTACH , o sistema chama a função de ponto de entrada da DLL. Essa chamada é feita no contexto do thread que causou a alteração do espaço de endereço do processo, como o thread primário do processo ou o thread que chamou LoadLibrary. O acesso ao ponto de entrada é serializado pelo sistema em todo o processo. Os threads no DllMain mantêm o bloqueio do carregador para que nenhuma DLL adicional possa ser carregada ou inicializada dinamicamente.

Se a função de ponto de entrada da DLL retornar FALSE após uma notificação DLL_PROCESS_ATTACH , ela receberá uma notificação de DLL_PROCESS_DETACH e a DLL será descarregada imediatamente. No entanto, se o código DLL_PROCESS_ATTACH gerar uma exceção, a função de ponto de entrada não receberá a notificação de DLL_PROCESS_DETACH .

Há casos em que a função de ponto de entrada é chamada para um thread de terminação, mesmo que a função de ponto de entrada nunca tenha sido chamada com DLL_THREAD_ATTACH para o thread:

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

Quando uma DLL é descarregada de um processo como resultado de uma carga malsucedida da DLL, encerramento do processo ou uma chamada para FreeLibrary, o sistema não chama a função de ponto de entrada da DLL com o valor de DLL_THREAD_DETACH para os threads individuais do processo. A DLL é enviada apenas uma notificação de DLL_PROCESS_DETACH . As DLLs podem aproveitar essa oportunidade para limpar todos os recursos de todos os threads conhecidos pela DLL.

Ao lidar com DLL_PROCESS_DETACH, uma DLL deve liberar recursos, como memória de heap, somente se a DLL estiver sendo descarregada dinamicamente (o parâmetro lpvReserved é NULL). Se o processo estiver sendo encerrado (o parâmetro lpvReserved não é NULL), todos os threads no processo, exceto o thread atual, já foram encerrados ou foram explicitamente encerrados por uma chamada para a função ExitProcess , o que pode deixar alguns recursos de processo, como heaps, em um estado inconsistente. Nesse caso, não é seguro para a DLL limpar os recursos. Em vez disso, a DLL deve permitir que o sistema operacional recupere a memória.

Se você encerrar um processo chamando TerminateProcess ou TerminateJobObject, as DLLs desse processo não receberão DLL_PROCESS_DETACH notificações. Se você terminar um thread chamando TerminateThread, as DLLs desse thread não receberão DLL_THREAD_DETACH notificações.

A função de ponto de entrada deve executar apenas tarefas simples de inicialização ou encerramento. Ele não deve chamar a função LoadLibrary ou LoadLibraryEx (ou uma função que chama essas funções), pois isso pode criar loops de dependência na ordem de carga DLL. Isso pode resultar em uma DLL sendo usada antes que o sistema execute seu código de inicialização. Da mesma forma, a função de ponto de entrada não deve chamar a função FreeLibrary (ou uma função que chama FreeLibrary) durante o encerramento do processo, pois isso pode resultar em uma DLL sendo usada após o sistema executar seu código de encerramento.

Como Kernel32.dll tem a garantia de ser carregada no espaço de endereço do processo quando a função de ponto de entrada é chamada, chamar funções em Kernel32.dll não resulta na DLL sendo usada antes de seu código de inicialização ser 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 seções críticas e mutexes, e usar TLS. Infelizmente, não há uma lista abrangente de funções seguras em Kernel32.dll.

Chamar funções que exigem DLLs diferentes de Kernel32.dll pode resultar em problemas difíceis de diagnosticar. Por exemplo, chamar funções de Usuário, Shell e COM pode causar erros de violação de acesso, pois algumas funções carregam outros componentes do sistema. Por outro lado, chamar funções como essas durante o encerramento 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 de DLL são serializadas, as funções de ponto de entrada não devem tentar se comunicar com outros threads ou processos. Deadlocks podem ocorrer como resultado.

Para obter informações sobre práticas recomendadas ao escrever uma DLL, consulte https://docs.microsoft.com/windows/win32/dlls/dynamic-link-library-best-practices.

Se a DLL estiver vinculada à CRT (biblioteca de tempo de execução) C, o ponto de entrada fornecido pelo CRT chamará os construtores e destruidores para objetos C++ globais e estáticos. Portanto, essas restrições para 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 sua DLL esteja vinculada à CRT (biblioteca de tempo de execução C) estática.

Requisitos

Requisito Valor
Cliente mínimo com suporte
Windows XP [somente aplicativos da área de trabalho]
Servidor mínimo com suporte
Windows Server 2003 [somente aplicativos da área de trabalho]
Cabeçalho
Process.h

Confira também

Função Entry-Point biblioteca de link dinâmico

Funções de biblioteca de link dinâmico

FreeLibrary

GetModuleFileName

LoadLibrary

TlsAlloc

TlsFree

DisableThreadLibraryCalls