Carregar dinamicamente uma function de uma DLL

Estou dando uma olhada nos arquivos .dll, entendo o uso deles e estou tentando entender como usá-los.

Eu criei um arquivo .dll que contém uma function que retorna um inteiro chamado funci ()

usando este código, eu (acho) eu importei o arquivo .dll para o projeto (não há reclamações):

#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; } 

No entanto, quando eu tento compilar esse arquivo .cpp que eu acho que importou o .dll eu tenho o seguinte erro:

 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 ===| 

Eu sei que um .dll é diferente de um arquivo de header, então eu sei que não posso importar uma function como esta, mas é o melhor que eu poderia fazer para mostrar que tentei.

Minha pergunta é, como posso usar o ponteiro “hGetProcIDDLL” para acessar a function dentro do .dll.

Espero que esta pergunta faça sentido e eu não esteja mais latindo novamente.

LoadLibrary não faz o que você acha que faz. Ele carrega a DLL na memory do processo atual, mas não importa magicamente funções definidas nela! Isso não seria possível, como chamadas de function são resolvidas pelo vinculador em tempo de compilation enquanto LoadLibrary é chamado em tempo de execução (lembre-se que C ++ é uma linguagem de tipo estaticamente ).

Você precisa de uma function WinAPI separada para obter o endereço das funções carregadas dinamicamente: GetProcAddress .

Exemplo

 #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; } 

Além disso, você deve exportar sua function da DLL corretamente. Isso pode ser feito assim:

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

Como Lundin observa, é uma boa prática liberar a alça para a biblioteca se você não precisar dela por mais tempo. Isso fará com que ele seja descarregado se nenhum outro processo ainda tiver um identificador para a mesma DLL.

Além da resposta já postada, eu pensei que deveria compartilhar um truque útil que eu usei para carregar todas as funções DLL no programa através de pointers de function, sem escrever uma chamada GetProcAddress separada para cada function. Eu também gosto de chamar as funções diretamente, como tentado no OP.

Comece definindo um tipo de ponteiro de function genérico:

 typedef int (__stdcall* func_ptr_t)(); 

Quais tipos usados ​​não são realmente importantes. Agora crie uma matriz desse tipo, que corresponde à quantidade de funções que você tem na DLL:

 func_ptr_t func_ptr [DLL_FUNCTIONS_N]; 

Nesta matriz, podemos armazenar os pointers de function reais que apontam para o espaço de memory da DLL.

O próximo problema é que GetProcAddress espera os nomes das funções como seqüências de caracteres. Então, crie uma matriz semelhante que consiste nos nomes das funções na DLL:

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

Agora podemos facilmente chamar GetProcAddress () em um loop e armazenar cada function dentro dessa matriz:

 for(int i=0; i 

Se o loop foi bem sucedido, o único problema que temos agora é chamar as funções. O ponteiro de function typedef anterior não é útil, porque cada function terá sua própria assinatura. Isso pode ser resolvido criando uma estrutura com todos os tipos de function:

 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; 

E, finalmente, para conectá-los ao array de antes, crie uma união:

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

Agora você pode carregar todas as funções da DLL com o loop conveniente, mas chamá-las através do membro da união by_type .

Mas é claro, é um pouco trabalhoso digitar algo como

functions.by_type.dll_add_ptr(1, 1); sempre que você quiser chamar uma function.

Como se constata, esta é a razão pela qual eu adicionei o postfix "ptr" aos nomes: Eu queria mantê-los diferentes dos nomes das funções reais. Agora podemos suavizar a syntax da estrutura icky e obter os nomes desejados, usando algumas 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) 

E voilà, agora você pode usar os nomes das funções, com o tipo e parâmetros corretos, como se eles estivessem vinculados estaticamente ao seu projeto:

 int result = dll_add(1, 1); 

Disclaimer: Estritamente falando, conversões entre diferentes pointers de function não são definidas pelo padrão C e não são seguras. Então, formalmente, o que estou fazendo aqui é um comportamento indefinido. No entanto, no mundo do Windows, os pointers de function são sempre do mesmo tamanho, independentemente do tipo, e as conversões entre eles são previsíveis em qualquer versão do Windows que usei.

Além disso, em teoria, pode haver padding inserido no union / struct, o que faria com que tudo falhasse. No entanto, os pointers são do mesmo tamanho que o requisito de alinhamento no Windows. Um static_assert para garantir que o struct / union não tenha preenchimento pode estar em ordem.