Dynamically load a function from a DLL

I look at DLL files a bit, I understand their use, and I'm trying to figure out how to use them.

I created a .dll file containing a function that returns an integer named funci ()

using this code, I (think) I imported the DLL file into the project (there are no complaints):

#include <windows.h> #include <iostream> 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; } 

However, when I try to compile this .cpp file, which I think imported .dll. I have the following 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 ===| 

I know that .dll is different from the header file, so I know that I can: t import such a function, but this is the best I could come up with to show what I tried.

My question is how can I use the "hGetProcIDDLL" pointer to access a function in a DLL.

I hope this question makes sense, and again I do not bark some wrong tree.

+52
c ++ dll winapi
Jan 02
source share
2 answers

LoadLibrary does not do what you think. It loads the DLL into the memory of the current process, but does not magically import the functions defined in it! This would not be possible, since function calls are resolved by the linker at compile time, and LoadLibrary is called at run time (remember that C ++ is statically typed ).

To obtain the address of dynamically loaded functions, you will need a separate WinAPI function: GetProcAddress .

Example

 #include <windows.h> #include <iostream> /* 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; } 

In addition, you must export your function from the DLL correctly. This can be done as follows:

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

As Lundin notes, it is good practice to free the library descriptor if you no longer need it. This will unload it if no other process still contains a handle to a single DLL.

+91
Jan 02 '12 at 1:10
source share

In addition to the answer already posted, I thought I should share a convenient trick that I use to load all the DLL functions into the program using function pointers, without creating a separate GetProcAddress call for each function. I would also like to call functions directly, like trying in the OP.

Start by defining a pointer type for a generic function:

 typedef int (__stdcall* func_ptr_t)(); 

Which types that are used are not very important. Now create an array of this type, which corresponds to the number of functions that you have in the DLL:

 func_ptr_t func_ptr [DLL_FUNCTIONS_N]; 

In this array, we can store the actual function pointers pointing to the DLL's memory space.

The next problem is that GetProcAddress expects function names as strings. Therefore, create a similar array consisting of function names in the DLL:

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

Now we can easily call GetProcAddress () in a loop and save each function inside this array:

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

If the cycle was successful, the only problem we have now is function calls. The typedef function pointer from earlier does not help, because each function will have its own signature. This can be solved by creating a structure with all types of functions:

 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; 

And finally, to associate them with the array earlier, create a union:

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

Now you can load all functions from the DLL using a convenient loop, but name them through the by_type member.

But of course it's a little burdensome to type something like

functions.by_type.dll_add_ptr(1, 1); whenever you want to call a function.

As it turned out, it was for this reason that I added the "ptr" postfix to the names: I wanted them to be different from the actual function names. Now we can smooth out the icky struct syntax and get the names we need using some 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) 

And now you can use function names with the correct type and parameters, as if they were statically linked to your project:

 int result = dll_add(1, 1); 



Disclaimer: Strictly speaking, conversions between different function pointers are not defined by the C standard and are unsafe. Formally, what I'm doing here is undefined behavior. However, in the Windows world, function pointers always have the same size regardless of type, and conversions between them are predictable in any version of Windows that I used.

In addition, there could theoretically be an addition added to union / struct, which will cause everything to fail. However, pointers are the same size as the alignment requirement in Windows. A static_assert , to ensure that struct / union is not indented, may still be in order.

+16
May 20 '14 at 14:55
source share



All Articles