从DLLdynamic加载函数
我有一点看看.dll文件,我了解他们的用法,我试图了解如何使用它们。
我创build了一个包含一个函数的.dll文件,该函数返回一个名为funci()的整数
使用这个代码,我(想)我已经导入.dll文件到项目中(没有投诉):
#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; }
但是,当我尝试编译这个.cpp文件,我认为已经导入.dll我有以下错误:
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 ===|
我知道一个.dll与头文件是不同的,所以我知道我可以不用导入这样的函数,但这是我能想出来的最好的certificate,我已经尝试过了。
我的问题是,如何使用“hGetProcIDDLL”指针访问.dll中的函数。
我希望这个问题是有道理的,我不是再次咆哮一些错误的树。
LoadLibrary
不会做你认为的事情。 它将DLL加载到当前进程的内存中,但不会奇迹般地导入其中定义的函数! 这是不可能的,因为函数调用在编译时由链接器parsing,而在运行时调用LoadLibrary
(请记住,C ++是静态types语言)。
您需要一个单独的WinAPI函数来获取dynamic加载函数的地址: GetProcAddress
。
例
#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; }
另外,你应该从DLL正确地导出你的函数。 这可以这样做:
int __declspec(dllexport) __stdcall funci() { // ... }
正如Lundin所指出的那样,如果你不再需要他们的话, 把手柄放到图书馆是一个很好的习惯。 这将导致它被卸载,如果没有其他进程仍然持有对同一个DLL的句柄。
除了已经发布的答案外,我还以为我应该分享一个方便的技巧,通过函数指针将所有DLL函数加载到程序中,而不必为每个函数都写一个单独的GetProcAddress调用。 我也想直接调用OP中的function。
首先定义一个通用函数指针types:
typedef int (__stdcall* func_ptr_t)();
使用什么types并不重要。 现在创build一个该types的数组,它对应于DLL中的函数数量:
func_ptr_t func_ptr [DLL_FUNCTIONS_N];
在这个数组中,我们可以存储指向DLL内存空间的实际函数指针。
接下来的问题是GetProcAddress
期望函数名string。 所以在DLL中创build一个由函数名组成的类似的数组:
const char* DLL_FUNCTION_NAMES [DLL_FUNCTIONS_N] = { "dll_add", "dll_subtract", "dll_do_stuff", ... };
现在我们可以在一个循环中轻松地调用GetProcAddress(),并将每个函数存储在该数组中:
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 } }
如果循环成功,我们现在唯一的问题就是调用函数。 前面的函数指针typedef没有帮助,因为每个函数都有自己的签名。 这可以通过创build一个包含所有函数types的结构来解决:
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;
最后,将这些连接到之前的数组,创build一个联合:
typedef union { functions_struct by_type; func_ptr_t func_ptr [DLL_FUNCTIONS_N]; } functions_union;
现在,您可以使用便捷的循环加载DLL中的所有函数,但通过by_type
联合成员调用它们。
但是,当然,input类似的东西有点麻烦
functions.by_type.dll_add_ptr(1, 1);
每当你想调用一个函数。
事实certificate,这就是为什么我将“ptr”后缀添加到名称的原因:我想让它们与实际函数名称不同。 我们现在可以通过使用一些macros来消除icky结构语法并获得所需的名称:
#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)
而且,您现在可以使用函数名称,正确的types和参数,就像静态链接到您的项目一样:
int result = dll_add(1, 1);
免责声明:严格来说,不同的函数指针之间的转换不是由C标准定义的,也不是安全的。 正式地说,我在这里做的是未定义的行为。 但是,在Windows世界中,无论函数指针的types如何,函数指针的大小始终是相同的,它们之间的转换在我使用的任何版本的Windows上都是可以预测的。
另外,理论上可能会在union / struct中插入填充,这会导致一切都失败。 但是,指针恰好与Windows中的alignment需求大小相同。 一个static_assert
以确保结构/联合没有填充可能仍然是。