C语言中调用GetModuleHandle获函数指针及DLL调用报错咨询
嘿,这俩问题都是Windows动态加载DLL时的典型坑,我之前做项目也踩过不少,给你详细拆解下:
问题1:C语言中调用GetModuleHandle并获取函数指针的正确方式
其实核心流程就是「拿模块句柄 → 定义匹配的函数指针 → 取函数地址 → 调用函数」,细节没做好很容易出问题,给你一步步来:
获取DLL模块句柄
- 如果目标DLL已经被进程加载(比如程序的依赖DLL),直接用
GetModuleHandleA/W传入DLL名字就能拿到句柄; - 如果DLL没加载,得先用
LoadLibraryA/W加载,用完记得用FreeLibrary释放,避免内存泄漏。
- 如果目标DLL已经被进程加载(比如程序的依赖DLL),直接用
定义正确的函数指针类型
这是重中之重!函数指针的签名必须和DLL导出函数的返回值、参数列表、调用约定完全一致,不然要么编译报错,要么运行时崩溃。一定要用typedef来定义,可读性和安全性都高。获取函数地址并调用
用GetProcAddress从模块句柄中取出函数地址,强制转换为你定义的函数指针类型,之后就可以像调用普通函数一样用了。
给你个完整的示例代码:
#include <windows.h> #include <stdio.h> // 假设DLL导出的函数是:int Add(int a, int b)(调用约定默认__cdecl) typedef int (*AddFunc)(int, int); // 严格匹配函数签名 int main() { HMODULE hDll = GetModuleHandleA("mydll.dll"); if (hDll == NULL) { // 尝试加载未加载的DLL hDll = LoadLibraryA("mydll.dll"); if (hDll == NULL) { printf("加载DLL失败,错误码:%d\n", GetLastError()); return 1; } } // 获取函数指针 AddFunc pAdd = (AddFunc)GetProcAddress(hDll, "Add"); if (pAdd == NULL) { printf("获取函数地址失败,错误码:%d\n", GetLastError()); FreeLibrary(hDll); return 1; } // 调用函数 int result = pAdd(3, 5); printf("3+5=%d\n", result); // 只有用LoadLibrary加载的DLL才需要手动释放 if (GetModuleHandleA("mydll.dll") == NULL) { FreeLibrary(hDll); } return 0; }
注意点:
- 区分
GetModuleHandleA(ASCII)和GetModuleHandleW(宽字符),根据你的项目编码选择; - 不要直接用
(int(*)(int,int))GetProcAddress(...)这种裸强制转换,维护起来太痛苦,typedef是最优解。
问题2:GetProcAddress编译报错+函数调用失败(double数组指针参数/返回值)
你遇到的问题大概率是函数指针签名不匹配或者调用约定不一致,再加上可能的函数名修饰问题,给你逐个解决:
核心问题分析
- 调用约定不匹配:Windows下VC编译的DLL默认导出函数用
__stdcall,而GCC默认是__cdecl,两者的栈处理方式不同,会导致编译报错或者运行时栈不平衡崩溃; - 数组指针的typedef写法错误:很多人会把
double*写成double**,但函数参数里的double[]会被隐式转为double*,所以参数和返回值都是double*就对了; - 函数名修饰:
__stdcall函数会被编译器加上名字修饰(比如_ProcessDoubleArray@4),用原名调用GetProcAddress会返回NULL。
正确的代码示例
假设DLL导出的函数原型是(VC编译,stdcall约定):
__declspec(dllexport) double* __stdcall ProcessDoubleArray(double* inputArray);
那GCC下的调用代码应该这么写:
#include <windows.h> #include <stdio.h> // 关键:严格匹配导出函数的签名和调用约定! // WINAPI是Windows.h定义的宏,等价于__stdcall typedef double* (WINAPI *ProcessArrayFunc)(double*); int main() { HMODULE hDll = LoadLibraryA("mydll.dll"); if (!hDll) { printf("LoadLibrary失败:%d\n", GetLastError()); return 1; } // 注意:这里要传修饰后的函数名! // 用dumpbin /exports mydll.dll可以查看DLL的导出表,确认函数名 ProcessArrayFunc pProcess = (ProcessArrayFunc)GetProcAddress(hDll, "_ProcessDoubleArray@4"); if (!pProcess) { printf("GetProcAddress失败:%d\n", GetLastError()); FreeLibrary(hDll); return 1; } // 测试调用 double input[] = {1.0, 2.0, 3.0}; double* output = pProcess(input); if (output) { // 示例:假设输出数组长度和输入一致 for (int i=0; i<3; i++) { printf("output[%d] = %.2lf\n", i, output[i]); } // 重要:如果DLL内部分配了内存,必须调用DLL提供的释放函数,不能直接free! } else { printf("函数调用失败!\n"); } FreeLibrary(hDll); return 0; }
解决报错的关键步骤
- 统一调用约定:在函数指针typedef里加上
WINAPI(或__stdcall),和DLL导出函数保持一致; - 确认函数名:用Windows SDK的
dumpbin工具查看DLL的导出表,拿到真实的导出函数名,不要凭猜测写; - GCC编译选项:编译时要链接kernel32库,比如加
-lkernel32,不然会找不到LoadLibrary这些Windows API; - 内存管理:如果DLL返回的数组是它内部分配的,一定要调用DLL导出的释放函数,不要用C标准库的
free,因为DLL和你的程序可能用的是不同的堆。
内容的提问来源于stack exchange,提问作者RTC222
相关产品推荐
相关产品推荐

