首页 > Web开发 > 详细

.netcore在linux下使用P/invoke方式调用linux动态库

时间:2019-04-04 14:10:26      阅读:229      评论:0      收藏:0      [点我收藏+]
 

.netcore下已经实现了通过p/invoke方式调用linux的动态链接库(*.so)文件

技术分享图片
技术分享图片
 1 [DllImport(@"libdl.so.2")]
 2         public static extern IntPtr dlopen(string filename, int flags);
 3         [DllImport("libdl.so.2")]
 4         public static extern IntPtr dlsym(IntPtr handle, string symbol);
 5 
 6         [DllImport("libdl.so.2", EntryPoint = "dlopen")]
 7         private static extern IntPtr UnixLoadLibrary(String fileName, int flags);
 8 
 9         [DllImport("libdl.so.2", EntryPoint = "dlclose", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
10         private static extern int UnixFreeLibrary(IntPtr handle);
11 
12         [DllImport("libdl.so.2", EntryPoint = "dlsym", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
13         private static extern IntPtr UnixGetProcAddress(IntPtr handle, String symbol);
14 
15         [DllImport("libdl.so.2", EntryPoint = "dlerror", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
技术分享图片

正常情况下,都是可以调用成功的

如果出现调用失败的情况,可能是so文件缺少了一些依赖文件,可以通过ldd命令进行查看

1
ldd libzmq.so

技术分享图片

如果有某些依赖文件找不到,会出现not found的字样,比如下面这种

/usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.20‘ not found (required by */3rd-party/protobuf-2.4.1/src/.libs/libprotobuf.so.7)
/usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.20‘ not found (required by */3rd-party/protobuf-2.4.1/src/.libs/libprotoc.so.7)

可以使用string命令查找是否确实缺少了依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
strings /usr/lib64/libstdc++.so.6 |grep GLIBCXX 得到结果
 
GLIBCXX_3.4
GLIBCXX_3.4.1
GLIBCXX_3.4.2
GLIBCXX_3.4.3
GLIBCXX_3.4.4
GLIBCXX_3.4.5
GLIBCXX_3.4.6
GLIBCXX_3.4.7
GLIBCXX_3.4.8
GLIBCXX_3.4.9
GLIBCXX_3.4.10
GLIBCXX_3.4.11
GLIBCXX_3.4.12
GLIBCXX_3.4.13
GLIBCXX_3.4.14
GLIBCXX_3.4.15
GLIBCXX_3.4.16
GLIBCXX_3.4.17
GLIBCXX_DEBUG_MESSAGE_LENGTH

确实缺少了文件,这种情况下,我们需要使用find命令来查找依赖文件

1
find / -name libstdc++.so.6*

技术分享图片

如果能找到依赖的so文件,可以使用cp命令将文件复制到lib64目录

1
cp /usr/local/lib64/libstdc++.so.6.0.20 /usr/lib64 //复制文件

Centos下系统目录是/usr/lib64,Suse下可能系统目录会有不同

如果有旧文件,可以使用rm命令,先删除旧文件

1
sudo rm -rf /usr/lib64/libstdc++.so.6  //删除旧文件

最后在使用ln命令,链接到新文件

1
sudo ln -s /usr/lib64/libstdc++.so.6.0.20 /usr/lib64/libstdc++.so.6 //链接到新版本 (libstdc++.so.6.0.20是复制到linux中的文件的文件名)

这些都做好之后,旧可以测试dlopen命令是否能正常打开文件了,如果可以正常打开,那dllimport方式就可以正常使用

没有开发dllimport的源码,很怀疑它内部也是调用了linux下的dlopen命令来调用so文件

技术分享图片

除了直接使用dllimport方式来调用,还可以使用委托的方式,来调用so文件

下面是测试代码,可以比较完整说明.netcore下p/invoke方式调用so文件

技术分享图片
技术分享图片
 1 public class SoTester
 2     {
 3         private const string LibraryName = "libzmq";
 4 
 5         const int RTLD_NOW = 2; // for dlopen‘s flags
 6         const int RTLD_GLOBAL = 8;
 7 
 8         [DllImport(@"libdl.so.2")]
 9         public static extern IntPtr dlopen(string filename, int flags);
10         [DllImport("libdl.so.2")]
11         public static extern IntPtr dlsym(IntPtr handle, string symbol);
12 
13         [DllImport("libdl.so.2", EntryPoint = "dlopen")]
14         private static extern IntPtr UnixLoadLibrary(String fileName, int flags);
15 
16         [DllImport("libdl.so.2", EntryPoint = "dlclose", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
17         private static extern int UnixFreeLibrary(IntPtr handle);
18 
19         [DllImport("libdl.so.2", EntryPoint = "dlsym", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
20         private static extern IntPtr UnixGetProcAddress(IntPtr handle, String symbol);
21 
22         [DllImport("libdl.so.2", EntryPoint = "dlerror", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
23         private static extern IntPtr UnixGetLastError();
24 
25         public delegate int sumHandler(int a, int b);
26         public static sumHandler sumfunc = null;
27 
28         [DllImport("libNativeLib.so", EntryPoint = "sum", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
29         public static extern int Sum(int a, int b);
30 
31         public void Start()
32         {
33             IntPtr libPtr = IntPtr.Zero;
34 
35             string libName = $"{AppContext.BaseDirectory}libNativeLib.so";
36 
37             libPtr = UnixLoadLibrary(libName, 2 | 8);
38 
39             //libPtr = dlopen(libName, RTLD_NOW);
40 
41             if (libPtr != IntPtr.Zero)
42                 Console.WriteLine($"调用dlopen打开{libName}成功");
43             else
44                 Console.WriteLine($"调用dlopen打开{libName}失败");
45 
46             var sumPtr = UnixGetProcAddress(libPtr, "sum");
47 
48             if (sumPtr != IntPtr.Zero)
49                 Console.WriteLine($"dlopen调用sum成功");
50             else
51                 Console.WriteLine($"dlopen调用sum失败");
52 
53             sumfunc = Marshal.GetDelegateForFunctionPointer<sumHandler>(sumPtr);
54 
55             int ret = sumfunc(1, 3);
56 
57             Console.WriteLine($"调用sum结果:{ret}");
58 
59             var sumRet = Sum(5, 7);
60 
61             Console.WriteLine($"DllImport调用sum结果:{sumRet}");
62 
63             //var libname2 = $"libc.so.6";
64             var libname2 = $"{AppContext.BaseDirectory}libzmq.so";
65             //var libname2 = $"{AppContext.BaseDirectory}libAdminConsole.so";
66             var consolePtr = UnixLoadLibrary(libname2, 2 | 8);
67             var erroPtr = UnixGetLastError();
68             Console.WriteLine($"错误描述:{Marshal.PtrToStringAnsi(erroPtr)}");
69 
70             if (consolePtr != IntPtr.Zero)
71                 Console.WriteLine($"打开{libname2}成功");
72             else
73                 Console.WriteLine($"打开{libname2}失败");
74         }
75     }
技术分享图片

 ps:目前测试过CentOS7和suse12 SP3,个人感觉,如果项目中需要使用P/INVOKE最好还是放在suse上跑,centos默认的gcc版本比较低,升级gcc又很麻烦。suse各个组件齐全,只是免费版无法更新组件,p/invoke直接可以使用。其他版本linux没有使用。

 

 
 

.netcore在linux下使用P/invoke方式调用linux动态库

原文:https://www.cnblogs.com/kelelipeng/p/10654315.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!