예제 #1
0
        /// <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);
            }
        }
예제 #2
0
        public static void OnBeforeDllUnload(NativeDll dll)
        {
            var unityPluginUnloadFunc = new NativeFunction("UnityPluginUnload", dll)
            {
                delegateType = typeof(UnityPluginUnloadDel)
            };

            DllManipulator.LoadTargetFunction(unityPluginUnloadFunc, true);
            if (unityPluginUnloadFunc.@delegate != null)
            {
                ((UnityPluginUnloadDel)unityPluginUnloadFunc.@delegate)();
            }
        }
예제 #3
0
        /// <summary>
        /// Creates and registers new DynamicMethod that mocks <paramref name="nativeMethod"/> and itself calls dynamically loaded function from DLL.
        /// </summary>
        private static DynamicMethod GetNativeFunctionMockMethod(MethodInfo nativeMethod)
        {
            if (!_nativeFunctionMocks.TryGetValue(nativeMethod, out var mockedDynamicMethod))
            {
                var    dllImportAttr = nativeMethod.GetCustomAttribute <DllImportAttribute>();
                var    dllName       = dllImportAttr.Value;
                string dllPath;
                var    nativeFunctionSymbol = dllImportAttr.EntryPoint;

                if (_dlls.TryGetValue(dllName, out var dll))
                {
                    dllPath = dll.path;
                }
                else
                {
                    dllPath = ApplyDirectoryPathMacros(Options.dllPathPattern).Replace(DLL_PATH_PATTERN_DLL_NAME_MACRO, dllName);
                    dll     = new NativeDll(dllName, dllPath);
                    _dlls.Add(dllName, dll);
                }

                var nativeFunction = new NativeFunction(nativeFunctionSymbol, dll);
                dll.functions.Add(nativeFunction);
                var nativeFunctionIndex = _mockedNativeFunctions.Count;
                _mockedNativeFunctions.Add(nativeFunction);

                var parameters            = nativeMethod.GetParameters();
                var parameterTypes        = parameters.Select(x => x.ParameterType).ToArray();
                var nativeMethodSignature = new NativeFunctionSignature(nativeMethod, dllImportAttr.CallingConvention,
                                                                        dllImportAttr.BestFitMapping, dllImportAttr.CharSet, dllImportAttr.SetLastError, dllImportAttr.ThrowOnUnmappableChar);
                if (!_delegateTypesForNativeFunctionSignatures.TryGetValue(nativeMethodSignature, out nativeFunction.delegateType))
                {
                    nativeFunction.delegateType = CreateDelegateTypeForNativeFunctionSignature(nativeMethodSignature, nativeMethod.Name);
                    _delegateTypesForNativeFunctionSignatures.Add(nativeMethodSignature, nativeFunction.delegateType);
                }
                var targetDelegateInvokeMethod = nativeFunction.delegateType.GetMethod("Invoke", BindingFlags.Instance | BindingFlags.Public);

                mockedDynamicMethod = new DynamicMethod(dllName + ":::" + nativeFunctionSymbol, nativeMethod.ReturnType, parameterTypes, typeof(DllManipulator));
                mockedDynamicMethod.DefineParameter(0, nativeMethod.ReturnParameter.Attributes, null);
                for (int i = 0; i < parameters.Length; i++)
                {
                    mockedDynamicMethod.DefineParameter(i + 1, parameters[i].Attributes, null);
                }

                GenerateNativeFunctionMockBody(mockedDynamicMethod.GetILGenerator(), parameters, targetDelegateInvokeMethod, nativeFunctionIndex);

                _antiGcRefHolder.AddLast(nativeFunction);
                _antiGcRefHolder.AddLast(mockedDynamicMethod);
            }

            return(mockedDynamicMethod);
        }
예제 #4
0
        /// <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);
            }
        }
