/// <summary> /// Unloads all DLLs and functions currently loaded /// </summary> public static void UnloadAll() { _nativeFunctionLoadLock.EnterWriteLock(); //Locking with no thread safety option is not required but is ok (this function isn't performance critical) try { foreach (var dll in _dlls.Values) { if (dll.handle != IntPtr.Zero) { LowLevelPluginManager.OnBeforeDllUnload(dll); InvokeCustomTriggers(_customBeforeUnloadTriggers, dll); bool success = SysUnloadDll(dll.handle); if (!success) { Debug.LogWarning($"Error while unloading DLL \"{dll.name}\" at path \"{dll.path}\""); } dll.ResetAsUnloaded(); InvokeCustomTriggers(_customAfterUnloadTriggers, dll); } } } finally { _nativeFunctionLoadLock.ExitWriteLock(); } }
/// <summary> /// Loads DLL and function delegate of <paramref name="nativeFunction"/> if not yet loaded. /// To achieve thread safety calls to this method must be synchronized. /// Note: This method is being called by dynamically generated code. Be careful when changing its signature. /// </summary> internal static void LoadTargetFunction(NativeFunction nativeFunction, bool ignoreLoadError) { var dll = nativeFunction.containingDll; if (dll.handle == IntPtr.Zero) { dll.handle = SysLoadDll(dll.path); if (dll.handle == IntPtr.Zero) { if (!ignoreLoadError) { dll.loadingError = true; #if UNITY_EDITOR DispatchOnMainThread(() => { EditorApplication.isPaused = true; }); #endif throw new NativeDllException($"Could not load DLL \"{dll.name}\" at path \"{dll.path}\"."); } return; } else { dll.loadingError = false; LowLevelPluginManager.OnDllLoaded(dll); // Call the custom triggers once UnityPluginLoad has been called // For Lazy mode call the triggers immediately, preload waits until all functions are loaded (in LoadAll) if (Options.loadingMode == DllLoadingMode.Lazy) { InvokeCustomTriggers(_customLoadedTriggers, dll); } } } if (nativeFunction.@delegate == null) { IntPtr funcPtr = SysGetDllProcAddress(dll.handle, nativeFunction.identity.symbol); if (funcPtr == IntPtr.Zero) { if (!ignoreLoadError) { dll.symbolError = true; #if UNITY_EDITOR DispatchOnMainThread(() => { EditorApplication.isPaused = true; }); #endif throw new NativeDllException($"Could not get address of symbol \"{nativeFunction.identity.symbol}\" in DLL \"{dll.name}\" at path \"{dll.path}\"."); } return; } else { dll.symbolError = false; } nativeFunction.@delegate = Marshal.GetDelegateForFunctionPointer(funcPtr, nativeFunction.delegateType); } }
/// <summary> /// Loads DLL and function delegate of <paramref name="nativeFunction"/> if not yet loaded. /// To achieve thread safety calls to this method must be synchronized. /// Note: This method is being called by dynamically generated code. Be careful when changing its signature. /// </summary> internal static void LoadTargetFunction(NativeFunction nativeFunction, bool ignoreLoadError) { var dll = nativeFunction.containingDll; if (dll.handle == IntPtr.Zero) { dll.handle = SysLoadDll(dll.path); if (dll.handle == IntPtr.Zero) { if (!ignoreLoadError) { dll.loadingError = true; Prop_EditorApplication_isPaused.Value?.SetValue(null, true); throw new NativeDllException($"Could not load DLL \"{dll.name}\" at path \"{dll.path}\"."); } return; } else { dll.loadingError = false; InvokeCustomTriggers(_customLoadedTriggers, dll); LowLevelPluginManager.OnDllLoaded(dll); } } if (nativeFunction.@delegate == null) { IntPtr funcPtr = SysGetDllProcAddress(dll.handle, nativeFunction.identity.symbol); if (funcPtr == IntPtr.Zero) { if (!ignoreLoadError) { dll.symbolError = true; Prop_EditorApplication_isPaused.Value?.SetValue(null, true); throw new NativeDllException($"Could not get address of symbol \"{nativeFunction.identity.symbol}\" in DLL \"{dll.name}\" at path \"{dll.path}\"."); } return; } else { dll.symbolError = false; } nativeFunction.@delegate = Marshal.GetDelegateForFunctionPointer(funcPtr, nativeFunction.delegateType); } }
/// <summary> /// Initialization. /// Finds and mocks relevant native function declarations. /// If <see cref="DllLoadingMode.Preload"/> option is specified, loads all DLLs specified by these functions. /// Options have to be configured before calling this method. /// </summary> internal static void Initialize(int unityMainThreadId, string assetsPath) { _unityMainThreadId = unityMainThreadId; _assetsPath = assetsPath; LowLevelPluginManager.ResetStubPlugin(); Assembly[] assemblies; if (Options.assemblyPaths.Length == 0) { assemblies = new[] { Assembly.GetExecutingAssembly() }; } else { var allAssemblies = AppDomain.CurrentDomain.GetAssemblies(); assemblies = allAssemblies.Where(a => !a.IsDynamic && Options.assemblyPaths.Any(p => p == PathUtils.NormallizeSystemAssemblyPath(a.Location))).ToArray(); var missingAssemblies = Options.assemblyPaths.Except(assemblies.Select(a => PathUtils.NormallizeSystemAssemblyPath(a.Location))); foreach (var assemblyPath in missingAssemblies) { Debug.LogError($"Could not find assembly at path {assemblyPath}"); } } foreach (var assembly in assemblies) { var allTypes = assembly.GetTypes(); foreach (var type in allTypes) { foreach (var method in type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) { if (method.IsDefined(typeof(DllImportAttribute))) { if (method.IsDefined(typeof(DisableMockingAttribute))) { continue; } if (method.DeclaringType.IsDefined(typeof(DisableMockingAttribute))) { continue; } 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))) { RegisterTriggerMethod(method, ref _customAfterUnloadTriggers); } } } } if (Options.loadingMode == DllLoadingMode.Preload) { LoadAll(); } }
/// <summary> /// Initialization. /// Finds and mocks relevant native function declarations. /// If <see cref="DllLoadingMode.Preload"/> option is specified, loads all DLLs specified by these functions. /// Options have to be configured before calling this method. /// </summary> internal static void Initialize(DllManipulatorOptions options, int unityMainThreadId, string assetsPath) { // Make a deep copy of the options so we can edit them in DllManipulatorScript independently Options = new DllManipulatorOptions(); options.CloneTo(Options); _unityMainThreadId = unityMainThreadId; _assetsPath = assetsPath; LowLevelPluginManager.ResetStubPlugin(); IEnumerable <string> assemblyPathsTemp = Options.assemblyNames; if (!assemblyPathsTemp.Any()) { assemblyPathsTemp = DEFAULT_ASSEMBLY_NAMES; } assemblyPathsTemp = assemblyPathsTemp.Concat(INTERNAL_ASSEMBLY_NAMES); var allAssemblies = AppDomain.CurrentDomain.GetAssemblies(); var assemblies = allAssemblies.Where(a => !a.IsDynamic && assemblyPathsTemp.Any(p => p == Path.GetFileNameWithoutExtension(a.Location))).ToArray(); var missingAssemblies = assemblyPathsTemp.Except(assemblies.Select(a => Path.GetFileNameWithoutExtension(a.Location))); foreach (var assembly in missingAssemblies.Except(DEFAULT_ASSEMBLY_NAMES)) { Debug.LogError($"Could not find assembly: {assembly}"); } foreach (var assembly in assemblies) { var allTypes = assembly.GetTypes(); foreach (var type in allTypes) { foreach (var method in type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) { if (method.IsDefined(typeof(DllImportAttribute))) { if (method.IsDefined(typeof(DisableMockingAttribute))) { continue; } if (method.DeclaringType.IsDefined(typeof(DisableMockingAttribute))) { continue; } if (Options.mockAllNativeFunctions || method.IsDefined(typeof(MockNativeDeclarationAttribute)) || method.DeclaringType.IsDefined(typeof(MockNativeDeclarationsAttribute))) { MockNativeFunction(method); } } else { if (method.IsDefined(typeof(NativeDllLoadedTriggerAttribute))) { RegisterTriggerMethod(method, ref _customLoadedTriggers, method.GetCustomAttribute <NativeDllLoadedTriggerAttribute>()); } if (method.IsDefined(typeof(NativeDllBeforeUnloadTriggerAttribute))) { RegisterTriggerMethod(method, ref _customBeforeUnloadTriggers, method.GetCustomAttribute <NativeDllBeforeUnloadTriggerAttribute>()); } if (method.IsDefined(typeof(NativeDllAfterUnloadTriggerAttribute))) { RegisterTriggerMethod(method, ref _customAfterUnloadTriggers, method.GetCustomAttribute <NativeDllAfterUnloadTriggerAttribute>()); } } } } } if (Options.loadingMode == DllLoadingMode.Preload) { LoadAll(); } }