仅持有DLL文件时,如何通过LoadLibrary()初始化C++类并调用成员函数?
问题场景
仅保留导出C类的MyClass.dll文件,无VC项目源码,尝试通过获取 mangled 函数名调用类的构造函数和成员函数时,触发访问违例。当类无成员变量和构造函数时可正常调用,有成员变量和构造函数则报错。
DLL原类定义
//MyClass.h class __declspec(dllexport) MyClass{ public: int a; int b; public: MyClass(); int Sum(int, int); };
//MyClass.cpp MyClass::MyClass(){ a=0; b=0; } int MyClass::Sum(int c, int d){ return c+d; }
原测试代码(存在问题)
//Test.exe typedef void(__stdcall *TCon)(); // for constructor typedef int(__stdcall *TSum)(int, int); // for function Sum() int main(){ HMODULE myDll = LoadLibrary(TEXT("MyClass.dll")); FARPROC con = GetProcAddress(myDll, "??0MyClass@@QAE@XZ"); // 从DUMPBIN获取的mangled名称 FARPROC sum = GetProcAddress(myDll, "?Sum@MyClass@@QAEHHH@Z"); // 从DUMPBIN获取的mangled名称 TCon f_con = (TCon)con; TSum f_sum = (TSum)sum; f_con(); // 触发访问违例 printf("Sum is:%d\n", f_sum(1,2)); return 0; }
问题根源
C++非静态成员函数(包括构造函数)默认采用__thiscall调用约定,会隐含传递对象的指针(this指针)作为第一个参数。原代码的函数指针定义没有包含this指针,调用时相当于让函数操作一个无效的内存地址,导致访问违例——构造函数里初始化a、b时会错误地访问内存,成员函数Sum也无法找到正确的对象上下文。
解决方法
1. 修正函数指针的定义
必须在函数指针的参数列表开头添加void*类型的this指针参数,同时使用__thiscall调用约定:
// 构造函数指针:第一个参数是对象的this指针 typedef void(__thiscall *TCon)(void*); // Sum函数指针:第一个参数是this指针,后续是原函数的参数 typedef int(__thiscall *TSum)(void*, int, int);
2. 为对象分配内存
构造函数的作用是初始化已分配的内存,而非直接创建对象,因此需要先为MyClass分配足够的内存。如果不确定类的大小,可以直接分配足够大的内存(比如1024字节,只要不超出范围即可),或者通过DUMPBIN工具确认类的大小(本例中类包含两个int,至少8字节)。
3. 修正调用逻辑
将分配好的内存地址作为this指针传入构造函数和成员函数:
修正后的完整测试代码:
#include <windows.h> #include <stdio.h> #include <malloc.h> // 修正后的函数指针定义 typedef void(__thiscall *TCon)(void*); typedef int(__thiscall *TSum)(void*, int, int); int main(){ HMODULE myDll = LoadLibrary(TEXT("MyClass.dll")); if (!myDll) { printf("Failed to load DLL\n"); return 1; } FARPROC con = GetProcAddress(myDll, "??0MyClass@@QAE@XZ"); FARPROC sum = GetProcAddress(myDll, "?Sum@MyClass@@QAEHHH@Z"); if (!con || !sum) { printf("Failed to get proc address\n"); FreeLibrary(myDll); return 1; } TCon f_con = (TCon)con; TSum f_sum = (TSum)sum; // 为MyClass分配内存 void* pObj = malloc(sizeof(int) * 2); // 对应类的两个int成员,大小足够 if (!pObj) { printf("Failed to allocate memory\n"); FreeLibrary(myDll); return 1; } // 调用构造函数,传入对象指针作为this f_con(pObj); // 调用Sum函数,传入this指针和参数 printf("Sum is:%d\n", f_sum(pObj, 1, 2)); // 注意:如果类有析构函数,也需要按同样方式调用析构函数释放资源 // 此处省略析构函数调用,若需要需从DUMPBIN获取析构函数的mangled名称 free(pObj); FreeLibrary(myDll); return 0; }
额外说明
- 如果类有析构函数,需要用同样的方式获取析构函数的mangled名称,定义对应的函数指针(
typedef void(__thiscall *TDtor)(void*);),在释放内存前调用析构函数。 - 若不确定类的大小,可使用
malloc(1024)分配足够大的内存,只要成员函数不访问超出类实际大小的内存,就不会出现问题。
内容的提问来源于stack exchange,提问作者hzh
相关产品推荐
相关产品推荐

