Cómo compilar un archivo dll
Crear un proyecto DLL
Aquí, para explicar brevemente el principio de DLL, decidimos utilizar el entorno de compilación más simple VC6.0, como se muestra a continuación, primero creamos un nuevo proyecto de biblioteca de enlaces dinámicos Win32, denominado "MyDLL". En Visual Studio, también puede crear un programa de consola Win32 y luego seleccionar la opción "DLL" en "Tipo de aplicación".
Haga clic en Seleccionar. "Un proyecto DLL vacío", confirme y complete.
Un dll simple
Cree un archivo de código fuente "dllmain.cpp" en el proyecto que creamos en el primer paso. En "dllmain.cpp", escriba el siguiente código.
p>
p>
[cpp] ver copia simple
#include lt;
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
cambiar (ul_reason_for_call)
{
caso DLL_PROCESS_ATTACH: p>
printf("DLL_PROCESS_ATTACH\n");
romper;
caso DLL_THREAD_ATTACH:
printf("DLL_THREAD_ATTACH\ n");
romper;
caso DLL_THREAD_DETACH:
printf("DLL_THREAD_DETACH\n");
romper; p>
caso DLL_PROCESS_DETACH:
printf("DLL_PROCESS_DETACH\n");
break
}
return TRUE;
}
Después de eso, compilamos directamente, es decir, podemos encontrar el archivo dll que generamos, "MyDLL.dll", en la carpeta Debug. Tenga en cuenta que printf. La declaración en el código no es necesaria, solo la usamos cuando probamos el programa. La función DllMain es la función de entrada/salida del dll.
De hecho, hay dos formas en que un subproceso llama a una DLL: vinculación implícita y vinculación explícita. El propósito de ambas es asignar la imagen del archivo de la DLL al espacio de direcciones del proceso del subproceso. . Aquí solo lo mencionaremos brevemente sin profundizar en una investigación. Si está interesado, puede leer el Capítulo 12 de la "Guía de programación avanzada de Windows".
Llamadas a enlaces implícitos
El enlace ermitaño es el método más común para asignar los efectos de archivos de una DLL al espacio de direcciones del proceso. Al vincular una aplicación, se debe especificar un conjunto de archivos LIB a vincular. Cada archivo LIB contiene una lista de funciones que el archivo DLL permite llamar a una aplicación (u otra DLL). Cuando el vinculador ve que una aplicación llama a una función dada en el archivo LIB de una DLL, agrega información a la imagen del archivo EXE generada indicando el nombre del archivo DLL que contiene la función. Cuando el sistema operativo carga un archivo EXE, mira el contenido de la imagen del archivo EXE para ver qué DLL cargar y luego intenta asignar la imagen del archivo DLL requerida al espacio de direcciones del proceso.
Al buscar una DLL, el sistema busca imágenes de archivos en una serie de ubicaciones.
1. El directorio que contiene el archivo de imagen EXE
2. El directorio actual del proceso
3.
4. Directorio de Windows
5. Directorio listado en la variable de entorno PATH
Este método generalmente se controla cuando el programa está vinculado y se refleja en la configuración del vinculador. de la configuración en línea de varias bibliotecas mencionadas, como OPENGL u OPENCV, etc., todas usan este método
Llamada de enlace explícito
Aquí solo mencionamos dos funciones, una es una función de carga
[cpp] ver copia simple
HINSTANCE LoadLibrary(LPCTSTR lpszLibFile);
HINSTANCE LoadLibraryEx(LPCSTR lpszLibFile, HANDLE hFile, DWORD dwFlags);
El valor de retorno HINSTANCE indica la dirección de memoria virtual de la asignación de imágenes del archivo. Si la DLL no se puede asignar al espacio de direcciones del proceso, la función devuelve NULL. Puedes usar algo como
[cpp] ver copia simple
LoadLibrary("MyDLL")
o
[cpp] ver Se llama a la copia simple
LoadLibrary("MyDLL.dll")
. Existen diferencias en las estrategias de búsqueda entre sin sufijo y con sufijo, que no se explicarán en detalle aquí.
Liberar explícitamente la DLL
Después de cargar explícitamente la DLL, se puede llamar a la función FreeLibrary en cualquier momento para eliminar explícitamente la imagen del archivo del espacio de direcciones del proceso.
[cpp] ver copia simple
BOOL FreeLibrary(HINSTANCE hinstDll);
Aquí, cuando se llama a la misma DLL en el mismo proceso, en realidad se trata de un cuestión de conteo. No hay explicación detallada aquí.
El hilo puede llamar a la función GetModuleHandle:
[cpp] ver copia simple
GetModuleHandle(LPCTSTR lpszModuleName
Para determinar); una DLL Si está asignada al espacio de direcciones del proceso. Por ejemplo, el siguiente código determina si MyDLL.dll se ha asignado al espacio de direcciones del proceso y, en caso contrario, lo carga:
[cpp] ver copia simple
HINSTANCE hinstDll;
hinstDll = GetModuleHandle("MiDLL");
if (hinstDll == NULL){
hinstDll = LoadLibrary("MiDLL"); >
}
De hecho, existen algunas funciones, como GetModuleFileName para obtener el nombre de ruta completo de la DLL y FreeLibraryAndExitThread para reducir el recuento de uso de DLL y salir del hilo. Para contenido específico, consulte el Capítulo 12 de la "Guía de programación avanzada de Windows". Este artículo no es adecuado para cubrir tanto contenido que los lectores no puedan aceptarlo todo a la vez.
Funciones de entrada y salida de DLL
Hablando de esto, solo hablamos de algunas funciones de uso común, y esta sección es el foco.
En el ejemplo de MyDLL que vimos arriba, hay una función DllMain, que es la llamada función de entrada/salida. El sistema llama a esta función en diferentes momentos. Estas llamadas proporcionan principalmente información y, a menudo, las DLL las utilizan para realizar trabajos de inicialización y limpieza a nivel de proceso o de subproceso. Si su DLL no requiere estas notificaciones, no necesita implementar esta función en su código fuente de DLL. Por ejemplo, si crea una DLL que solo contiene recursos, no necesita implementar esta función. Pero si lo hay, debe estar en el formato que tenemos arriba.
El parámetro ul_reason_for_call en la función DllMain indica por qué se llama a la función. Este parámetro tiene 4 valores posibles: DLL_PROCESS_ATTACH, DLL_THREAD_ATTACH, DLL_THREAD_DETACH, DLL_PROCESS_DETACH.
Entre ellos, DLL_PROCESS_ATTACH es cuando una DLL se asigna al espacio de direcciones del proceso por primera vez, el sistema llama a su función DllMain y el parámetro ul_reason_for_call pasado es DLL_PROCESS_ATTACH. Esto sólo sucede cuando se mapea por primera vez. Si más tarde un subproceso llama a LoadLibrary o LoadLibraryEx para una DLL que se ha asignado, el sistema operativo solo aumentará el recuento de DLL y no llamará a la función DllMain de la DLL con DLL_PROCESS_ATTACH.
DLL_PROCESS_DETACH es cuando la DLL se desasigna del espacio de direcciones del proceso, el sistema llama a su función DllMain y el valor ul_reason_for_call pasado es DLL_PROCESS_DETACH. Lo que debemos tener en cuenta es que al llamar a la función DllMain de la DLL con DLL_PROCESS_ATTACH, si devuelve FALSE, lo que indica que la inicialización no tuvo éxito, el sistema seguirá usando DLL_PROCESS_DETACH para llamar a DllMain de la DLL. Por lo tanto, debe asegurarse de no limpiar elementos que no se inicializaron correctamente.
DLL_THREAD_ATTACH: Cuando se crea un hilo en el proceso, el sistema mira todas las imágenes de archivos DLL actualmente asignadas al espacio de direcciones del proceso y llama a la función DllMain de todas estas DLL con el valor DLL_THREAD_ATTACH. Esta notificación indica a todas las DLL que realicen una inicialización a nivel de subproceso. Tenga en cuenta que al asignar una nueva DLL, ya hay varios subprocesos ejecutándose en el proceso y el sistema no llamará a la función DllMain de la DLL con el valor DLL_THREAD_ATTACH para los subprocesos que ya se están ejecutando.
En cuanto a DLL_THREAD_DETACH, si el subproceso llama a ExitThread para finalizar (si la función del subproceso regresa en lugar de llamar a ExitThread, el sistema llamará automáticamente a ExitThread), el sistema verifica todas las imágenes de archivos DLL actualmente asignadas al espacio de proceso. y usa el valor DLL_THREAD_DETACH para llamar a la función DllMain de todas las DLL. Esta notificación indica a todas las DLL que realicen un trabajo de limpieza a nivel de subproceso.
Aquí debemos tener en cuenta que si el hilo termina porque un hilo en el sistema llama a TerminateThread, el sistema ya no usará DLL_THREAD_DETACH para llamar a las funciones DLL y DllMain. Esto es lo mismo que TerminateProcess y no debe usarse a menos que sea absolutamente necesario.
A continuación, publicamos dos imágenes de la "Guía de programación avanzada de Windows" para ilustrar la llamada de los cuatro parámetros anteriores.
Bien, se ha introducido la situación anterior. Ahora, continuemos con la práctica. Esta vez, creemos un nuevo proyecto de consola win32 vacío, TestDLL. No hay más detalles, el código es el siguiente:
<. p>[cpp] ver copia simple#include lt;iostreamgt;
#include lt;Windows.hgt
usando el espacio de nombres std;
p>
DWORD WINAPI algunaFunción(LPVOID lpParam)
{
cout lt; "ingrese alguna Función lt; >
Dormir(1000);
cout lt;lt; "¡Esta es una función!" p>cout lt ;lt; "salir de alguna función!" lt;lt; endl;
return 0; >
{
HINSTANCE hinstance = LoadLibrary("MyDLL");
if(hinstance!=NULL)
{
cout lt; "Carga exitosa" lt; endl;
cout lt; /p>
}
HANDLE hThread;
DWORD dwThreadId;
cout lt;lt; "createThread antes de " lt;lt;
hThread = CreateThread(NULL, 0, someFunction, NULL, 0, & dwThreadId);
cout lt; "createThread después de " lt;
cout lt;lt; endl;
Sueño(3000);
cout lt;lt; "waitForSingleObject before " lt;endl;
WaitForSingleObject( hThread, INFINITE);
cout lt;lt; "WaitForSingleObject después de " lt;lt;
cout lt;endl;
FreeLibrary( hinstance);
return 0;
}
El código es fácil de entender, pero la premisa es que debes tener un cierto concepto de trapos. Además, tenga en cuenta que el "MyDLL.dll" que compilamos anteriormente debe copiarse en un lugar donde se pueda encontrar nuestro proyecto, que es un lugar en la ruta de búsqueda que mencionamos anteriormente.
Aquí publicaremos los resultados primero. Por supuesto, este es solo el resultado de una de las ejecuciones en mi máquina.
Con el conocimiento que presentamos anteriormente, esto no es difícil de entender. Cuando el proceso principal llama a LoadLibrary, llama a la función DllMain con DLL_PROCESS_ATTACH, y cuando se crea el hilo, llama a la función DllMain con DLL_THREAD_ATTACH. Dado que debido a que el subproceso principal y el subproceso son paralelos, puede haber interrupciones durante la salida. Sin embargo, esto nos permitirá comprender el programa con mayor claridad.