在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 .. makebuild目录下,命名为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
相关产品推荐
相关产品推荐