예제 #5
0
        public static void OnDllLoaded(NativeDll dll)
        {
            if (_unityInterfacePtr == IntPtr.Zero)
            {
                return;
            }

            var unityPluginLoadFunc = new NativeFunction("UnityPluginLoad", dll)
            {
                delegateType = typeof(UnityPluginLoadDel)
            };

            DllManipulator.LoadTargetFunction(unityPluginLoadFunc, true);
            if (unityPluginLoadFunc.@delegate != null)
            {
                ((UnityPluginLoadDel)unityPluginLoadFunc.@delegate)(_unityInterfacePtr);
            }
        }
        public static void OnDllLoaded(NativeDll dll)
        {
            if (_triedLoadingStubPlugin && _unityInterfacePtr == IntPtr.Zero)
            {
                return;
            }

            var unityPluginLoadFunc = new NativeFunction("UnityPluginLoad", dll)
            {
                delegateType = typeof(UnityPluginLoadDel)
            };

            DllManipulator.LoadTargetFunction(unityPluginLoadFunc, true);
            if (unityPluginLoadFunc.@delegate == null)
            {
                return;
            }

            if (!_triedLoadingStubPlugin)
            {
                try
                {
                    _unityInterfacePtr = GetUnityInterfacesPtr();
                    if (_unityInterfacePtr == IntPtr.Zero)
                    {
                        throw new Exception($"{nameof(GetUnityInterfacesPtr)} returned null");
                    }
                }
                catch (DllNotFoundException)
                {
                    Debug.LogWarning("StubLluiPlugin not found. UnityPluginLoad and UnityPluginUnload callbacks won't fire. If you didn't install UnityNativeTool from .unitypackage or it didn't contain the compiled plugin, you'll need to compile it manually. You may also comment out this warning if you don't care about these callbacks.");
                }
                finally
                {
                    _triedLoadingStubPlugin = true;
                }
            }

            if (_unityInterfacePtr != IntPtr.Zero)
            {
                ((UnityPluginLoadDel)unityPluginLoadFunc.@delegate)(_unityInterfacePtr);
            }
        }
예제 #7
0
        /// <summary>
        /// Logs native function's call to file. If that file exists, it is overwritten. One file is maintained for each thread.
        /// Note: This method is being called by dynamically generated code. Be careful when changing its signature.
        /// </summary>
        private static void WriteNativeCrashLog(NativeFunction nativeFunction, object[] arguments)
        {
            var threadId = Thread.CurrentThread.ManagedThreadId;
            var filePath = Path.Combine(ApplyDirectoryPathMacros(Options.crashLogsDir), $"{CRASH_FILE_NAME_PREFIX}tid{threadId}.log");

            using (var file = File.Open(filePath, FileMode.Create, FileAccess.Write, FileShare.Read)) //Truncates file if exists
            {
                using (var writer = new StreamWriter(file))
                {
                    writer.Write("function: ");
                    writer.WriteLine(nativeFunction.identity.symbol);

                    writer.Write($"from DLL: ");
                    writer.WriteLine(nativeFunction.containingDll.name);

                    writer.Write($"  at path: ");
                    writer.WriteLine(nativeFunction.containingDll.path);

                    writer.Write("arguments: ");
                    if (arguments.Length == 0)
                    {
                        writer.WriteLine("no arguments");
                    }
                    else
                    {
                        writer.WriteLine();
                        for (int i = 0; i < arguments.Length; i++)
                        {
                            writer.Write($"  {i}:".PadRight(5));
                            var param = arguments[i];
                            if (param == null)
                            {
                                writer.Write("null");
                            }
                            else
                            {
                                switch (param)
                                {
                                case string _:
                                    writer.Write($"\"{param}\"");
                                    break;

                                //For float types use InvariantCulture, as so to use dot decimal separator over comma
                                case float f:
                                    writer.Write(f.ToString(System.Globalization.CultureInfo.InvariantCulture));
                                    break;

                                case double f:
                                    writer.Write(f.ToString(System.Globalization.CultureInfo.InvariantCulture));
                                    break;

                                case decimal f:
                                    writer.Write(f.ToString(System.Globalization.CultureInfo.InvariantCulture));
                                    break;

                                default:
                                    writer.Write(param);
                                    break;
                                }
                            }
                            writer.WriteLine();
                        }
                    }

                    writer.Write("thread: ");
                    if (threadId == _unityMainThreadId)
                    {
                        writer.WriteLine("unity main thread");
                    }
                    else
                    {
                        writer.WriteLine($"{Thread.CurrentThread.Name}({threadId})");
                    }

                    var nativeCallIndex = Interlocked.Increment(ref _lastNativeCallIndex) - 1;
                    writer.Write("call index: ");
                    writer.WriteLine(nativeCallIndex);

                    if (Options.crashLogsStackTrace)
                    {
                        var stackTrace = new System.Diagnostics.StackTrace(1); //Skip this frame
                        writer.WriteLine("stack trace:");
                        writer.Write(stackTrace.ToString());
                    }
                }
            }
        }