.netcore在linux下使用P/invoke方式調用linux動態庫
.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命令進行查看
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動態庫