Skip to content

Instantly share code, notes, and snippets.

@Aurumaker72
Created October 27, 2023 09:48
Show Gist options
  • Select an option

  • Save Aurumaker72/72b5fcc4b43cbd2f2e3cc5b91465d610 to your computer and use it in GitHub Desktop.

Select an option

Save Aurumaker72/72b5fcc4b43cbd2f2e3cc5b91465d610 to your computer and use it in GitHub Desktop.
using System.Runtime.InteropServices;
namespace M64RPFW.Models.Helpers;
/// <summary>
/// Marks a pseudo-<see cref="DllImportAttribute"/>ed function resolved at runtime.
///
/// <para>
/// To register correctly, a <c>RuntimeDllImport</c>ed delegate must either
/// set the <see cref="Name"/> property or start with a capital <c>D</c>.
/// </para>
/// </summary>
[AttributeUsage(AttributeTargets.Delegate)]
public class RuntimeDllImportAttribute : Attribute
{
/// <summary>
/// The name of the function to import. If it is not set and the
/// delegate's name begins with a capital <c>D</c>, then an automatic
/// name will be generated by removing the first character.
/// </summary>
public string? Name { get; init; }
}
public static class NativeLibHelper
{
/// <summary>
/// Tries to get a function from a library.
/// </summary>
/// <param name="lib">The library</param>
/// <param name="name">The name of the function</param>
/// <typeparam name="T">A delegate type to call the function</typeparam>
/// <exception cref="EntryPointNotFoundException">If the function can't be found.</exception>
/// <returns></returns>
public static T? GetFunction<T>(IntPtr lib, string name)
{
IntPtr res = NativeLibrary.GetExport(lib, name);
return (res == IntPtr.Zero)? default : Marshal.GetDelegateForFunctionPointer<T>(res);
}
/// <summary>
/// Resolve a delegate tagged with <see cref="RuntimeDllImportAttribute"/>.
/// </summary>
/// <param name="lib">the library to resolve symbols from</param>
/// <param name="del">the delegate to set</param>
/// <typeparam name="T">the delegate to resolve to</typeparam>
/// <returns>the delegate</returns>
/// <exception cref="ArgumentException">If the delegate type is not tagged correctly</exception>
/// <seealso cref="RuntimeDllImportAttribute"/>
public static void ResolveDelegate<T>(IntPtr lib, out T del) where T : Delegate
{
Attribute[] attrs = Attribute.GetCustomAttributes(typeof(T));
var rtDllImport = (RuntimeDllImportAttribute?)
attrs.FirstOrDefault(a => a is RuntimeDllImportAttribute, null);
if (rtDllImport is null)
{
throw new ArgumentException(
$"Type T ({typeof(T).FullName}) is not tagged with {typeof(RuntimeDllImportAttribute).FullName}");
}
string symbol;
if (rtDllImport.Name is null)
{
string name = typeof(T).Name;
if (name[0] != 'D')
{
throw new ArgumentException("Type does not match automatic delegate typename");
}
symbol = name.Substring(1);
}
else
{
symbol = rtDllImport.Name;
}
T? val = GetFunction<T>(lib, symbol);
if (val == null)
throw new ApplicationException("Failed to load function");
del = val;
}
public static string AsDLL(string name)
{
return $"{name}{LibraryExtension}";
}
private static string? _platformLibExtension;
public static string LibraryExtension
{
get
{
if (_platformLibExtension != null)
return _platformLibExtension;
if (OperatingSystem.IsWindows())
return _platformLibExtension = ".dll";
if (OperatingSystem.IsLinux())
return _platformLibExtension = ".so";
throw new PlatformNotSupportedException("Only Windows and Linux are supported at the moment");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment