C# 引用C/C++ DLL的几种方式(linux篇)

书接上回《C# 引用C/C++ DLL的几种方式(windows篇)》,本文将继续介绍在linux下通过C#调用c/c++ dll的方法。通过这篇文章,我们将获得一个可以同时在visual studio和linux下编译的c语言文件和一个可以同时在windows和linux下调用c dll的.net工程

源码下载

一、准备

修改CDLL工程文件

1.修改test.h文件,使其能够在linux下被编译成.so文件

test.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#pragma once
#ifdef ENV_WINDOWS
_declspec(dllexport) int Add(int a, int b);
#else

#ifdef __cplusplus
extern "C" {
#endif

int Add(int a, int b);

#ifdef __cplusplus
}
#endif

#endif

2.修改VisualStudio中CDLL工程属性,添加ENV_WINDOWS宏定义使之可以继续生成windows下可以被调用的c dll

3.将test.c和test.h复制到linux上,并进行编译,生成cdll.so

1
$ gcc -fPIC -shared test.c -o cdll.so

二、通过libdl.so动态加载dll

介绍

和windows下的kernel32类似,libdl.so是linux提供的一个动态加载/卸载/调用dll方法的工具。
libdl.so提供了以下接口:

方法 介绍
dlopen 打开动态共享目标文件并将其映射到内存中,返回其首地址
dlclose 关闭动态共享文件
dlsym 返回请求的入口点的指针
dlerror 返回NULL或者指向描述最近错误的字符串

使用

向CSharpTest工程中添加DL3.cs文件,并编辑

DL3.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class DL3
{
[DllImport("libdl.so.2", EntryPoint = "dlopen")]
private static extern IntPtr UnixLoadLibrary(String fileName, int flags);

[DllImport("libdl.so.2", EntryPoint = "dlclose", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern int UnixFreeLibrary(IntPtr handle);

[DllImport("libdl.so.2", EntryPoint = "dlsym", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern IntPtr UnixGetProcAddress(IntPtr handle, String symbol);

[DllImport("libdl.so.2", EntryPoint = "dlerror", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern IntPtr UnixGetLastError();

[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate int tAdd(int a, int b);

public static void TestDllImport()
{
var ptr = UnixLoadLibrary("./cdll.so", 2);
var ptr_Add = UnixGetProcAddress(ptr, "Add");
tAdd add = (tAdd)Marshal.GetDelegateForFunctionPointer(ptr_Add, typeof(tAdd));
int res2 = add.Invoke(3, 4);
Console.WriteLine(res2);
UnixFreeLibrary(ptr);
}
}

修改Program.cs,使之可以兼容linux和windows两种环境,并根据环境调用不同的dll

Program.cs
1
2
3
4
5
6
7
8
9
10
11
static void Main(string[] args)
{
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
{
DL2.TestDllImport();
DL1.TestDllImport();
}
if (Environment.OSVersion.Platform == PlatformID.Unix)
DL3.TestDllImport();
Console.WriteLine("Hello, World!");
}

编译Linux下的CSharpTest程序,并将其放到linux环境下运行,查看输出(记得把cdll.so放到可执行文件目录下)

至此,我们已经实现了这个C#程序同时兼容windows和linux下调用C/C++ DLL方法。

注: 这里我开始写的是’[DllImport(“libdl.so”, EntryPoint = “dlopen”)]’,结果报错”Unhandled exception. System.DllNotFoundException: Unable to load shared library ‘libdl.so’ or one of its dependencies…” 。后来经过尝试,将”libdl.so”改为”libdl.so.2”后成功调用。也许你看到这篇文章的时候需要改成”libdl.so.3”或者”libdl.so.fucklinux”什么的,总之,祝你好运!