From d2afe06f5b0df612fba588b75e972549a1ae60cb Mon Sep 17 00:00:00 2001 From: Bepis <36346617+bbepis@users.noreply.github.com> Date: Thu, 21 May 2020 03:06:34 +1000 Subject: [PATCH] Initial abstraction of console related calls --- .../Logger/PreloaderLogWriter.cs | 5 +- BepInEx.Preloader/Preloader.cs | 23 +-- BepInEx/BepInEx.csproj | 14 +- BepInEx/Bootstrap/Chainloader.cs | 29 +-- BepInEx/Console/ConsoleManager.cs | 183 ++++++++++++++++++ .../ConsoleEncoding.Buffers.cs | 0 .../ConsoleEncoding.PInvoke.cs | 0 .../ConsoleEncoding/ConsoleEncoding.cs | 0 .../Windows}/ConsoleWindow.cs | 25 +-- .../{ConsoleUtil => Console/Windows}/Kon.cs | 30 ++- .../Windows}/SafeConsole.cs | 0 BepInEx/Logging/ConsoleLogListener.cs | 5 +- 12 files changed, 239 insertions(+), 75 deletions(-) create mode 100644 BepInEx/Console/ConsoleManager.cs rename BepInEx/{ConsoleUtil => Console/Windows}/ConsoleEncoding/ConsoleEncoding.Buffers.cs (100%) rename BepInEx/{ConsoleUtil => Console/Windows}/ConsoleEncoding/ConsoleEncoding.PInvoke.cs (100%) rename BepInEx/{ConsoleUtil => Console/Windows}/ConsoleEncoding/ConsoleEncoding.cs (100%) rename BepInEx/{ConsoleUtil => Console/Windows}/ConsoleWindow.cs (84%) rename BepInEx/{ConsoleUtil => Console/Windows}/Kon.cs (79%) rename BepInEx/{ConsoleUtil => Console/Windows}/SafeConsole.cs (100%) diff --git a/BepInEx.Preloader/Logger/PreloaderLogWriter.cs b/BepInEx.Preloader/Logger/PreloaderLogWriter.cs index ba2a7de..3465fd8 100644 --- a/BepInEx.Preloader/Logger/PreloaderLogWriter.cs +++ b/BepInEx.Preloader/Logger/PreloaderLogWriter.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.IO; using System.Text; -using BepInEx.ConsoleUtil; using BepInEx.Logging; namespace BepInEx.Preloader @@ -37,9 +36,9 @@ namespace BepInEx.Preloader LogBuilder.Append(log); - Kon.ForegroundColor = eventArgs.Level.GetConsoleColor(); + ConsoleManager.SetConsoleColor(eventArgs.Level.GetConsoleColor()); ConsoleDirectWrite(log); - Kon.ForegroundColor = ConsoleColor.Gray; + ConsoleManager.SetConsoleColor(ConsoleColor.Gray); } public void ConsoleDirectWrite(string value) diff --git a/BepInEx.Preloader/Preloader.cs b/BepInEx.Preloader/Preloader.cs index ec27ba9..348887a 100644 --- a/BepInEx.Preloader/Preloader.cs +++ b/BepInEx.Preloader/Preloader.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; -using System.Text; using BepInEx.Configuration; using BepInEx.Logging; using BepInEx.Preloader.Patching; @@ -12,7 +11,6 @@ using HarmonyLib; using Mono.Cecil; using Mono.Cecil.Cil; using MonoMod.RuntimeDetour; -using UnityInjector.ConsoleUtil; using MethodAttributes = Mono.Cecil.MethodAttributes; namespace BepInEx.Preloader @@ -33,6 +31,7 @@ namespace BepInEx.Preloader { try { + ConsoleManager.Initialize(false); AllocateConsole(); bool bridgeInitialized = Utility.TryDo(() => @@ -57,7 +56,10 @@ namespace BepInEx.Preloader Logger.Listeners.Add(PreloaderLog); string consoleTile = $"BepInEx {typeof(Paths).Assembly.GetName().Version} - {Process.GetCurrentProcess().ProcessName}"; - ConsoleWindow.Title = consoleTile; + + if (ConsoleManager.ConsoleActive) + ConsoleManager.SetConsoleTitle(consoleTile); + Logger.LogMessage(consoleTile); //See BuildInfoAttribute for more information about this section. @@ -112,7 +114,7 @@ namespace BepInEx.Preloader PreloaderLog?.Dispose(); - if (!ConsoleWindow.IsAttached) + if (!ConsoleManager.ConsoleActive) { //if we've already attached the console, then the log will already be written to the console AllocateConsole(); @@ -222,20 +224,13 @@ namespace BepInEx.Preloader /// public static void AllocateConsole() { - if (!ConsoleWindow.ConfigConsoleEnabled.Value) + if (!ConsoleManager.ConfigConsoleEnabled.Value) return; try { - ConsoleWindow.Attach(); - - var encoding = (uint)Encoding.UTF8.CodePage; - - if (ConsoleWindow.ConfigConsoleShiftJis.Value) - encoding = 932; - - ConsoleEncoding.ConsoleCodePage = encoding; - Console.OutputEncoding = ConsoleEncoding.GetEncoding(encoding); + ConsoleManager.CreateConsole(); + ConsoleManager.SetConsoleEncoding(); } catch (Exception ex) { diff --git a/BepInEx/BepInEx.csproj b/BepInEx/BepInEx.csproj index 41bf15b..ae6ed98 100644 --- a/BepInEx/BepInEx.csproj +++ b/BepInEx/BepInEx.csproj @@ -62,6 +62,7 @@ + @@ -72,12 +73,12 @@ - - - - - - + + + + + + @@ -110,5 +111,6 @@ + \ No newline at end of file diff --git a/BepInEx/Bootstrap/Chainloader.cs b/BepInEx/Bootstrap/Chainloader.cs index d54440c..fa7be91 100644 --- a/BepInEx/Bootstrap/Chainloader.cs +++ b/BepInEx/Bootstrap/Chainloader.cs @@ -6,11 +6,9 @@ using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; -using System.Text; using System.Text.RegularExpressions; using Mono.Cecil; using UnityEngine; -using UnityInjector.ConsoleUtil; using Logger = BepInEx.Logging.Logger; namespace BepInEx.Bootstrap @@ -69,20 +67,17 @@ namespace BepInEx.Bootstrap } // Start logging - if (ConsoleWindow.ConfigConsoleEnabled.Value && startConsole) + if (ConsoleManager.ConfigConsoleEnabled.Value && startConsole) { - ConsoleWindow.Attach(); + ConsoleManager.CreateConsole(); Logger.Listeners.Add(new ConsoleLogListener()); } // Fix for standard output getting overwritten by UnityLogger - if (ConsoleWindow.StandardOut != null) + if (ConsoleManager.ConsoleActive) { - Console.SetOut(ConsoleWindow.StandardOut); - - var encoding = ConsoleWindow.ConfigConsoleShiftJis.Value ? 932 : (uint)Encoding.UTF8.CodePage; - ConsoleEncoding.ConsoleCodePage = encoding; - Console.OutputEncoding = ConsoleEncoding.GetEncoding(encoding); + ConsoleManager.SetConsoleStreams(); + ConsoleManager.SetConsoleEncoding(); } Logger.Listeners.Add(new UnityLogListener()); @@ -227,7 +222,9 @@ namespace BepInEx.Bootstrap try { var productNameProp = typeof(Application).GetProperty("productName", BindingFlags.Public | BindingFlags.Static); - ConsoleWindow.Title = $"{CurrentAssemblyName} {CurrentAssemblyVersion} - {productNameProp?.GetValue(null, null) ?? Process.GetCurrentProcess().ProcessName}"; + + if (ConsoleManager.ConsoleActive) + ConsoleManager.SetConsoleTitle($"{CurrentAssemblyName} {CurrentAssemblyVersion} - {productNameProp?.GetValue(null, null) ?? Process.GetCurrentProcess().ProcessName}"); Logger.LogMessage("Chainloader started"); @@ -384,10 +381,14 @@ namespace BepInEx.Bootstrap } catch (Exception ex) { - ConsoleWindow.Attach(); + try + { + ConsoleManager.CreateConsole(); + } + catch { } - Console.WriteLine("Error occurred starting the game"); - Console.WriteLine(ex.ToString()); + Logger.LogFatal("Error occurred starting the game"); + Logger.LogFatal(ex.ToString()); } Logger.LogMessage("Chainloader startup complete"); diff --git a/BepInEx/Console/ConsoleManager.cs b/BepInEx/Console/ConsoleManager.cs new file mode 100644 index 0000000..4d86eeb --- /dev/null +++ b/BepInEx/Console/ConsoleManager.cs @@ -0,0 +1,183 @@ +using System; +using System.IO; +using System.Text; +using BepInEx.Configuration; +using BepInEx.ConsoleUtil; +using UnityInjector.ConsoleUtil; + +namespace BepInEx +{ + public static class ConsoleManager + { + /// + /// True if an external console has been started, false otherwise. + /// + public static bool ConsoleActive { get; private set; } + + /// + /// The stream that writes to the standard out stream of the process. Should never be null. + /// + public static TextWriter StandardOutStream { get; internal set; } + + /// + /// The stream that writes to an external console. Null if no such console exists + /// + public static TextWriter ConsoleStream { get; internal set; } + + + public static void Initialize(bool active) + { + StandardOutStream = Console.Out; + Console.SetOut(StandardOutStream); + ConsoleActive = active; + + switch (Environment.OSVersion.Platform) + { + case PlatformID.MacOSX: + case PlatformID.Unix: + { + ConsoleActive = true; + break; + } + } + } + + + public static void CreateConsole() + { + if (ConsoleActive) + return; + + switch (Environment.OSVersion.Platform) + { + case PlatformID.Win32NT: + { + ConsoleWindow.Attach(); + break; + } + + default: + throw new PlatformNotSupportedException("Spawning a console is not currently supported on this platform"); + } + + SetConsoleStreams(); + + ConsoleActive = true; + } + + public static void DetachConsole() + { + if (!ConsoleActive) + return; + + switch (Environment.OSVersion.Platform) + { + case PlatformID.Win32NT: + { + ConsoleWindow.Detach(); + break; + } + + default: + throw new PlatformNotSupportedException("Spawning a console is not currently supported on this platform"); + } + + ConsoleActive = false; + } + + public static void SetConsoleEncoding() + { + uint encoding = ConfigConsoleShiftJis.Value ? 932 : (uint)Encoding.UTF8.CodePage; + + SetConsoleEncoding(encoding); + } + + public static void SetConsoleEncoding(uint encodingCodePage) + { + if (!ConsoleActive) + throw new InvalidOperationException("Console is not currently active"); + + switch (Environment.OSVersion.Platform) + { + case PlatformID.Win32NT: + { + ConsoleEncoding.ConsoleCodePage = encodingCodePage; + Console.OutputEncoding = Encoding.GetEncoding((int)encodingCodePage); + break; + } + + case PlatformID.MacOSX: + case PlatformID.Unix: + { + break; + } + } + } + + public static void SetConsoleTitle(string title) + { + switch (Environment.OSVersion.Platform) + { + case PlatformID.Win32NT: + { + if (!ConsoleActive) + return; + + ConsoleWindow.Title = title; + break; + } + } + } + + public static void SetConsoleColor(ConsoleColor color) + { + switch (Environment.OSVersion.Platform) + { + case PlatformID.Win32NT: + { + if (!ConsoleActive) + return; + + Kon.ForegroundColor = color; + break; + } + } + + Console.ForegroundColor = color; + } + + internal static void SetConsoleStreams() + { + switch (Environment.OSVersion.Platform) + { + case PlatformID.Win32NT: + { + Console.SetOut(ConsoleStream); + Console.SetError(ConsoleStream); + break; + } + + case PlatformID.MacOSX: + case PlatformID.Unix: + { + // We do not have external consoles on Unix platforms. + // Set the console output to standard output + + Console.SetOut(StandardOutStream); + Console.SetError(StandardOutStream); + break; + } + } + } + + public static readonly ConfigEntry ConfigConsoleEnabled = ConfigFile.CoreConfig.Bind( + "Logging.Console", "Enabled", + false, + "Enables showing a console for log output."); + + public static readonly ConfigEntry ConfigConsoleShiftJis = ConfigFile.CoreConfig.Bind( + "Logging.Console", "ShiftJisEncoding", + false, + "If true, console is set to the Shift-JIS encoding, otherwise UTF-8 encoding."); + } +} diff --git a/BepInEx/ConsoleUtil/ConsoleEncoding/ConsoleEncoding.Buffers.cs b/BepInEx/Console/Windows/ConsoleEncoding/ConsoleEncoding.Buffers.cs similarity index 100% rename from BepInEx/ConsoleUtil/ConsoleEncoding/ConsoleEncoding.Buffers.cs rename to BepInEx/Console/Windows/ConsoleEncoding/ConsoleEncoding.Buffers.cs diff --git a/BepInEx/ConsoleUtil/ConsoleEncoding/ConsoleEncoding.PInvoke.cs b/BepInEx/Console/Windows/ConsoleEncoding/ConsoleEncoding.PInvoke.cs similarity index 100% rename from BepInEx/ConsoleUtil/ConsoleEncoding/ConsoleEncoding.PInvoke.cs rename to BepInEx/Console/Windows/ConsoleEncoding/ConsoleEncoding.PInvoke.cs diff --git a/BepInEx/ConsoleUtil/ConsoleEncoding/ConsoleEncoding.cs b/BepInEx/Console/Windows/ConsoleEncoding/ConsoleEncoding.cs similarity index 100% rename from BepInEx/ConsoleUtil/ConsoleEncoding/ConsoleEncoding.cs rename to BepInEx/Console/Windows/ConsoleEncoding/ConsoleEncoding.cs diff --git a/BepInEx/ConsoleUtil/ConsoleWindow.cs b/BepInEx/Console/Windows/ConsoleWindow.cs similarity index 84% rename from BepInEx/ConsoleUtil/ConsoleWindow.cs rename to BepInEx/Console/Windows/ConsoleWindow.cs index 23f7ea4..497d6d6 100644 --- a/BepInEx/ConsoleUtil/ConsoleWindow.cs +++ b/BepInEx/Console/Windows/ConsoleWindow.cs @@ -7,28 +7,17 @@ using System; using System.IO; using System.Runtime.InteropServices; using System.Text; -using BepInEx.Configuration; +using BepInEx; +using Microsoft.Win32.SafeHandles; namespace UnityInjector.ConsoleUtil { internal class ConsoleWindow { - public static readonly ConfigEntry ConfigConsoleEnabled = ConfigFile.CoreConfig.Bind( - "Logging.Console", "Enabled", - false, - "Enables showing a console for log output."); - - public static readonly ConfigEntry ConfigConsoleShiftJis = ConfigFile.CoreConfig.Bind( - "Logging.Console", "ShiftJisEncoding", - false, - "If true, console is set to the Shift-JIS encoding, otherwise UTF-8 encoding."); - public static bool IsAttached { get; private set; } private static IntPtr _cOut; private static IntPtr _oOut; - public static TextWriter StandardOut { get; private set; } - public static void Attach() { if (IsAttached) @@ -53,6 +42,10 @@ namespace UnityInjector.ConsoleUtil if (!SetStdHandle(-11, _cOut)) throw new Exception("SetStdHandle() failed"); + + var originalOutStream = new FileStream(new SafeFileHandle(_oOut, false), FileAccess.Write); + ConsoleManager.StandardOutStream = new StreamWriter(originalOutStream, new UTF8Encoding(false)); + Init(); IsAttached = true; @@ -134,13 +127,13 @@ namespace UnityInjector.ConsoleUtil private static void Init() { var stdOut = Console.OpenStandardOutput(); - StandardOut = new StreamWriter(stdOut, Encoding.Default) + ConsoleManager.ConsoleStream = new StreamWriter(stdOut, Encoding.Default) { AutoFlush = true }; - Console.SetOut(StandardOut); - Console.SetError(StandardOut); + Console.SetOut(ConsoleManager.ConsoleStream); + Console.SetError(ConsoleManager.ConsoleStream); } [DllImport("kernel32.dll", SetLastError = true)] diff --git a/BepInEx/ConsoleUtil/Kon.cs b/BepInEx/Console/Windows/Kon.cs similarity index 79% rename from BepInEx/ConsoleUtil/Kon.cs rename to BepInEx/Console/Windows/Kon.cs index 2130348..e4f733e 100644 --- a/BepInEx/ConsoleUtil/Kon.cs +++ b/BepInEx/Console/Windows/Kon.cs @@ -64,27 +64,19 @@ namespace BepInEx.ConsoleUtil succeeded = false; if (!(conOut == INVALID_HANDLE_VALUE)) { - try + CONSOLE_SCREEN_BUFFER_INFO console_SCREEN_BUFFER_INFO; + if (!GetConsoleScreenBufferInfo(conOut, out console_SCREEN_BUFFER_INFO)) { - // FIXME: Windows console shouldn't be used in other OSs in the first place - CONSOLE_SCREEN_BUFFER_INFO console_SCREEN_BUFFER_INFO; - if (!GetConsoleScreenBufferInfo(conOut, out console_SCREEN_BUFFER_INFO)) - { - bool consoleScreenBufferInfo = GetConsoleScreenBufferInfo(GetStdHandle(-12), out console_SCREEN_BUFFER_INFO); - if (!consoleScreenBufferInfo) - consoleScreenBufferInfo = GetConsoleScreenBufferInfo(GetStdHandle(-10), out console_SCREEN_BUFFER_INFO); - - if (!consoleScreenBufferInfo) - if (Marshal.GetLastWin32Error() == 6 && !throwOnNoConsole) - return default(CONSOLE_SCREEN_BUFFER_INFO); - } - succeeded = true; - return console_SCREEN_BUFFER_INFO; - } - catch (EntryPointNotFoundException) - { - // Fails under unsupported OSes + bool consoleScreenBufferInfo = GetConsoleScreenBufferInfo(GetStdHandle(-12), out console_SCREEN_BUFFER_INFO); + if (!consoleScreenBufferInfo) + consoleScreenBufferInfo = GetConsoleScreenBufferInfo(GetStdHandle(-10), out console_SCREEN_BUFFER_INFO); + + if (!consoleScreenBufferInfo) + if (Marshal.GetLastWin32Error() == 6 && !throwOnNoConsole) + return default(CONSOLE_SCREEN_BUFFER_INFO); } + succeeded = true; + return console_SCREEN_BUFFER_INFO; } if (!throwOnNoConsole) diff --git a/BepInEx/ConsoleUtil/SafeConsole.cs b/BepInEx/Console/Windows/SafeConsole.cs similarity index 100% rename from BepInEx/ConsoleUtil/SafeConsole.cs rename to BepInEx/Console/Windows/SafeConsole.cs diff --git a/BepInEx/Logging/ConsoleLogListener.cs b/BepInEx/Logging/ConsoleLogListener.cs index 413af9c..0f4df61 100644 --- a/BepInEx/Logging/ConsoleLogListener.cs +++ b/BepInEx/Logging/ConsoleLogListener.cs @@ -1,6 +1,5 @@ using System; using BepInEx.Configuration; -using BepInEx.ConsoleUtil; namespace BepInEx.Logging { @@ -16,9 +15,9 @@ namespace BepInEx.Logging string log = $"[{eventArgs.Level,-7}:{((ILogSource)sender).SourceName,10}] {eventArgs.Data}\r\n"; - Kon.ForegroundColor = eventArgs.Level.GetConsoleColor(); + ConsoleManager.SetConsoleColor(eventArgs.Level.GetConsoleColor()); Console.Write(log); - Kon.ForegroundColor = ConsoleColor.Gray; + ConsoleManager.SetConsoleColor(ConsoleColor.Gray); } public void Dispose() { }