You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

仅持有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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 15:35:47