Skip to content

Instantly share code, notes, and snippets.

@kb10uy
Last active October 3, 2024 12:31
Show Gist options
  • Save kb10uy/37a0f0330a719e16c1323fdb8cbdef15 to your computer and use it in GitHub Desktop.
Save kb10uy/37a0f0330a719e16c1323fdb8cbdef15 to your computer and use it in GitHub Desktop.
A generic script for UnityEditor that loads dynamic library *dynamically*. In other words, it can avoid the Unity's "never unread" restriction.
/*
DynamicNativeLibrary.cs by kb10uy
This code is licensed under the CC0-1.0.
You should have received a copy of the CC0 legalcode along with this work.
If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
*/
using System;
using System.Runtime.InteropServices;
namespace KusakaFactory.Dylib
{
public sealed class DynamicNativeLibrary : SafeHandle
{
public override bool IsInvalid => handle == IntPtr.Zero;
private DynamicNativeLibrary(IntPtr moduleHandle) : base(IntPtr.Zero, true)
{
SetHandle(moduleHandle);
}
protected override bool ReleaseHandle()
{
return GenericClose(handle);
}
public static DynamicNativeLibrary Load(string libraryPath)
{
var moduleHandle = GenericOpen(libraryPath);
if (moduleHandle == IntPtr.Zero)
{
throw new ArgumentException($"failed to load dynamic library '{libraryPath}'");
}
return new DynamicNativeLibrary(moduleHandle);
}
public TDelegate GetFunction<TDelegate>(string functionName) where TDelegate : Delegate
{
var functionPointer = GenericSymbolAddress(handle, functionName);
if (functionPointer == IntPtr.Zero)
{
throw new ArgumentException($"failed to get function address '{functionName}'");
}
return Marshal.GetDelegateForFunctionPointer<TDelegate>(functionPointer);
}
#if UNITY_EDITOR_WIN
private static IntPtr GenericOpen(string filename) => NativeMethods.LoadLibrary(filename);
private static bool GenericClose(IntPtr handle) => NativeMethods.FreeLibrary(handle);
private static IntPtr GenericSymbolAddress(IntPtr handle, string symbol) => NativeMethods.GetProcAddress(handle, symbol);
#elif UNITY_EDITOR_LINUX || UNITY_EDITOR_OSX
private static IntPtr Open(string filename) => NativeMethods.dlopen(filename, NativeMethods.RTLD_LAZY);
private static bool Close(IntPtr handle) => NativeMethods.dlclose(handle) == 0;
private static IntPtr GetSymbolAddress(IntPtr handle, string symbol) => NativeMethods.dlsym(handle, symbol);
#endif
private static class NativeMethods
{
#if UNITY_EDITOR_WIN
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
public extern static IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPWStr)] string filename);
[DllImport("kernel32.dll")]
public extern static bool FreeLibrary(IntPtr handle);
[DllImport("kernel32.dll")]
public extern static IntPtr GetProcAddress(IntPtr handle, [MarshalAs(UnmanagedType.LPStr)] string procName);
#elif UNITY_EDITOR_LINUX
const int RTLD_LAZY = 1;
[DllImport("libdl.so.2")]
public extern static IntPtr dlopen(string filename, int flags);
[DllImport("libdl.so.2")]
public extern static int dlclose(IntPtr handle);
[DllImport("libdl.so.2")]
public extern static IntPtr dlsym(IntPtr handle, string symbol);
#elif UNITY_EDITOR_OSX
const int RTLD_LAZY = 1;
[DllImport("libdl.dylib")]
public extern static IntPtr dlopen(string filename, int flags);
[DllImport("libdl.dylib")]
public extern static int dlclose(IntPtr handle);
[DllImport("libdl.dylib")]
public extern static IntPtr dlsym(IntPtr handle, string symbol);
#endif
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment