Además de la respuesta ya publicada, pensé que debería compartir un truco útil que uso para cargar todas las funciones de DLL en el programa 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.
de inicio mediante la definición de un tipo genérico de puntero de función:
typedef int (__stdcall* func_ptr_t)();
¿Qué tipos que se utilizan no son realmente importantes. Ahora crear una matriz de ese tipo, que corresponde a la cantidad de funciones que tiene en el archivo DLL:
func_ptr_t func_ptr [DLL_FUNCTIONS_N];
En esta matriz podemos almacenar los punteros a funciones reales que apuntan hacia el espacio de memoria DLL.
El siguiente problema es que GetProcAddress
espera los nombres de las funciones como cadenas.Por lo tanto crear una matriz similares que consiste en los nombres de las funciones de la DLL:
const char* DLL_FUNCTION_NAMES [DLL_FUNCTIONS_N] =
{
"dll_add",
"dll_subtract",
"dll_do_stuff",
...
};
Ahora podemos llamar fácilmente GetProcAddress() en un bucle y almacenar cada función dentro de esa matriz:
for(int i=0; i<DLL_FUNCTIONS_N; i++)
{
func_ptr[i] = GetProcAddress(hinst_mydll, DLL_FUNCTION_NAMES[i]);
if(func_ptr[i] == NULL)
{
// error handling, most likely you have to terminate the program here
}
}
Si el bucle 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 se puede resolver mediante la creación de 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, por último, para conectar estos a la matriz de antes, crear una unión:
typedef union
{
functions_struct by_type;
func_ptr_t func_ptr [DLL_FUNCTIONS_N];
} functions_union;
Ahora puede cargar toda la funciones de la DLL con el bucle conveniente, pero llámelos a través del miembro de la unión by_type
.
Pero, por supuesto, que es un poco onerosa que escribir algo así como
functions.by_type.dll_add_ptr(1, 1);
cada vez que desee 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 sintaxis estructura desordenada 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 listo, ahora puede usar los nombres de función, con el tipo y los parámetros correctos, como si fueran estáticamente vinculado a su proyecto:
int result = dll_add(1, 1);
de responsabilidad: en sentido estricto, las conversiones entre diferentes punteros de función no están definidas por la norma C y no es seguro. 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 falle. Sin embargo, los punteros son del mismo tamaño que el requisito de alineación en Windows. A static_assert
para asegurarse de que la estructura/unión no tenga relleno todavía podría estar en orden.
búsqueda enlace estático/dinámico. –
Gracias, voy a mirar esto –
Yo doblo mi código pero cuando lo introduzco aquí el formato se arruina así que termino hendiéndolo todo por 4 líneas –