Punto de entrada de DllMain
Un punto de entrada opcional en una biblioteca de vínculos dinámicos (DLL). Cuando el sistema inicia o finaliza un proceso o subproceso, llama a la función de punto de entrada para cada DLL cargada mediante el primer subproceso del proceso. El sistema también llama a la función de punto de entrada para un archivo DLL cuando se carga o descarga mediante las funciones LoadLibrary y FreeLibrary .
Ejemplo
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 es un ejemplo de la biblioteca de vínculos dinámicos Entry-Point Function.
Advertencia
Hay límites importantes sobre lo que se puede hacer de forma segura en un punto de entrada del archivo DLL. Consulte Procedimientos recomendados generales para las API específicas de Windows que no son seguras para llamar a en DllMain. Si necesita algo más que la inicialización más sencilla, puede hacerlo en una función de inicialización para el archivo DLL. Puede requerir que las aplicaciones llamen a la función de inicialización después de que DllMain se haya ejecutado y antes de llamar a cualquier otra función del archivo DLL.
Sintaxis
BOOL WINAPI DllMain(
_In_ HINSTANCE hinstDLL,
_In_ DWORD fdwReason,
_In_ LPVOID lpvReserved
);
Parámetros
-
hinstDLL [in]
-
Identificador del módulo DLL. El valor es la dirección base del archivo DLL. El HINSTANCE de un archivo DLL es el mismo que el HMODULE del archivo DLL, por lo que hinstDLL se puede usar en llamadas a funciones que requieren un identificador de módulo.
-
fdwReason [in]
-
El código de motivo que indica por qué se llama a la función de punto de entrada dll. Este parámetro puede ser uno de los valores siguientes.
Valor Significado - DLL_PROCESS_ATTACH
- 1
El archivo DLL se carga en el espacio de direcciones virtuales del proceso actual como resultado del inicio del proceso o como resultado de una llamada a LoadLibrary. Los archivos DLL pueden usar esta oportunidad para inicializar los datos de instancia o usar la función TlsAlloc para asignar un índice de almacenamiento local (TLS) de subprocesos.
El parámetro lpvReserved indica si el archivo DLL se carga estática o dinámicamente.- DLL_PROCESS_DETACH
- 0
El archivo DLL se está descargando desde el espacio de direcciones virtuales del proceso de llamada porque se cargó sin éxito o el recuento de referencias ha alcanzado cero (los procesos han finalizado o llamado FreeLibrary una vez por cada vez que se llama LoadLibrary).
El parámetro lpvReserved indica si el archivo DLL se está descargando como resultado de una llamada FreeLibrary , un error de carga o finalización del proceso.
El archivo DLL puede usar esta oportunidad para llamar a la función TlsFree para liberar los índices TLS asignados mediante TlsAlloc y liberar cualquier dato local del subproceso.
Tenga en cuenta que el subproceso que recibe la notificación de DLL_PROCESS_DETACH no es necesariamente el mismo subproceso que recibió la notificación DLL_PROCESS_ATTACH .- DLL_THREAD_ATTACH
- 2
El proceso actual está creando un nuevo subproceso. Cuando esto ocurre, el sistema llama a la función de punto de entrada de todos los archivos DLL asociados actualmente al proceso. La llamada se realiza en el contexto del nuevo subproceso. Los archivos DLL pueden usar esta oportunidad para inicializar una ranura TLS para el subproceso. Un subproceso que llama a la función de punto de entrada dll con DLL_PROCESS_ATTACH no llama a la función de punto de entrada dll con DLL_THREAD_ATTACH.
Tenga en cuenta que los subprocesos creados después de cargar el archivo DLL solo llaman a la función de punto de entrada de un archivo DLL con este valor. Cuando se carga un archivo DLL mediante LoadLibrary, los subprocesos existentes no llaman a la función de punto de entrada del archivo DLL recién cargado.- DLL_THREAD_DETACH
- 3
Un subproceso sale limpiamente. Si el archivo DLL ha almacenado un puntero a la memoria asignada en una ranura TLS, debe usar esta oportunidad para liberar la memoria. El sistema llama a la función de punto de entrada de todos los archivos DLL cargados actualmente con este valor. La llamada se realiza en el contexto del subproceso de salida. -
lpvReserved [in]
-
Si fdwReason es DLL_PROCESS_ATTACH, lpvReserved es NULL para cargas dinámicas y no NULL para cargas estáticas.
Si fdwReason es DLL_PROCESS_DETACH, lpvReserved es NULL si se ha llamado a FreeLibrary o se ha producido un error en la carga de DLL y no NULL si el proceso finaliza.
Valor devuelto
Cuando el sistema llama a la función DllMain con el valor de DLL_PROCESS_ATTACH , la función devuelve TRUE si se ejecuta correctamente o FALSE si se produce un error en la inicialización. Si el valor devuelto es FALSE cuando se llama a DllMain porque el proceso usa la función LoadLibrary , LoadLibrary devuelve NULL. (El sistema llama inmediatamente a la función de punto de entrada con DLL_PROCESS_DETACH y descarga el archivo DLL). Si el valor devuelto es FALSE cuando se llama a DllMain durante la inicialización del proceso, el proceso finaliza con un error. Para obtener información de error extendida, llame a GetLastError.
Cuando el sistema llama a la función DllMain con cualquier valor distinto de DLL_PROCESS_ATTACH, se omite el valor devuelto.
Comentarios
DllMain es un marcador de posición para el nombre de función definido por la biblioteca. Debe especificar el nombre real que use al compilar el archivo DLL. Para obtener más información, consulte la documentación incluida con las herramientas de desarrollo.
Durante el inicio del proceso inicial o después de una llamada a LoadLibrary, el sistema examina la lista de archivos DLL cargados para el proceso. Para cada DLL que aún no se ha llamado con el valor de DLL_PROCESS_ATTACH , el sistema llama a la función de punto de entrada del archivo DLL. Esta llamada se realiza en el contexto del subproceso que hizo que el espacio de direcciones del proceso cambiara, como el subproceso principal del proceso o el subproceso que llamó a LoadLibrary. El sistema serializa el acceso al punto de entrada en todo el proceso. Los subprocesos de DllMain contienen el bloqueo del cargador, por lo que no se pueden cargar ni inicializar archivos DLL adicionales de forma dinámica.
Si la función de punto de entrada del archivo DLL devuelve FALSE después de una notificación de DLL_PROCESS_ATTACH , recibe una notificación de DLL_PROCESS_DETACH y el archivo DLL se descarga inmediatamente. Sin embargo, si el código de DLL_PROCESS_ATTACH produce una excepción, la función de punto de entrada no recibirá la notificación DLL_PROCESS_DETACH .
Hay casos en los que se llama a la función de punto de entrada para un subproceso de terminación aunque nunca se haya llamado a la función de punto de entrada con DLL_THREAD_ATTACH para el subproceso:
- El subproceso era el subproceso inicial en el proceso, por lo que el sistema llamó a la función de punto de entrada con el valor DLL_PROCESS_ATTACH .
- El subproceso ya se estaba ejecutando cuando se realizó una llamada a la función LoadLibrary , por lo que el sistema nunca llamó a la función de punto de entrada.
Cuando un archivo DLL se descarga de un proceso como resultado de una carga incorrecta del archivo DLL, la finalización del proceso o una llamada a FreeLibrary, el sistema no llama a la función de punto de entrada del archivo DLL con el valor de DLL_THREAD_DETACH para los subprocesos individuales del proceso. El archivo DLL solo se envía una notificación de DLL_PROCESS_DETACH . Los archivos DLL pueden aprovechar esta oportunidad para limpiar todos los recursos de todos los subprocesos conocidos para el archivo DLL.
Al controlar DLL_PROCESS_DETACH, un archivo DLL debe liberar recursos como la memoria del montón solo si el archivo DLL se descarga dinámicamente (el parámetro lpvReserved es NULL). Si el proceso finaliza (el parámetro lpvReserved no es NULL), todos los subprocesos del proceso excepto el subproceso actual ya se han cerrado o han finalizado explícitamente mediante una llamada a la función ExitProcess , lo que podría dejar algunos recursos de proceso como montones en un estado incoherente. En este caso, no es seguro que el archivo DLL limpie los recursos. En su lugar, el archivo DLL debe permitir que el sistema operativo recupere la memoria.
Si finaliza un proceso llamando a TerminateProcess o TerminateJobObject, los archivos DLL de ese proceso no reciben DLL_PROCESS_DETACH notificaciones. Si finaliza un subproceso llamando a TerminateThread, los archivos DLL de ese subproceso no reciben DLL_THREAD_DETACH notificaciones.
La función de punto de entrada solo debe realizar tareas simples de inicialización o finalización. No debe llamar a la función LoadLibrary o LoadLibraryEx (o una función que llama a estas funciones), ya que esto puede crear bucles de dependencia en el orden de carga de DLL. Esto puede dar lugar a que se use un archivo DLL antes de que el sistema haya ejecutado su código de inicialización. Del mismo modo, la función de punto de entrada no debe llamar a la función FreeLibrary (o una función que llama a FreeLibrary) durante la finalización del proceso, ya que esto puede dar lugar a que se use un archivo DLL después de que el sistema haya ejecutado su código de terminación.
Dado que se garantiza que Kernel32.dll se carguen en el espacio de direcciones del proceso cuando se llama a la función de punto de entrada, llamar a funciones en Kernel32.dll no da lugar a que se use el archivo DLL antes de que se haya ejecutado su código de inicialización. Por lo tanto, la función de punto de entrada puede llamar a funciones en Kernel32.dll que no cargan otros archivos DLL. Por ejemplo, DllMain puede crear objetos de sincronización , como secciones críticas y exclusiones mutuas, y usar TLS. Desafortunadamente, no hay una lista completa de funciones seguras en Kernel32.dll.
Las funciones de llamada que requieren archivos DLL distintos de Kernel32.dll pueden dar lugar a problemas difíciles de diagnosticar. Por ejemplo, llamar a funciones User, Shell y COM puede provocar errores de infracción de acceso, ya que algunas funciones cargan otros componentes del sistema. Por el contrario, llamar a funciones como estas durante la finalización puede provocar errores de infracción de acceso porque es posible que el componente correspondiente ya se haya descargado o no inicializado.
Dado que las notificaciones DLL se serializan, las funciones de punto de entrada no deben intentar comunicarse con otros subprocesos o procesos. Los interbloqueos pueden producirse como resultado.
Para obtener información sobre los procedimientos recomendados al escribir un archivo DLL, consulte Procedimientos recomendados de la biblioteca de vínculos dinámicos.
Si el archivo DLL está vinculado a la biblioteca en tiempo de ejecución de C (CRT), el punto de entrada proporcionado por CRT llama a los constructores y destructores para objetos C++ globales y estáticos. Por lo tanto, estas restricciones para DllMain también se aplican a constructores y destructores y a cualquier código al que se llame desde ellos.
Considere la posibilidad de llamar a DisableThreadLibraryCalls al recibir DLL_PROCESS_ATTACH, a menos que el archivo DLL esté vinculado con la biblioteca en tiempo de ejecución estática de C (CRT).
Requisitos
Requisito | Value |
---|---|
Cliente mínimo compatible |
Windows XP [solo aplicaciones de escritorio] |
Servidor mínimo compatible |
Windows Server 2003 [solo aplicaciones de escritorio] |
Encabezado |
|