Created
September 24, 2019 23:06
-
-
Save clarvalon/1b11a43e0e002cd7c6bae5df14f4782b to your computer and use it in GitHub Desktop.
FNA Net Core 3 using NativeLibrary (proof of concept v2)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// in Program.cs | |
// Get FNA Assembly | |
var fnaAssembly = Assembly.GetAssembly(typeof(Microsoft.Xna.Framework.Graphics.ColorWriteChannels)); | |
// Register mechanism for determining native libraries based on NativeLibrary instead of DllImport | |
DllMap.Register(fnaAssembly); | |
// in DllMap.cs | |
using System; | |
using System.Collections; | |
using System.IO; | |
using System.Linq; | |
using System.Reflection; | |
using System.Runtime.InteropServices; | |
using System.Xml.Linq; | |
public static class DllMap | |
{ | |
// Register a call-back for native library resolution. | |
public static void Register(Assembly assembly) | |
{ | |
NativeLibrary.SetDllImportResolver(assembly, MapAndLoad); | |
} | |
// The callback: which loads the mapped libray in place of the original | |
private static IntPtr MapAndLoad(string libraryName, Assembly assembly, DllImportSearchPath? dllImportSearchPath) | |
{ | |
string mappedName = null; | |
mappedName = MapLibraryName(assembly.Location, libraryName, out mappedName) ? mappedName : libraryName; | |
return NativeLibrary.Load(mappedName, assembly, dllImportSearchPath); | |
} | |
// Parse the assembly.xml file, and map the old name to the new name of a library. | |
private static bool MapLibraryName(string assemblyLocation, string originalLibName, out string mappedLibName) | |
{ | |
// TODO: Cache xml results to prevent costly loads? Store os and cpu also | |
// TODO: Remove Console.Writeline use | |
string os = GetCurrentPlatform().ToString().ToLowerInvariant(); | |
string cpu = GetCurrentRuntimeArchitecture().ToString().ToLowerInvariant(); | |
string xmlPath = Path.Combine(Path.GetDirectoryName(assemblyLocation), | |
Path.GetFileNameWithoutExtension(assemblyLocation) + ".dll.config"); | |
mappedLibName = null; | |
if (!File.Exists(xmlPath)) | |
{ | |
Console.WriteLine($"=== Cannot find XML"); | |
return false; | |
} | |
XElement root = XElement.Load(xmlPath); | |
var map = | |
(from el in root.Elements("dllmap") | |
where (string)el.Attribute("dll") == originalLibName | |
&& el.Attribute("os").ToString().IndexOf(os) >= 0 | |
&& (el.Attribute("cpu") == null || string.IsNullOrEmpty(cpu) || el.Attribute("cpu").ToString().IndexOf("cpu") >= 0) | |
select el).SingleOrDefault(); | |
if (map != null) | |
mappedLibName = map.Attribute("target").Value; | |
Console.WriteLine($"=== OS={os}, CPU={cpu}, Original={originalLibName}, Mapped={mappedLibName}"); | |
return (mappedLibName != null); | |
} | |
// Below pinched from Mono.DllMap project: https://github.com/Firwood-Software/AdvancedDLSupport/tree/1b7394211a655b2f77649ce3b610a3161215cbdc/Mono.DllMap | |
public static DllMapOS GetCurrentPlatform() | |
{ | |
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) | |
{ | |
return DllMapOS.Linux; | |
} | |
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) | |
{ | |
return DllMapOS.Windows; | |
} | |
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) | |
{ | |
return DllMapOS.OSX; | |
} | |
var operatingDesc = RuntimeInformation.OSDescription.ToUpperInvariant(); | |
foreach (var system in Enum.GetValues(typeof(DllMapOS)).Cast<DllMapOS>() | |
.Except(new[] { DllMapOS.Linux, DllMapOS.Windows, DllMapOS.OSX })) | |
{ | |
if (operatingDesc.Contains(system.ToString().ToUpperInvariant())) | |
{ | |
return system; | |
} | |
} | |
throw new PlatformNotSupportedException($"Couldn't detect platform: {RuntimeInformation.OSDescription}"); | |
} | |
public static DllMapArchitecture GetCurrentRuntimeArchitecture() | |
{ | |
switch (RuntimeInformation.ProcessArchitecture) | |
{ | |
case Architecture.Arm: | |
{ | |
return DllMapArchitecture.ARM; | |
} | |
case Architecture.X64: | |
{ | |
return DllMapArchitecture.x86_64; | |
} | |
case Architecture.X86: | |
{ | |
return DllMapArchitecture.x86; | |
} | |
} | |
typeof(object).Module.GetPEKind(out _, out var machine); | |
switch (machine) | |
{ | |
case ImageFileMachine.I386: | |
{ | |
return DllMapArchitecture.x86; | |
} | |
case ImageFileMachine.AMD64: | |
{ | |
return DllMapArchitecture.x86_64; | |
} | |
case ImageFileMachine.ARM: | |
{ | |
return DllMapArchitecture.ARM; | |
} | |
case ImageFileMachine.IA64: | |
{ | |
return DllMapArchitecture.IA64; | |
} | |
} | |
throw new PlatformNotSupportedException("Couldn't detect the current architecture."); | |
} | |
public enum DllMapOS | |
{ | |
Linux = 1 << 0, | |
OSX = 1 << 1, | |
Solaris = 1 << 2, | |
FreeBSD = 1 << 3, | |
OpenBSD = 1 << 4, | |
NetBSD = 1 << 5, | |
Windows = 1 << 6, | |
AIX = 1 << 7, | |
HPUX = 1 << 8 | |
} | |
public enum DllMapArchitecture | |
{ | |
x86 = 1 << 0, | |
x86_64 = 1 << 1, | |
SPARC = 1 << 2, | |
PPC = 1 << 3, | |
S390 = 1 << 4, | |
S390X = 1 << 5, | |
ARM = 1 << 6, | |
ARMV8 = 1 << 7, | |
MIPS = 1 << 8, | |
Alpha = 1 << 9, | |
HPPA = 1 << 10, | |
IA64 = 1 << 11 | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment