public static int DllMain(string arg) { assemblyPath = null; Args args = new Args(arg); assemblyPath = args.GetString("MainAssembly"); if (!string.IsNullOrEmpty(assemblyPath)) { if (string.IsNullOrEmpty(assemblyPath) || !File.Exists(assemblyPath)) { return((int)AssemblyLoaderError.MainAssemblyNotFound); } mainAssemblyDirectory = Path.GetDirectoryName(assemblyPath); } else { return((int)AssemblyLoaderError.MainAssemblyPathNotProvided); } IntPtr asyncTaskAddr = (IntPtr)args.GetInt64("AsyncTask"); IntPtr isInGameThreadAddr = (IntPtr)args.GetInt64("IsInGameThread"); if (asyncTaskAddr == IntPtr.Zero || isInGameThreadAddr == IntPtr.Zero) { return((int)AssemblyLoaderError.GameThreadAsyncNull); } GameThreadHelper.Init(asyncTaskAddr, isInGameThreadAddr); entryPointArg = arg; string currentAssemblyPath = Assembly.GetExecutingAssembly().Location; string currentAssemblyFileName = Path.GetFileNameWithoutExtension(currentAssemblyPath); currentAssemblyDirectory = Path.GetDirectoryName(currentAssemblyPath); if (!IsSameOrSubDirectory(currentAssemblyDirectory, mainAssemblyDirectory)) { return((int)AssemblyLoaderError.MainAssemblyPathNotProvided); } if (!ReloadAppDomain()) { return((int)AssemblyLoaderError.LoadFailed); } return(0); }
private static void CreatePreloadedContext(string entryPointArg) { Runtime.AssemblyContextRef contextRef = Runtime.AssemblyContextRef.Invalid; if (SharedRuntimeState.CurrentRuntime == EDotNetRuntime.CoreCLR) { contextRef = Runtime.AssemblyContext.Create(); } else { // Seperate method to avoid issues with .NET Core contextRef = CreatePreloadedContextAppDomain(entryPointArg); } entryPointArg += "|AssemblyContext=" + contextRef.Format(); if (!contextRef.IsInvalid) { Debug.Assert(preloadedContextRef.IsInvalid, "Trying to preload when there is already something preloaded"); try { AssemblyLoader loader = new AssemblyLoader(mainAssemblyPath, entryPointType, entryPointMethod, entryPointArg, true, contextRef); contextRef.DoCallBack(loader.Load); preloadedContextRef = contextRef; } catch (Exception e) { GameThreadHelper.Run(delegate() // For stylized message box (as we may not be in the game thread) { MessageBox("Failed to create assembly context for \"" + mainAssemblyPath + "\" " + Environment.NewLine + Environment.NewLine + e, errorMsgBoxTitle); }); preloadFailed = true; UnloadContext(contextRef); } } }
private static bool ReloadAppDomain() { if (!GameThreadHelper.IsInGameThread()) { bool result = false; GameThreadHelper.Run(delegate { result = ReloadAppDomain(); }); return(result); } if (!File.Exists(assemblyPath)) { hotreloadData = null; return(false); } if (appDomain != null) { UnloadAppDomain(); } if (LoadAssemblyWithoutAppDomain) { try { AssemblyLoader loader = new AssemblyLoader(assemblyPath, entryPointType, entryPointMethod, entryPointArg, hotreloadData, false); loader.Load(); } catch (Exception e) { MessageBox.Show("Failed to load assembly \"" + assemblyPath + "\" " + Environment.NewLine + Environment.NewLine + e, errorMsgBoxTitle); hotreloadData = null; return(false); } } else { string entryPointArgEx = entryPointArg; bool firstLoad = preloadAppDomainWaitHandle == null; if (firstLoad) { PreloadNextAppDomain(true); } else { entryPointArgEx += "|Reloading=true"; } preloadAppDomainWaitHandle.WaitOne(Timeout.Infinite); preloadAppDomainWaitHandle.Reset(); if (!preloadFailed) { appDomain = preloadAppDomain; preloadAppDomain = null; try { AssemblyLoader loader = new AssemblyLoader(assemblyPath, entryPointType, entryPointMethod, entryPointArgEx, hotreloadData, false); appDomain.DoCallBack(loader.Load); UpdateAssemblyWatchers(appDomain.GetData(hotReloadAssemblyPathsName) as string[]); } catch (Exception e) { MessageBox.Show("Failed to create AppDomain for \"" + assemblyPath + "\" " + Environment.NewLine + Environment.NewLine + e, errorMsgBoxTitle); } } } if (!LoadAssemblyWithoutAppDomain) { PreloadNextAppDomain(false); } hotreloadData = null; return(true); }
private static void UnloadContext(Runtime.AssemblyContextRef contextRef, bool threaded = true) { if (threaded) { // Run the Unload on a seperate thread to avoid waiting for it. new Thread(delegate() { UnloadContext(contextRef, false); }).Start(); } else { Exception exception = null; bool unloaded = false; if (SharedRuntimeState.CurrentRuntime == EDotNetRuntime.CoreCLR) { WeakReference weakRef = contextRef.GetWeakReference(); try { // Just fire and hope for the best contextRef.Unload(); unloaded = true; } catch (Exception e) { exception = e; } if (unloaded && weakRef != null) { for (int i = 0; i < 15 && weakRef.IsAlive; i++) { GC.Collect(); GC.WaitForPendingFinalizers(); if (i > 10) { Thread.Sleep(100); } } if (weakRef.IsAlive) { exception = new Exception(".NET Core couldn't unload the AssemblyLoadContext. There is likely some global event" + " which is being bound to. You will crash on the next load due to the state not being cleaned up properly."); unloaded = false; } } } else { for (int i = 0; i < 3; i++) { try { contextRef.Unload(); unloaded = true; break; } catch (CannotUnloadAppDomainException e) { exception = e; } catch (AppDomainUnloadedException e) { exception = e; } // Give it a little more time and then try again Thread.Sleep(300); } } if (!unloaded) { GameThreadHelper.Run(delegate() // For stylized message box (as we may not be in the game thread) { MessageBox("Failed to unload assembly context for \"" + mainAssemblyPath + "\" " + Environment.NewLine + Environment.NewLine + exception, unloadErrorMsgBoxTitle); }); } } }
public static int DllMain(string arg) { try { Args args = new Args(arg); SharedRuntimeState.Initialize((IntPtr)args.GetInt64("RuntimeState")); Runtime.AssemblyContext.Initialize(); mainAssemblyPath = args.GetString("MainAssembly"); if (!string.IsNullOrEmpty(mainAssemblyPath)) { if (string.IsNullOrEmpty(mainAssemblyPath) || !File.Exists(mainAssemblyPath)) { return((int)AssemblyLoaderError.MainAssemblyNotFound); } mainAssemblyDirectory = Path.GetDirectoryName(mainAssemblyPath); } else { return((int)AssemblyLoaderError.MainAssemblyPathNotProvided); } IntPtr addTickerAddr = (IntPtr)args.GetInt64("AddTicker"); IntPtr isInGameThreadAddr = (IntPtr)args.GetInt64("IsInGameThread"); if (addTickerAddr == IntPtr.Zero || isInGameThreadAddr == IntPtr.Zero) { return((int)AssemblyLoaderError.GameThreadHelpersNull); } GameThreadHelper.Init(addTickerAddr, isInGameThreadAddr, OnRuntimeChanged); Debug.Assert(GameThreadHelper.IsInGameThread()); entryPointArg = arg; string currentAssemblyPath = Assembly.GetExecutingAssembly().Location; string currentAssemblyFileName = Path.GetFileNameWithoutExtension(currentAssemblyPath); currentAssemblyDirectory = Path.GetDirectoryName(currentAssemblyPath); if (!IsSameOrSubDirectory(currentAssemblyDirectory, mainAssemblyDirectory)) { return((int)AssemblyLoaderError.MainAssemblyPathNotProvided); } // If there is already a loaded runtime only do a pre-load if (SharedRuntimeState.GetLoadedRuntimes() != EDotNetRuntime.None) { Debug.Assert(mainContextRef.IsInvalid); // Make sure the main assembly path exists if (!File.Exists(mainAssemblyPath)) { return((int)AssemblyLoaderError.LoadFailed); } // Make sure we are using assmbly contexts loadding otherwise hotreload wont work which defeats the purpose of // using multiple runtimes if (LoadAssemblyWithoutContexts) { return((int)AssemblyLoaderError.LoadFailed); } // Preload now and then do a full load when NextRuntime is set to this runtime type PreloadNextContext(); // Watch for assembly changes (the paths should have been set up by the full load in the other runtime) UpdateAssemblyWatchers(); return(0); } unsafe { SharedRuntimeState.Instance->ActiveRuntime = SharedRuntimeState.CurrentRuntime; } bool loaded; if (LoadAssemblyWithoutContexts) { loaded = LoadWithoutUsingContexts(); } else { loaded = ReloadMainContext(); } if (!loaded) { unsafe { SharedRuntimeState.Instance->ActiveRuntime = EDotNetRuntime.None; } return((int)AssemblyLoaderError.LoadFailed); } } catch (Exception e) { string exceptionStr = "Entry point exception (Loader): " + e; if (SharedRuntimeState.Initialized) { SharedRuntimeState.LogError(exceptionStr); SharedRuntimeState.MessageBox(exceptionStr, errorMsgBoxTitle); } return((int)AssemblyLoaderError.Exception); } return(0); }
private static bool ReloadMainContext(bool threaded = true) { if (!GameThreadHelper.IsInGameThread()) { bool result = false; GameThreadHelper.Run(delegate { result = ReloadMainContext(); }); return(result); } if (!SharedRuntimeState.IsActiveRuntime) { return(false); } if (!File.Exists(mainAssemblyPath)) { SharedRuntimeState.SetHotReloadData(null); return(false); } if (!mainContextRef.IsInvalid) { UnloadMainContext(threaded); } string entryPointArgEx = entryPointArg; bool firstLoad = preloadContextWaitHandle == null; if (firstLoad) { PreloadNextContext(threaded); } else { entryPointArgEx += "|Reloading=true"; } preloadContextWaitHandle.WaitOne(Timeout.Infinite); preloadContextWaitHandle.Reset(); if (!preloadFailed) { Debug.Assert(!preloadedContextRef.IsInvalid, "Preloaded context shouldn't be invalid"); mainContextRef = preloadedContextRef; preloadedContextRef = Runtime.AssemblyContextRef.Invalid; entryPointArgEx += "|AssemblyContext=" + mainContextRef.Format(); try { AssemblyLoader loader = new AssemblyLoader(mainAssemblyPath, entryPointType, entryPointMethod, entryPointArgEx, false, mainContextRef); mainContextRef.DoCallBack(loader.Load); UpdateAssemblyWatchers(); } catch (Exception e) { MessageBox("Failed to create assembly context for \"" + mainAssemblyPath + "\" " + Environment.NewLine + Environment.NewLine + e, errorMsgBoxTitle); } } PreloadNextContext(threaded); SharedRuntimeState.SetHotReloadData(null); return(true); }