diff --git a/README.md b/README.md
index 252813b..218d111 100644
--- a/README.md
+++ b/README.md
@@ -30,6 +30,8 @@ Tool created mainly to solve old problem with reloading [native plugins](https:/
- If something is not working, first check out available options (and read their descriptions), then [report an issue](https://github.com/mcpiroman/UnityNativeTool/issues/new).
- Options are accessible via `DllManipulatorScript` editor or window.
- Although this tool presumably works in built game, it's intended to be used in editor.
+- Get callbacks in C# when the dll load state has changed with attributes like `[NativeDllLoadedTrigger]`, see `Attributes.cs` for more information
+- Unload and load all DLLs via shortcut `Alt+D` and `Alt+Shfit+D` respectively. Editable in the Shortcut Manager for 2019.1+
## Limitations
- Marshaling parameter attributes other than `[MarshalAs]`, `[In]` and `[Out]` are not supported.
diff --git a/scripts/Attributes.cs b/scripts/Attributes.cs
index bae18f9..c4a18e4 100644
--- a/scripts/Attributes.cs
+++ b/scripts/Attributes.cs
@@ -30,13 +30,32 @@ public class DisableMockingAttribute : Attribute
}
+ ///
+ /// Such a method must be static and have one of the following signatures:
+ ///
+ /// public static void Func()
+ /// public static void Func(NativeDll dll)
+ /// public static void Func(NativeDll dll, int mainThreadId)
+ ///
+ ///
+ [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
+ public class TriggerAttribute : Attribute
+ {
+ ///
+ /// Should the method always be executed on the main thread, to allow use of the Unity API.
+ /// Note: this means the method is not immediately executed but put in a queue, if it is not triggered from the main thread.
+ ///
+ public bool UseMainThreadQueue = false;
+ }
+
///
/// Methods with this attribute are called directly after a native DLL has been loaded.
/// Such method must be and either have no parameters or one parameter of type
/// which indicates the state of the dll being loaded. Please treat this parameter as readonly.
+ ///
///
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
- public class NativeDllLoadedTriggerAttribute : Attribute
+ public class NativeDllLoadedTriggerAttribute : TriggerAttribute
{
}
@@ -45,9 +64,10 @@ public class NativeDllLoadedTriggerAttribute : Attribute
/// Methods with this attribute are called directly before a native DLL is going to be unloaded.
/// Such method must be and either have no parameters or one parameter of type
/// which indicates the state of the dll being unloaded. Please treat this parameter as readonly.
+ ///
///
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
- public class NativeDllBeforeUnloadTriggerAttribute : Attribute
+ public class NativeDllBeforeUnloadTriggerAttribute : TriggerAttribute
{
}
@@ -56,9 +76,10 @@ public class NativeDllBeforeUnloadTriggerAttribute : Attribute
/// Methods with this attribute are called directly after a native DLL has been unloaded.
/// Such method must be and either have no parameters or one parameter of type
/// which indicates the state of the dll being unloaded. Please treat this parameter as readonly.
+ ///
///
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
- public class NativeDllAfterUnloadTriggerAttribute : Attribute
+ public class NativeDllAfterUnloadTriggerAttribute : TriggerAttribute
{
}
diff --git a/scripts/DllManipulator.cs b/scripts/DllManipulator.cs
index 8440a3f..84f143f 100644
--- a/scripts/DllManipulator.cs
+++ b/scripts/DllManipulator.cs
@@ -30,10 +30,11 @@ public partial class DllManipulator
private static List _mockedNativeFunctions = new List();
private static int _createdDelegateTypes = 0;
private static int _lastNativeCallIndex = 0; //Use with synchronization
- private static List _customLoadedTriggers = null;
- private static List _customBeforeUnloadTriggers = null;
- private static List _customAfterUnloadTriggers = null;
-
+
+ private static List> _customLoadedTriggers = null; //List of callbacks to run, whether to run them on the main thread.
+ private static List> _customBeforeUnloadTriggers = null;
+ private static List> _customAfterUnloadTriggers = null;
+
///
/// Initialization.
/// Finds and mocks relevant native function declarations.
@@ -81,17 +82,16 @@ internal static void Initialize(int unityMainThreadId, string assetsPath)
if (Options.mockAllNativeFunctions || method.IsDefined(typeof(MockNativeDeclarationAttribute)) || method.DeclaringType.IsDefined(typeof(MockNativeDeclarationsAttribute)))
MockNativeFunction(method);
}
- else if(method.IsDefined(typeof(NativeDllLoadedTriggerAttribute)))
- {
- RegisterTriggerMethod(method, ref _customLoadedTriggers);
- }
- else if (method.IsDefined(typeof(NativeDllBeforeUnloadTriggerAttribute)))
- {
- RegisterTriggerMethod(method, ref _customBeforeUnloadTriggers);
- }
- else if (method.IsDefined(typeof(NativeDllAfterUnloadTriggerAttribute)))
+ else
{
- RegisterTriggerMethod(method, ref _customAfterUnloadTriggers);
+ if (method.IsDefined(typeof(NativeDllLoadedTriggerAttribute)))
+ RegisterTriggerMethod(method, ref _customLoadedTriggers, method.GetCustomAttribute());
+
+ if (method.IsDefined(typeof(NativeDllBeforeUnloadTriggerAttribute)))
+ RegisterTriggerMethod(method, ref _customBeforeUnloadTriggers, method.GetCustomAttribute());
+
+ if (method.IsDefined(typeof(NativeDllAfterUnloadTriggerAttribute)))
+ RegisterTriggerMethod(method, ref _customAfterUnloadTriggers, method.GetCustomAttribute());
}
}
}
@@ -101,18 +101,34 @@ internal static void Initialize(int unityMainThreadId, string assetsPath)
LoadAll();
}
- private static void RegisterTriggerMethod(MethodInfo method, ref List triggersList)
+ ///
+ /// Will unload/forget all dll's and reset the state
+ ///
+ public static void Reset()
+ {
+ UnloadAll();
+ ForgetAllDlls();
+ ClearCrashLogs();
+
+ _customLoadedTriggers?.Clear();
+ _customAfterUnloadTriggers?.Clear();
+ _customBeforeUnloadTriggers?.Clear();
+ }
+
+ private static void RegisterTriggerMethod(MethodInfo method, ref List> triggersList, TriggerAttribute attribute)
{
var parameters = method.GetParameters();
- if (parameters.Length == 0 || parameters.Length == 1 && parameters[0].ParameterType == typeof(NativeDll))
+ if (parameters.Length == 0 || parameters.Length == 1 && parameters[0].ParameterType == typeof(NativeDll)
+ || parameters.Length == 2 && parameters[0].ParameterType == typeof(NativeDll) && parameters[1].ParameterType == typeof(int))
{
if (triggersList == null)
- triggersList = new List(2);
- triggersList.Add(method);
+ triggersList = new List>();
+ triggersList.Add(new Tuple(method, attribute.UseMainThreadQueue));
}
else
{
- Debug.LogError($"Trigger method must either take no parameters or one parameter of type {nameof(NativeDll)}. Violation on method {method.Name} in {method.DeclaringType.FullName}");
+ Debug.LogError($"Trigger method must either take no parameters, one parameter of type {nameof(NativeDll)} or one of type {nameof(NativeDll)} and one int. " +
+ $"See the TriggerAttribute for more details. Violation on method {method.Name} in {method.DeclaringType.FullName}");
}
}
@@ -509,17 +525,28 @@ internal static void LoadTargetFunction(NativeFunction nativeFunction, bool igno
}
}
- private static void InvokeCustomTriggers(List triggers, NativeDll dll)
+ private static void InvokeCustomTriggers(List> triggers, NativeDll dll)
{
if (triggers == null)
return;
- foreach(var triggerMethod in triggers)
+ foreach(var (methodInfo, useMainThreadQueue) in triggers)
{
- if (triggerMethod.GetParameters().Length == 1)
- triggerMethod.Invoke(null, new object[] { dll });
+ object[] args;
+
+ // Determine args for method
+ if (methodInfo.GetParameters().Length == 2)
+ args = new object[] { dll, _unityMainThreadId };
+ else if (methodInfo.GetParameters().Length == 1)
+ args = new object[] { dll };
+ else
+ args = Array.Empty