forked from MonoMod/MonoMod.Common
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDynDll.cs
157 lines (131 loc) · 6.68 KB
/
DynDll.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
using System.Reflection;
using System.Runtime.InteropServices;
using System;
using System.Collections.Generic;
using System.IO;
using System.ComponentModel;
using System.Linq;
namespace MonoMod.Utils {
#if !MONOMOD_INTERNAL
public
#endif
static partial class DynDll {
/// <summary>
/// Allows you to remap library paths / names and specify loading flags. Useful for cross-platform compatibility. Applies only to DynDll.
/// </summary>
public static Dictionary<string, List<DynDllMapping>> Mappings = new Dictionary<string, List<DynDllMapping>>();
/// <summary>
/// Extension method wrapping Marshal.GetDelegateForFunctionPointer
/// </summary>
public static T AsDelegate<T>(this IntPtr s) where T : class {
#pragma warning disable CS0618 // Type or member is obsolete
return Marshal.GetDelegateForFunctionPointer(s, typeof(T)) as T;
#pragma warning restore CS0618 // Type or member is obsolete
}
/// <summary>
/// Fill all static delegate fields with the DynDllImport attribute.
/// Call this early on in the static constructor.
/// </summary>
/// <param name="type">The type containing the DynDllImport delegate fields.</param>
/// <param name="mappings">Any optional mappings similar to the static mappings.</param>
public static void ResolveDynDllImports(this Type type, Dictionary<string, List<DynDllMapping>> mappings = null)
=> InternalResolveDynDllImports(type, null, mappings);
/// <summary>
/// Fill all instance delegate fields with the DynDllImport attribute.
/// Call this early on in the constructor.
/// </summary>
/// <param name="instance">An instance of a type containing the DynDllImport delegate fields.</param>
/// <param name="mappings">Any optional mappings similar to the static mappings.</param>
public static void ResolveDynDllImports(object instance, Dictionary<string, List<DynDllMapping>> mappings = null)
=> InternalResolveDynDllImports(instance.GetType(), instance, mappings);
private static void InternalResolveDynDllImports(Type type, object instance, Dictionary<string, List<DynDllMapping>> mappings) {
BindingFlags fieldFlags = BindingFlags.Public | BindingFlags.NonPublic;
if (instance == null)
fieldFlags |= BindingFlags.Static;
else
fieldFlags |= BindingFlags.Instance;
foreach (FieldInfo field in type.GetFields(fieldFlags)) {
bool found = true;
foreach (DynDllImportAttribute attrib in field.GetCustomAttributes(typeof(DynDllImportAttribute), true)) {
found = false;
IntPtr libraryPtr = IntPtr.Zero;
if (mappings != null && mappings.TryGetValue(attrib.LibraryName, out List<DynDllMapping> mappingList)) {
bool mappingFound = false;
foreach (var mapping in mappingList) {
if (TryOpenLibrary(mapping.LibraryName, out libraryPtr, true, mapping.Flags)) {
mappingFound = true;
break;
}
}
if (!mappingFound)
continue;
} else {
if (!TryOpenLibrary(attrib.LibraryName, out libraryPtr))
continue;
}
foreach (string entryPoint in attrib.EntryPoints.Concat(new[] { field.Name, field.FieldType.Name })) {
if (!libraryPtr.TryGetFunction(entryPoint, out IntPtr functionPtr))
continue;
#pragma warning disable CS0618 // Type or member is obsolete
field.SetValue(instance, Marshal.GetDelegateForFunctionPointer(functionPtr, field.FieldType));
#pragma warning restore CS0618 // Type or member is obsolete
found = true;
break;
}
if (found)
break;
}
if (!found)
throw new EntryPointNotFoundException($"No matching entry point found for {field.Name} in {field.DeclaringType.FullName}");
}
}
public static class DlopenFlags {
public const int RTLD_LAZY = 0x0001;
public const int RTLD_NOW = 0x0002;
public const int RTLD_LOCAL = 0x0000;
public const int RTLD_GLOBAL = 0x0100;
}
}
/// <summary>
/// Similar to DllImport, but requires you to run typeof(DeclaringType).ResolveDynDllImports();
/// </summary>
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
public class DynDllImportAttribute : Attribute {
/// <summary>
/// The library or library alias to use.
/// </summary>
public string LibraryName { get; set; }
/// <summary>
/// A list of possible entrypoints that the function can be resolved to. Implicitly includes the field name and delegate name.
/// </summary>
public string[] EntryPoints { get; set; }
/// <param name="libraryName">The library or library alias to use.</param>
/// <param name="entryPoints">A list of possible entrypoints that the function can be resolved to. Implicitly includes the field name and delegate name.</param>
public DynDllImportAttribute(string libraryName, params string[] entryPoints) {
LibraryName = libraryName;
EntryPoints = entryPoints;
}
}
/// <summary>
/// A mapping entry, to be used by <see cref="DynDllImportAttribute"/>.
/// </summary>
public sealed class DynDllMapping {
/// <summary>
/// The name as which the library will be resolved as. Useful to remap libraries or to provide full paths.
/// </summary>
public string LibraryName { get; set; }
/// <summary>
/// Platform-dependent loading flags.
/// </summary>
public int? Flags { get; set; }
/// <param name="libraryName">The name as which the library will be resolved as. Useful to remap libraries or to provide full paths.</param>
/// <param name="flags">Platform-dependent loading flags.</param>
public DynDllMapping(string libraryName, int? flags = null) {
LibraryName = libraryName ?? throw new ArgumentNullException(nameof(libraryName));
Flags = flags;
}
public static implicit operator DynDllMapping(string libraryName) {
return new DynDllMapping(libraryName);
}
}
}