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

在Ubuntu的Mono中调用VS编译的C++ DLL报错,是否需重新编译?

解答:Linux下Mono调用原生C++库的问题

没错,你必须在Linux环境下重新编译你的C++库——Windows的DLL和Linux的共享库(.so文件)是完全不同的二进制格式,Mono根本没法加载Windows平台编译的DLL文件,这就是你调用时出错的核心原因。

为什么必须重新编译?

Windows DLL采用PE(Portable Executable)格式,而Linux共享库是ELF(Executable and Linkable Format)格式,二者的二进制结构、系统调用接口、依赖的标准库都完全不兼容。Mono虽然能运行.NET程序集,但它调用原生库时依赖目标系统的原生加载机制,所以只能加载Linux平台编译的.so文件。

基于现有VS C++代码在Linux编译的步骤

1. 准备Linux编译环境

首先在Ubuntu上安装必要的编译工具链:

sudo apt-get update
sudo apt-get install build-essential cmake

build-essential包含了gcc/g++、make等基础编译工具,cmake可以帮你更方便地处理复杂项目的跨平台构建。

2. 迁移项目配置与代码适配

VS的.vcxproj项目文件是Windows专属的,Linux无法识别,你需要做以下关键调整:

  • 导出符号处理:Windows下你可能用了__declspec(dllexport)导出函数,在Linux下需要替换为extern "C"(避免C++名字修饰),配合__attribute__((visibility("default")))明确导出符号。如果想让代码跨平台兼容,可以用宏定义:
    #ifdef _WIN32
    #define EXPORT __declspec(dllexport)
    #else
    #define EXPORT extern "C" __attribute__((visibility("default")))
    #endif
    
    EXPORT int MyFunction(int param);
    
  • 移除Windows专属API:如果代码里用了Win32 API(比如CreateFile、WaitForSingleObject、注册表操作等),需要替换为POSIX兼容的API或者跨平台库(比如Boost、POCO)。比如Windows的DWORD可以替换为uint32_t,HANDLE根据实际用途替换为文件描述符或其他对应类型。

3. 构建共享库

你可以选择用Makefile或者CMake来构建:

  • 用Makefile:创建一个Makefile文件,示例内容:
    CC = g++
    CFLAGS = -fPIC -Wall -Wextra
    LDFLAGS = -shared
    TARGET = libyourlibrary.so
    SOURCES = your_source_file1.cpp your_source_file2.cpp
    
    all: $(TARGET)
    
    $(TARGET): $(SOURCES)
        $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^
    
    clean:
        rm -f $(TARGET)
    
    终端运行make即可生成libyourlibrary.so。
  • 用CMake:创建CMakeLists.txt文件,示例内容:
    cmake_minimum_required(VERSION 3.10)
    project(YourLibrary)
    
    set(CMAKE_CXX_STANDARD 11)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
    
    add_library(yourlibrary SHARED
        your_source_file1.cpp
        your_source_file2.cpp
    )
    
    执行以下命令构建:
    mkdir build
    cd build
    cmake ..
    make
    
    生成的共享库会在build目录下,命名为libyourlibrary.so。

4. 调整.NET程序的PInvoke调用

修改.NET代码中的DllImport属性,把原来的Windows DLL名称换成Linux下的.so文件名:

// 原Windows版本
[DllImport("YourLibrary.dll")]
public static extern int MyFunction(int param);

// 修改后的Linux版本
[DllImport("libyourlibrary.so")]
public static extern int MyFunction(int param);

同时确保.so文件能被Mono找到:

  • 把.so文件和.NET程序放在同一目录;
  • 或者设置LD_LIBRARY_PATH环境变量:
    export LD_LIBRARY_PATH=/path/to/your/so/directory:$LD_LIBRARY_PATH
    mono AppName.exe
    

关键注意事项

  • C++名字修饰:一定要用extern "C"包裹导出函数,否则C++编译器会对函数名进行特殊修饰(比如MyFunction会变成_Z11MyFunctioni),Mono会找不到对应的符号。
  • 数据类型兼容性:确保PInvoke签名中的数据类型和Linux下C++函数的类型完全匹配,比如Windows的long是4字节,Linux的long是8字节,这类差异要重点关注。
  • 字符串处理:Windows下常用宽字符(wchar_t),Linux下默认是UTF-8窄字符(char),如果函数涉及字符串传递,要统一编码格式,避免乱码或崩溃。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 07:45:03