1. 程式人生 > >.netcore在linux下使用P/invoke方式調用linux動態庫

.netcore在linux下使用P/invoke方式調用linux動態庫

glob glibc glibcxx pointer ngs 得到 bst view ons

.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)]
View Code

正常情況下,都是可以調用成功的

如果出現調用失敗的情況,可能是so文件缺少了一些依賴文件,可以通過ldd命令進行查看

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命令查找是否確實缺少了依賴

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命令來查找依賴文件

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

技術分享圖片

如果能找到依賴的so文件,可以使用cp命令將文件復制到lib64目錄

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

Centos下系統目錄是/usr/lib64,Suse下可能系統目錄會有不同

如果有舊文件,可以使用rm命令,先刪除舊文件

sudo rm -rf /usr/lib64/libstdc++.so.6  //刪除舊文件

最後在使用ln命令,鏈接到新文件

sudo ln -s /usr/lib64/libstdc++.so.6.0.20 /usr/lib64/libstdc++.so.6 //鏈接到新版本

這些都做好之後,舊可以測試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     }
View Code

.netcore在linux下使用P/invoke方式調用linux動態庫