GetProcAddress返回NULL

在编译DLL时,导出函数会进行名字处理以确保跨语言调用兼容性。通过extern C和__declspec(dllexport)修饰符,可以指定函数在DLL中的导出方式。

在编译DLL之后,原有的函数名会被进行名字处理而改变名称。 


在dll中,导出函数之前加上   extern   "C "   ,   比如: 
extern   "C "   __declspec(dllexport)   void   fnDll1(int   tmp);

<think>我们正在讨论C++中GetProcAddress函数的使用方法。根据提供的引用内容,我们可以总结出以下关键点: 1. GetProcAddress用于从已加载的DLL模块中获取导出函数的地址。 2. 使用步骤: a. 使用LoadLibrary或LoadLibraryEx加载DLL,获得模块句柄(HMODULE)。 b. 使用GetProcAddress传入模块句柄和函数名(或导出序号)获取函数地址。 c.返回的地址转换为适当的函数指针类型。 d. 通过函数指针调用函数。 e. 最后使用FreeLibrary卸载DLL。 3. 注意:在C++中,由于函数名修饰(name mangling)问题,如果DLL是用C++编写的,并且希望按名称获取函数,则需要在DLL中声明导出函数时使用extern "C"(以避免名称修饰),或者使用模块定义文件(.def)指定导出名称。否则,需要使用经过修饰的名称(可以通过工具如dumpbin查看)。 4. 函数指针的声明必须与导出函数的调用约定、返回类型和参数类型完全匹配。 引用[1]展示了传统的C语言用法,使用FARPROC(实际上是一个函数指针,但未指定参数和返回类型)来接收函数地址,然后强制转换并调用。但在C++中,我们可以使用类型安全的函数指针。 引用[2]提供了一个更清晰的C++示例,其中定义了一个与导出函数匹配的函数指针类型(AddFunc),然后使用GetProcAddress获取函数地址并赋值给该类型的指针,然后调用。 下面我们将按照用户要求,提供一个详细的C++教程和示例。 ### C++中使用GetProcAddress的步骤 1. **包含头文件**:需要包含Windows.h头文件。 2. **加载DLL**:使用`LoadLibrary`或`LoadLibraryEx`函数加载DLL返回一个HMODULE句柄。 3. **获取函数地址**:使用`GetProcAddress`函数,传入HMODULE和函数名称(字符串)或导出序号。注意,函数名称区分大小写。 4. **定义函数指针类型**:根据导出函数的签名(返回类型、参数类型和调用约定)定义函数指针类型。 5. **转换函数地址**:将GetProcAddress返回的FARPROC(实际上是void*)转换为定义的函数指针类型。 6. **调用函数**:通过函数指针调用函数。 7. **卸载DLL**:使用`FreeLibrary`释放DLL模块。 ### 示例代码 假设我们有一个DLL(名为MyDll.dll),其中导出了一个函数: ```cpp extern "C" __declspec(dllexport) int Add(int a, int b); ``` 注意:这里使用`extern "C"`来避免C++名称修饰,这样我们就可以用函数名"Add"获取地址。 下面是在C++应用程序中动态加载该DLL并调用Add函数的示例: ```cpp #include <Windows.h> #include <iostream> // 定义函数指针类型:调用约定为__stdcall(如果导出函数是__stdcall,则需要指定,否则默认是__cdecl) // 这里假设Add函数是__cdecl(默认调用约定) typedef int (*AddFunc)(int, int); int main() { // 1. 加载DLL HMODULE hDll = LoadLibrary(TEXT("MyDll.dll")); if (hDll == NULL) { std::cerr << "Failed to load DLL! Error: " << GetLastError() << std::endl; return 1; } // 2. 获取Add函数的地址 AddFunc pAdd = (AddFunc)GetProcAddress(hDll, "Add"); if (pAdd == NULL) { std::cerr << "Failed to get function address! Error: " << GetLastError() << std::endl; FreeLibrary(hDll); return 1; } // 3. 调用函数 int a = 5, b = 3; int result = pAdd(a, b); std::cout << a << " + " << b << " = " << result << std::endl; // 4. 卸载DLL FreeLibrary(hDll); return 0; } ``` ### 注意事项 1. **调用约定**:如果DLL中的函数使用不同的调用约定(如__stdcall),则函数指针类型也必须指定相同的调用约定。例如: ```cpp typedef int (__stdcall *AddFunc)(int, int); ``` 2. **名称修饰问题**:如果DLL是用C++编写的且没有使用extern "C",则函数名会被修饰(例如,可能变成"?Add@@YAHHH@Z")。这时,要么使用修饰后的名称,要么在DLL导出时避免名称修饰。 3. **错误处理**:每次调用Windows API后,检查返回值并处理错误(使用GetLastError)。 4. **Unicode支持**:如果使用Unicode字符集,可以使用TEXT宏或L"..."字符串,也可以直接使用宽字符版本的LoadLibraryW(使用宽字符串)或LoadLibraryA(使用ANSI字符串)。上例中,TEXT宏会根据编译设置自动选择。 5. **导出序号**:除了函数名,还可以使用导出序号(在DLL.def文件中指定)来获取函数地址,这时需要将序号转换为字符串指针(注意:序号不能是0,并且低字必须为非0)。例如: ```cpp pAdd = (AddFunc)GetProcAddress(hDll, MAKEINTRESOURCE(1)); // 假设Add函数的导出序号是1 ``` 6. **函数指针类型定义**:为了代码清晰,建议为每个要调用的导出函数定义相应的函数指针类型。 ### 常见问题 1. **GetProcAddress返回NULL**:可能原因包括函数名拼写错误、DLL没有导出该函数、名称修饰问题、DLL未加载成功等。 2. **调用函数时崩溃**:可能原因包括函数指针类型声明错误(如调用约定、参数类型或数量不匹配)、堆栈损坏等。 3. **内存泄漏**:确保每个LoadLibrary调用都有对应的FreeLibrary调用。 通过以上步骤和示例,你应该能够在C++中正确使用GetProcAddress函数动态调用DLL中的函数。 §§相关问题§§ 1. 如何解决C++中由于名称修饰导致GetProcAddress失败的问题? 2. 在动态加载DLL时,如何处理不同的调用约定(如__stdcall和__cdecl)? 3. 如何安全地定义函数指针类型以避免类型不匹配导致的运行时错误? 4. 使用GetProcAddress获取函数地址时,使用函数名和导出序号有什么区别? 5. 在C++中,如何设计DLL以便于动态加载?</think>### C++中GetProcAddress函数使用教程 GetProcAddress是Windows API中用于从动态链接库(DLL)获取函数地址的关键函数。以下是详细使用步骤和示例: #### 核心步骤 1. **加载DLL**:使用`LoadLibrary()`加载目标DLL 2. **获取函数地址**:使用`GetProcAddress()`获取函数指针 3. **定义函数指针**:声明与DLL函数匹配的函数指针类型 4. **调用函数**:通过函数指针执行DLL函数 5. **卸载DLL**:使用`FreeLibrary()`释放资源 #### 完整示例代码 ```cpp #include <Windows.h> #include <iostream> // 1. 定义函数指针类型(需与DLL函数签名完全匹配) typedef int (*AddFunc)(int, int); // 匹配int Add(int, int) typedef void (*PrintFunc)(const char*); // 匹配void Print(const char*) int main() { // 2. 加载DLL HMODULE hDll = LoadLibrary("Example.dll"); if (!hDll) { std::cerr << "DLL加载失败! 错误代码: " << GetLastError() << std::endl; return 1; } // 3. 获取Add函数地址 AddFunc pAdd = (AddFunc)GetProcAddress(hDll, "Add"); if (!pAdd) { std::cerr << "Add函数获取失败! 错误代码: " << GetLastError() << std::endl; FreeLibrary(hDll); return 1; } // 4. 获取Print函数地址 PrintFunc pPrint = (PrintFunc)GetProcAddress(hDll, "Print"); if (!pPrint) { std::cerr << "Print函数获取失败!" << std::endl; FreeLibrary(hDll); return 1; } // 5. 调用DLL函数 int result = pAdd(5, 3); pPrint("计算结果: "); std::cout << "5 + 3 = " << result << std::endl; // 6. 卸载DLL FreeLibrary(hDll); return 0; } ``` #### 关键注意事项 1. **函数签名匹配**:函数指针声明必须与DLL导出函数的: - 返回类型 - 参数类型和数量 - 调用约定(如`__stdcall`或`__cdecl`) 完全一致[^1] 2. **名称修饰问题**: - C++函数会进行名称修饰(name mangling) - 解决方案: ```cpp // DLL中使用extern "C"避免名称修饰 extern "C" __declspec(dllexport) int Add(int a, int b); ``` - 或使用`.def`文件显式指定导出名称 3. **错误处理**: - 检查`GetProcAddress`返回的`NULL`值 - 使用`GetLastError()`获取错误代码 - 常见错误: - `127`:函数未找到(名称不匹配) - `193`:无效的DLL格式(32/64位不匹配) 4. **调用约定**: - 如果DLL函数使用`__stdcall`: ```cpp typedef int (__stdcall *AddFunc)(int, int); ``` #### 典型应用场景 - 插件系统开发 - 按需加载功能模块 - 兼容不同版本的DLL - 热更新系统
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值