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

为何C++编写的DLL函数无法调用,而C编写的DLL可正常调用?

C++ DLL函数调用失败,C DLL正常

能调用C语言(.c文件)编写的DLL中的printword()函数,但调用C++语言(.cpp文件)编写的同名DLL函数时,GetProcAddress返回ERROR_PROC_NOT_FOUND错误。

C++版DLL代码

// FILE: dllmain.cpp
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

__declspec(dllexport) void printword() {
    std::cout << "word" << std::endl;    // 用printf()也无法解决
}

调用代码

int main() {
    HMODULE dll;
    if ((dll = LoadLibrary(L"D:\\Visual Studio projects\\Dll1\\x64\\Debug\\Dll1.dll")) == NULL) {
        return GetLastError();
    }

    FARPROC func;
    if ((func = GetProcAddress(dll, "printword")) == NULL) {
        return GetLastError();
    }

    func();

    return 0;
}

C版DLL代码(可正常调用)

// FILE: dllmain.c
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

__declspec(dllexport) void printword() {
    printf("word\n");
}

原因分析

问题核心是C++的名称修饰(Name Mangling)机制:

  • C语言编译时,会直接以函数原名导出,比如printword就是导出的名称;
  • C++为了支持函数重载、命名空间等特性,编译时会对函数名进行编码修饰,比如无参数的printword()会被修饰成类似?printword@@YAXXZ的名称(具体格式取决于编译器)。
    调用代码中用"printword"去查找,自然找不到实际导出的修饰名称,导致ERROR_PROC_NOT_FOUND。

解决方案

方法1:用extern "C"强制C语言链接规则

修改C++ DLL的函数声明,用extern "C"包裹,让编译器按C语言的规则导出函数,保留原名:

extern "C" __declspec(dllexport) void printword() {
    std::cout << "word" << std::endl;
}

如果需要导出多个函数,可以把它们放在extern "C"块中:

extern "C" {
    __declspec(dllexport) void printword();
    __declspec(dllexport) int add(int a, int b);
}

方法2:使用模块定义文件(.def)指定导出名称

创建一个.def文件(比如Dll1.def),内容如下:

LIBRARY Dll1
EXPORTS
    printword @1

将该文件添加到Visual Studio项目中,编译后DLL会按照指定的原名导出函数,无需修改代码中的函数声明。

验证方式

可以使用Visual Studio自带的dumpbin工具查看DLL的导出表,命令如下:

dumpbin /exports Dll1.dll

对比C和未处理的C++ DLL的输出,就能直观看到名称修饰的差异。

内容的提问来源于stack exchange,提问作者FocalCoscade

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 09:50:24