Carga dinámicamente una función desde una DLL

Estoy echando un vistazo a los archivos .dll, entiendo su uso y trato de entender cómo usarlos.

Creé un archivo .dll que contiene una función que devuelve un entero llamado funci ()

usando este código, creo que he importado el archivo .dll al proyecto (no hay quejas):

#include  #include  int main() { HINSTANCE hGetProcIDDLL = LoadLibrary("C:\\Documents and Settings\\User\\Desktop \\fgfdg\\dgdg\\test.dll"); if (hGetProcIDDLL == NULL) { std::cout << "cannot locate the .dll file" << std::endl; } else { std::cout << "it has been called" << std::endl; return -1; } int a = funci(); return a; } # funci function int funci() { return 40; } 

Sin embargo, cuando bash comstackr este archivo .cpp que creo que importó el .dll, tengo el siguiente error:

 C:\Documents and Settings\User\Desktop\fgfdg\onemore.cpp||In function 'int main()':| C:\Documents and Settings\User\Desktop\fgfdg\onemore.cpp|16|error: 'funci' was not declared in this scope| ||=== Build finished: 1 errors, 0 warnings ===| 

Sé que .dll es diferente de un archivo de encabezado, así que sé que no puedo importar una función como esta, pero es lo mejor que pude hacer para demostrar que lo he intentado.

Mi pregunta es, ¿cómo puedo usar el puntero “hGetProcIDDLL” para acceder a la función dentro del .dll.

Espero que esta pregunta tenga sentido y no estoy gritando un árbol equivocado una vez más.

LoadLibrary no hace lo que crees que hace. Carga la DLL en la memoria del proceso actual, ¡pero no importa mágicamente las funciones definidas en ella! Esto no sería posible, ya que las llamadas a funciones las resuelve el vinculador en tiempo de comstackción mientras se llama a LoadLibrary en el tiempo de ejecución (recuerde que C ++ es un lenguaje estáticamente tipado ).

Necesita una función WinAPI separada para obtener la dirección de las funciones cargadas dinámicamente: GetProcAddress .

Ejemplo

 #include  #include  /* Define a function pointer for our imported * function. * This reads as "introduce the new type f_funci as the type: * pointer to a function returning an int and * taking no arguments. * * Make sure to use matching calling convention (__cdecl, __stdcall, ...) * with the exported function. __stdcall is the convention used by the WinAPI */ typedef int (__stdcall *f_funci)(); int main() { HINSTANCE hGetProcIDDLL = LoadLibrary("C:\\Documents and Settings\\User\\Desktop\\test.dll"); if (!hGetProcIDDLL) { std::cout << "could not load the dynamic library" << std::endl; return EXIT_FAILURE; } // resolve function address here f_funci funci = (f_funci)GetProcAddress(hGetProcIDDLL, "funci"); if (!funci) { std::cout << "could not locate the function" << std::endl; return EXIT_FAILURE; } std::cout << "funci() returned " << funci() << std::endl; return EXIT_SUCCESS; } 

Además, debe exportar su función desde la DLL correctamente. Esto se puede hacer así:

 int __declspec(dllexport) __stdcall funci() { // ... } 

Como señala Lundin, es una buena práctica liberar el identificador a la biblioteca si no los necesita por más tiempo. Esto hará que se descargue si ningún otro proceso todavía tiene un identificador para la misma DLL.

Además de la respuesta ya publicada, pensé que debería compartir un truco práctico que uso para cargar todas las funciones de DLL en el progtwig a través de indicadores de función, sin escribir una llamada GetProcAddress por separado para cada función. También me gusta llamar a las funciones directamente como se intentó en el OP.

Comience por definir un tipo de puntero de función genérico:

 typedef int (__stdcall* func_ptr_t)(); 

Qué tipos que se usan no son realmente importantes. Ahora crea una matriz de ese tipo, que corresponde a la cantidad de funciones que tienes en la DLL:

 func_ptr_t func_ptr [DLL_FUNCTIONS_N]; 

En esta matriz, podemos almacenar los punteros de función reales que apuntan al espacio de memoria DLL.

El siguiente problema es que GetProcAddress espera los nombres de las funciones como cadenas. Por lo tanto, cree una matriz similar que conste de los nombres de las funciones en la DLL:

 const char* DLL_FUNCTION_NAMES [DLL_FUNCTIONS_N] = { "dll_add", "dll_subtract", "dll_do_stuff", ... }; 

Ahora podemos llamar fácilmente a GetProcAddress () en un bucle y almacenar cada función dentro de esa matriz:

 for(int i=0; i 

Si el ciclo fue exitoso, el único problema que tenemos ahora es llamar a las funciones. El puntero de función typedef de antes no es útil, porque cada función tendrá su propia firma. Esto puede resolverse creando una estructura con todos los tipos de funciones:

 typedef struct { int (__stdcall* dll_add_ptr)(int, int); int (__stdcall* dll_subtract_ptr)(int, int); void (__stdcall* dll_do_stuff_ptr)(something); ... } functions_struct; 

Y finalmente, para conectar estos a la matriz de antes, crea una unión:

 typedef union { functions_struct by_type; func_ptr_t func_ptr [DLL_FUNCTIONS_N]; } functions_union; 

Ahora puede cargar todas las funciones de la DLL con el bucle conveniente, pero by_type a través del miembro de la unión by_type .

Pero, por supuesto, es un poco molesto escribir algo así como

functions.by_type.dll_add_ptr(1, 1); cada vez que quiera llamar a una función.

Resulta que esta es la razón por la que agregué el postfix "ptr" a los nombres: quería mantenerlos diferentes de los nombres de las funciones reales. Ahora podemos suavizar la syntax icky struct y obtener los nombres deseados, mediante el uso de algunas macros:

 #define dll_add (functions.by_type.dll_add_ptr) #define dll_subtract (functions.by_type.dll_subtract_ptr) #define dll_do_stuff (functions.by_type.dll_do_stuff_ptr) 

Y voilà, ahora puede usar los nombres de las funciones, con el tipo y los parámetros correctos, como si estuvieran vinculados estáticamente a su proyecto:

 int result = dll_add(1, 1); 

Descargo de responsabilidad: Estrictamente hablando, las conversiones entre diferentes punteros de función no están definidas por el estándar C y no son seguras. Entonces formalmente, lo que estoy haciendo aquí es un comportamiento indefinido. Sin embargo, en el mundo de Windows, los punteros de función siempre son del mismo tamaño sin importar su tipo y las conversiones entre ellos son predecibles en cualquier versión de Windows que haya utilizado.

Además, en teoría podría haber relleno insertado en la unión / estructura, lo que haría que todo fallara. Sin embargo, los punteros son del mismo tamaño que el requisito de alineación en Windows. Un static_assert para asegurar que struct / union no tenga relleno puede estar todavía en orden.