MS_UnmanagedCodeCrossPlatform - NetDevInfraWGinOSSConsortium/NetDevInfraWiki GitHub Wiki
- æ»ãïŒ.NETã®ã¯ãã¹ãã©ãããã©ãŒã 察å¿ïŒ
-
Windows ã§äœ¿çšã§ããŠãã P/Invoke ã¯ã
.NET CoreãLinux ã§ãå©çšå¯èœã§ããæš¡æ§ã -
ããã§ã¯ãã¢ã³ãããŒãžãã³ãŒãã P/Invoke ããŠã
ã©ã®ããã« .NET Core ãã¯ãã¹ãã©ãããã©ãŒã 察å¿ãããã®ãïŒ
è£è¶³ïŒP/Invoke 㯠Linux ã§ãåã ââ ä»çµã¿ïŒ: åæã®ãå©çšå¯èœã§ããæš¡æ§ã
ã¯æ£ããããªãåãã®ããè£ã£ãŠããããP/Invoke ã®å®äœã â æå®ãããååã®ãã€ãã£ã ã©ã€ãã©ãªãæ¢ããŠããŒããã â¡ ãã®äžã®é¢æ°ã®ã¢ãã¬ã¹ãååŸãã ⢠ãããŒãžãïŒã¢ã³ãããŒãžãã®å¢çã§åŒæ°ã倿ããŠåŒã¶ ãOS ããšã®å·®ã¯ â â¡ ã ããâ Windows: LoadLibrary / GetProcAddress â xxx.dll Linux : dlopen / dlsym â libxxx.so macOS : dlopen / dlsym â libxxx.dylib â CLR ããå OS ã® API ãåŒã³åãããã®ã§ã C# åŽã®ã³ãŒãã¯åãã§ããã©ã€ãã©ãªåã®è§£æ±ºèŠåãéèŠã§ããã
[DllImport("mylib")] // â æ¡åŒµåã»æ¥é èŸãæžããªã â static extern int Foo();ã.NET ã詊ãååã Windows: mylib.dll Linux : libmylib.so, mylib.so, libmylib, mylib macOS : libmylib.dylib, mylib.dylib, ⊠â ãæ¡åŒµåãæžããšç§»æ€æ§ãèœã¡ãã â "mylib.dll" ãšæžããšãLinux ã§ã "mylib.dll" ãæ¢ãã«è¡ã ïŒèŠã€ããã DllNotFoundExceptionïŒããæè»ã«è§£æ±ºãããå ŽåïŒ.NET Core 3.0 以éïŒ:
// å®è¡æã«è§£æ±ºæ¹æ³ãå·®ã蟌ãã NativeLibrary.SetDllImportResolver(typeof(Program).Assembly, (name, asm, path) => name == "mylib" && RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? NativeLibrary.Load("/opt/myapp/libmylib.so.1") : IntPtr.Zero);
-
å ããDLL ã SO ãªã©ã®ã¢ã³ãããŒãžã»ã©ã€ãã©ãªãäœæããã
-
次ãã§ãäžèšã®ã¢ã³ãããŒãžã»ã©ã€ãã©ãªãè¥ããã¯ã
Win32 API ã syscall, libc, etc. ãåŒåºããP/Invoke ã©ã€ãã©ãªãäœæããã -
ã€ã«ãšããã°ããããªæããã
-
POSIX
...ãšèšãåå空éã«åå²ããã®ãè¯ãããã
https://github.com/OpenTouryoProject/OpenTouryo/tree/develop/root/programs/CS/Frameworks/Infrastructure/Public/POSIX
è£è¶³ïŒåå空éã§åããæ¡ã¯åŠ¥åœïŒ: ã
Win32/POSIXãšããåå空éã«
åå²ããããšããåæã®æ¡ã¯ãèšèšãšããŠæ£ãããããªãåããã®ãã ã»P/Invoke ã®å®£èšã¯ãOS äŸåãã®ãã®ãã§ãã â ã·ã°ããã£ãæ§é äœã®ã¬ã€ã¢ãŠãããšã©ãŒã³ãŒããéã ã»æ··åšããããšãã©ã¡ãã® OS çšãåãããªããªã ã»ãçæ¹ã® OS ã§ãããã¹ãã§ããªãã³ãŒããã å ±éã®åå空éã«çŽãã ãåãããšã ã»ããã®åå空é㯠Windows å°çšããšæç€ºã§ãã ã»æ¡ä»¶ä»ãã³ã³ãã€ã«ããã©ãããã©ãŒã 屿§ãä»ããããçŸåšã¯å±æ§ã§æç€ºã§ããïŒ.NET 5 以éïŒã
[SupportedOSPlatform("windows")] // â ã¢ãã©ã€ã¶ãŒãèŠåããŠããã â internal static class Win32 { [DllImport("kernel32.dll", SetLastError = true)] internal static extern bool Beep(uint dwFreq, uint dwDuration); } [SupportedOSPlatform("linux")] [SupportedOSPlatform("macos")] internal static class Posix { [DllImport("libc", SetLastError = true)] internal static extern int chmod(string path, int mode); }ãSupportedOSPlatform ã®äŸ¡å€ãâ ã»å¯Ÿå¿å€ã® OS åããã«ãã§äœ¿ããšãã³ã³ãã€ã«æã«èŠåããåºã ã»OperatingSystem.IsWindows() ã§å²ããšèŠåãæ¶ãã â å岿Œããéçã«æ€åºã§ãã
libcãçŽæ¥åŒã¶éã®æ³šæ:ã»libc ã®é¢æ°ã¯ãglibc / musl ã§æåãã·ã³ãã«ãéããããšããã â Alpine LinuxïŒmuslïŒã§åããªãããšããäºæ ãèµ·ãã â ã»æ§é äœã®ã¬ã€ã¢ãŠãã¯ã32bit / 64bitãABI ã§å€ããã ã»errno ã®ååŸã¯ SetLastError = true + Marshal.GetLastWin32Error() ïŒåå㯠Win32 ã ããLinux ã§ã errno ãåããïŒ ã»å¯èœãªãã.NET ã®æšæº API ã§æžãŸããã â File.SetUnixFileModeïŒ.NET 7 以éïŒçã P/Invoke ãªãã§æ±ããç¯å²ãåºãã£ãŠãã â
- ã©ãããŒã»ã©ã€ãã©ãªãããP/Invoke ã©ã€ãã©ãªãåŒã³åºãã
- ã©ãããŒã»ã©ã€ãã©ãªã¯ãããŒãžãã³ãŒãäžã§ã€ã³ã¿ãŒãã§ã€ã¹ãå®çŸ©ã
Environment.OSVersion ã䜿çšããŠåå²ãããã
ç§»è¡ã¡ã¢ïŒ
Environment.OSVersionã§ã®åå²ã¯çŸåšã¯éæšå¥šïŒ: ãã®æ¹æ³ã¯
.NET Framework æã®æžãæ¹ã§ããã
çŸåšã¯ããæç¢ºãª API ãããã// â æ§æ¥ã®æ¹æ³ïŒPlatformID 㯠Unix ãš MacOSX ã®åºå¥ãææ§çã®åé¡ãããïŒ if (Environment.OSVersion.Platform == PlatformID.Win32NT) { } // â .NET Core 以éïŒæšå¥šïŒ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { } // â .NET 5 以éïŒæãç°¡æœãã¢ãã©ã€ã¶ãŒãšãé£åããïŒâ if (OperatingSystem.IsWindows()) { } else if (OperatingSystem.IsLinux()) { } else if (OperatingSystem.IsMacOS()) { } // çãŸã§å€å®ãã if (OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17763)) { }ãOperatingSystem.IsXxx() ãåªããŠããç¹ãâ ã»[SupportedOSPlatform] 屿§ã®ã¢ãã©ã€ã¶ãŒã ããã®åå²ãçè§£ããã â åå²ã®äžã§ã ã Windows å°çš API ãåŒã¹ã°èŠåãæ¶ãã ã»ããªãã³ã°æã«ãäžèŠãªåå²ãåããã â Native AOT ã§æå©ãEnvironment.OSVersion ã®èœãšã穎ã ã»Windows ã§ã¯ãäºææ§ãããã§ã¹ãããªããšå€ãçãè¿ããããšããã ã»Platform ã Unix ã«ãªã OS ãè€æ°ããïŒLinux / macOS / FreeBSDïŒ â ãæ°èŠã³ãŒãã§ã¯äœ¿ããªãã
-
ã©ãããŒã»ã©ã€ãã©ãªå éšã§ã
Environment.OSVersion ã䜿çšããŠåå²ãããã -
ã©ãããŒã»ã©ã€ãã©ãªã WindowsãLinux ã®ïŒç³»çµ±ãäœæããŠã
åŒã³åºãå ã§ Environment.OSVersion ã䜿çšããŠåå²ãããã
â» æå·åã©ã€ãã©ãªã§ã¯ãå
éšã§
ãCAPI(CSP)ãCNGãOpenSSL ãªã©ã®ãããã€ããåãæ¿ããããã®ã§ãåºæ¬ã
ãåè
ã®ããã«ãã©ãããŒã»ã©ã€ãã©ãªå
éšã§åå²ãããã®ãè¯ããšèããã
è£è¶³ïŒãã®å€æã¯æ£ãã ââ .NET èªèº«ãåãèšèšãããŠããïŒ: åæã®çµè«
ãã©ãããŒå éšã§åå²ããããã¯åŠ¥åœã§ã
.NET ã®ã©ã³ã¿ã€ã èªäœããŸãã«ãã®èšèšã§ãããã.NET ã®æå·å API ã®æ§é ã System.Security.Cryptography.Aes.Create() â å©çšåŽã¯ OS ãæèããªã â ⌠å éšã§åå² Windows â CNG / CAPIïŒBcryptïŒ Linux â OpenSSL macOS â CommonCrypto / SecurityFramework â [.NETã®çœ²åã»æå·åã¢ã«ãŽãªãºã ](MS_DotNetCryptoAlgorithms) / [OpenSSL](MS_OpenSSL) åç §ãªãå éšã§åå²ãããæ¹ãè¯ãã®ã:
å éšã§åå²ïŒæšå¥šïŒ å€éšã§åå² å©çšåŽã®ã³ãŒã OS ãæèããªãïŒ1 æ¬ïŒ â OS ããšã«æžã 远å OS ãžã®å¯Ÿå¿ ã©ãããŒã ãçŽã å šåŒã³åºãå ãçŽã â ãã¹ã ã©ãããŒã®ãã¹ãã«éçŽ åŒã³åºãå ããšã«èŠã äŸåã®åã å©çšåŽ â æœè±¡ å©çšåŽ â å ·äœïŒOSïŒ ããã㯠[äŸåæ§å転åå](MS_DependencyInversionPrinciple) ãã®ãã®ãâ ã»å©çšåŽã¯ãæœè±¡ïŒã€ã³ã¿ãŒãã§ã€ã¹ïŒãã«äŸåãã ã»OS åºæã®å®è£ ã¯ãæœè±¡ã®è£åŽãã«é ã â åæã®ãã©ãããŒã»ã©ã€ãã©ãªã¯ãããŒãžãã³ãŒãäžã§ ã€ã³ã¿ãŒãã§ã€ã¹ãå®çŸ©ããšããèšè¿°ãããŸãã«ããå®è£ ã®åœ¢:
// æœè±¡ public interface IFilePermission { void SetReadOnly(string path); } // å®è£ ïŒå éšã§åå²ããŸã㯠DI ã§å·®ãæ¿ãïŒ public static class FilePermission { public static void SetReadOnly(string path) { if (OperatingSystem.IsWindows()) Win32.SetFileAttributes(path, /* ... */); else Posix.chmod(path, 0x124 /* 0444 */); } }ãããäžæ®µè¯ãèšèšã OS å€å®ããäžèŠã«ã§ããªãããå ã«æ€èšãã â // .NET 7 以éïŒæšæº API ã§æžã File.SetUnixFileMode(path, UnixFileMode.UserRead | UnixFileMode.GroupRead); new FileInfo(path).IsReadOnly = true; â ãP/Invoke ãæžããªãã®ãæåã â æšæº API ã§è¶³ããããå¿ ã確èªãã
è£è¶³ïŒãã€ãã£ã ã©ã€ãã©ãªã®é åžïŒ: åæãæ±ã£ãŠããªã
å®åäžã®é£æãè£ã£ãŠããã**ãã©ããã£ãŠ .so ãé ããã**ã§ããããåé¡ã ã»Windows åãã«ã¯ mylib.dllãLinux åãã«ã¯ libmylib.so ãèŠã ã»ããã«ãCPU ã¢ãŒããã¯ãã£ãããšã«å¥ïŒx64 / arm64ïŒ ã»å©çšè ã«ãäºåã«å ¥ããŠãããŠããšã¯èšãã«ããNuGet ã®
runtimesãã©ã«ãèŠçŽã§è§£æ±ºã§ãããMyPackage.nupkg â lib/net8.0/MyWrapper.dll â ãããŒãžãã®ã©ãã㌠â runtimes/ â win-x64/native/mylib.dll â win-arm64/native/mylib.dll â linux-x64/native/libmylib.so â â linux-arm64/native/libmylib.so â osx-arm64/native/libmylib.dylib â dotnet publish -r linux-x64 ãšãããš ã該åœãã native ã ããåºåãããã â SkiaSharpãMicrosoft.Data.SqlClient çããã®æ¹åŒãã³ã³ããã§ã®æ³šæã ã»AlpineïŒmuslïŒåã㯠linux-musl-x64 ãšå¥ RID ã«ãªã â ã»äŸåããå ±æã©ã€ãã©ãªïŒlibssl çïŒã ã€ã¡ãŒãžã«å ¥ã£ãŠãããã確èªãã â ãéçºæ©ã§ã¯åãã®ã«ã³ã³ããã§ DllNotFoundExceptionã ã®å žåçãªåå ã»ldd libmylib.so ã§äŸåã確èªã§ãã
DllNotFoundExceptionã®åãåã:â ãã¡ã€ã«ã¯é 眮ãããŠãããïŒpublish åºåã確èªïŒ â¡ ååã¯æ£ãããïŒlib æ¥é èŸãæ¡åŒµåïŒ â¢ äŸåããå ±æã©ã€ãã©ãªã¯æã£ãŠãããïŒlddïŒ â£ ã¢ãŒããã¯ãã£ã¯äžèŽããŠãããïŒfile ã³ãã³ãïŒ â€ å®è¡æš©éã¯ãããïŒLinuxïŒ ããããã°çšã®ç°å¢å€æ°ã DOTNET_EnableWriteXorExecute=0 LD_DEBUG=libs ./myapp â Linux ã§ããŒãã®éçšãèŠãã â
- ãã€ãã£ã ã©ã€ãã©ãªã®èªã¿èŸŒã¿ïŒNativeLibraryïŒ
https://learn.microsoft.com/ja-jp/dotnet/standard/native-interop/native-library-loading - ãã©ãããã©ãŒã äºææ§ã¢ãã©ã€ã¶ãŒ
https://learn.microsoft.com/ja-jp/dotnet/standard/analyzers/platform-compat-analyzer - ã©ã³ã¿ã€ã èå¥åïŒRIDïŒã®ã«ã¿ãã°
https://learn.microsoft.com/ja-jp/dotnet/core/rid-catalog - ãœãŒã¹çæããã P/InvokeïŒLibraryImportïŒ
https://learn.microsoft.com/ja-jp/dotnet/standard/native-interop/pinvoke-source-generation
Tags: ç§»è¡, Windows, ããã°ã©ãã³ã°, .NETéçº