From 2cf257571cf102f8071a8e483bec4c024b8d99b3 Mon Sep 17 00:00:00 2001 From: Meivyn <793322+Meivyn@users.noreply.github.com> Date: Fri, 19 May 2023 13:08:15 -0400 Subject: [PATCH 1/3] Cleanup of console code --- IPA.Loader/Logging/ConsoleWindow.cs | 77 +++++++++++------------------ 1 file changed, 29 insertions(+), 48 deletions(-) diff --git a/IPA.Loader/Logging/ConsoleWindow.cs b/IPA.Loader/Logging/ConsoleWindow.cs index 89f07fb2..5f72e6dc 100644 --- a/IPA.Loader/Logging/ConsoleWindow.cs +++ b/IPA.Loader/Logging/ConsoleWindow.cs @@ -26,10 +26,10 @@ namespace IPA.Logging { bool consoleAttached = true; if (alwaysCreateNewConsole - || (AttachConsole(AttachParent) == 0 + || (AttachConsole(AttachParent) && Marshal.GetLastWin32Error() != ErrorAccessDenied)) { - consoleAttached = AllocConsole() != 0; + consoleAttached = AllocConsole(); } if (consoleAttached) @@ -39,7 +39,7 @@ namespace IPA.Logging } } - public static void InitializeStreams() + private static void InitializeStreams() { InitializeOutStream(); InitializeInStream(); @@ -62,7 +62,7 @@ namespace IPA.Logging if (!SetConsoleMode(handle, mode)) { UseVTEscapes = false; - Console.Error.WriteLine("Could not enable VT100 escape code processing (maybe you're running an old Windows?): " + + Console.Error.WriteLine("Could not enable VT100 escape code processing (maybe you're running an old Windows?): " + new Win32Exception(Marshal.GetLastWin32Error()).Message); } } @@ -87,18 +87,11 @@ namespace IPA.Logging private static FileStream CreateFileStream(string name, uint win32DesiredAccess, uint win32ShareMode, FileAccess dotNetFileAccess, out SafeFileHandle handle) { - var file = new SafeFileHandle(CreateFileW(name, win32DesiredAccess, win32ShareMode, IntPtr.Zero, OpenExisting, FileAttributeNormal, IntPtr.Zero), true); + var file = new SafeFileHandle(CreateFile(name, win32DesiredAccess, win32ShareMode, IntPtr.Zero, OpenExisting, FileAttributeNormal, IntPtr.Zero), true); if (!file.IsInvalid) { handle = file; -#if NET4 var fs = new FileStream(file, dotNetFileAccess); -#elif NET3 -#pragma warning disable CS0618 - // this is marked obsolete, and shouldn't need to be used, but the constructor used in .NET 4 doesn't exist in Unity's mscorlib.dll - var fs = new FileStream(file.DangerousGetHandle(), dotNetFileAccess); -#pragma warning restore -#endif return fs; } @@ -106,43 +99,31 @@ namespace IPA.Logging return null; } -#region Win API Functions and Constants - [DllImport("kernel32.dll", - EntryPoint = "AllocConsole", - SetLastError = true, - CharSet = CharSet.Auto, - CallingConvention = CallingConvention.StdCall)] - private static extern int AllocConsole(); - - [DllImport("kernel32.dll", - EntryPoint = "AttachConsole", - SetLastError = true, - CharSet = CharSet.Auto, - CallingConvention = CallingConvention.StdCall)] - private static extern uint AttachConsole(uint dwProcessId); - - [DllImport("kernel32.dll", - EntryPoint = "CreateFileW", - SetLastError = true, - CharSet = CharSet.Unicode, - CallingConvention = CallingConvention.StdCall)] - private static extern IntPtr CreateFileW( - string lpFileName, - uint dwDesiredAccess, - uint dwShareMode, - IntPtr lpSecurityAttributes, - uint dwCreationDisposition, - uint dwFlagsAndAttributes, - IntPtr hTemplateFile - ); - - [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + #region Win API Functions and Constants + + [DllImport("kernel32.dll")] + [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] + private static extern bool AllocConsole(); + + [DllImport("kernel32.dll")] + [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] + private static extern bool AttachConsole(int dwProcessId); + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] + [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] + private static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode, + IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile); + + [DllImport("kernel32.dll", SetLastError = true)] + [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] private static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode); - [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + [DllImport("kernel32.dll", SetLastError = true)] + [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] private static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode); - [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + [DllImport("kernel32.dll")] + [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] private static extern IntPtr GetStdHandle(int nStdHandle); private const uint EnableVTProcessing = 0x0004; @@ -154,9 +135,9 @@ namespace IPA.Logging private const uint OpenExisting = 0x00000003; private const uint FileAttributeNormal = 0x80; private const uint ErrorAccessDenied = 5; - - private const uint AttachParent = 0xFFFFFFFF; -#endregion + private const int AttachParent = -1; + + #endregion } } \ No newline at end of file From 6968ca9783dc121e84fd14f3d002cc7e64b2c712 Mon Sep 17 00:00:00 2001 From: Meivyn <793322+Meivyn@users.noreply.github.com> Date: Fri, 19 May 2023 13:10:15 -0400 Subject: [PATCH 2/3] Fix some issues with console initialization --- IPA.Loader/Logging/ConsoleWindow.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/IPA.Loader/Logging/ConsoleWindow.cs b/IPA.Loader/Logging/ConsoleWindow.cs index 5f72e6dc..648fd73e 100644 --- a/IPA.Loader/Logging/ConsoleWindow.cs +++ b/IPA.Loader/Logging/ConsoleWindow.cs @@ -24,10 +24,8 @@ namespace IPA.Logging public static void Initialize(bool alwaysCreateNewConsole = false) { - bool consoleAttached = true; - if (alwaysCreateNewConsole - || (AttachConsole(AttachParent) - && Marshal.GetLastWin32Error() != ErrorAccessDenied)) + bool consoleAttached; + if (alwaysCreateNewConsole || !(consoleAttached = AttachConsole(AttachParent))) { consoleAttached = AllocConsole(); } From 4e48fe197e26c7a07a27ba1fe7be00bf9c202e2d Mon Sep 17 00:00:00 2001 From: Meivyn <793322+Meivyn@users.noreply.github.com> Date: Fri, 19 May 2023 13:22:34 -0400 Subject: [PATCH 3/3] Allow console attachment to a specific PID --- IPA.Injector/Injector.cs | 31 +++++++++++++++++++++++------ IPA.Loader/Logging/ConsoleWindow.cs | 7 +++---- docs/articles/command-line.md | 21 +++++++++++-------- 3 files changed, 41 insertions(+), 18 deletions(-) diff --git a/IPA.Injector/Injector.cs b/IPA.Injector/Injector.cs index 1ea7e058..401e9f71 100644 --- a/IPA.Injector/Injector.cs +++ b/IPA.Injector/Injector.cs @@ -43,8 +43,8 @@ namespace IPA.Injector try { - if (Environment.GetCommandLineArgs().Contains("--verbose")) - WinConsole.Initialize(); + var arguments = Environment.GetCommandLineArgs(); + MaybeInitializeConsole(arguments); SetupLibraryLoading(); @@ -63,8 +63,8 @@ namespace IPA.Injector // this is weird, but it prevents Mono from having issues loading the type. // IMPORTANT: NO CALLS TO ANY LOGGER CAN HAPPEN BEFORE THIS var unused = StandardLogger.PrintFilter; - #region // Above hack explaination - /* + #region // Above hack explanation + /* * Due to an unknown bug in the version of Mono that Unity uses, if the first access to StandardLogger * is a call to a constructor, then Mono fails to load the type correctly. However, if the first access is to * the above static property (or maybe any, but I don't really know) it behaves as expected and works fine. @@ -73,7 +73,7 @@ namespace IPA.Injector log.Debug("Initializing logger"); - SelfConfig.ReadCommandLine(Environment.GetCommandLineArgs()); + SelfConfig.ReadCommandLine(arguments); SelfConfig.Load(); DisabledConfig.Load(); @@ -106,6 +106,25 @@ namespace IPA.Injector } } + private static void MaybeInitializeConsole(string[] arguments) + { + var i = 0; + while (i < arguments.Length) + { + if (arguments[i++] == "--verbose") + { + if (i == arguments.Length) + { + WinConsole.Initialize(WinConsole.AttachParent); + return; + } + + WinConsole.Initialize(int.TryParse(arguments[i], out int processId) ? processId : WinConsole.AttachParent); + return; + } + } + } + private static void EnsureDirectories() { string path; @@ -260,7 +279,7 @@ namespace IPA.Injector using var ascModule = VirtualizedModule.Load(ascPath); ascModule.Virtualize(cAsmName, () => bkp?.Add(ascPath)); } - catch (Exception e) + catch (Exception e) { injector.Error($"Could not virtualize {ascPath}"); if (SelfConfig.Debug_.ShowHandledErrorStackTraces_) diff --git a/IPA.Loader/Logging/ConsoleWindow.cs b/IPA.Loader/Logging/ConsoleWindow.cs index 648fd73e..5284b201 100644 --- a/IPA.Loader/Logging/ConsoleWindow.cs +++ b/IPA.Loader/Logging/ConsoleWindow.cs @@ -22,10 +22,10 @@ namespace IPA.Logging internal static bool IsInitialized; - public static void Initialize(bool alwaysCreateNewConsole = false) + public static void Initialize(int processId, bool alwaysCreateNewConsole = false) { bool consoleAttached; - if (alwaysCreateNewConsole || !(consoleAttached = AttachConsole(AttachParent))) + if (alwaysCreateNewConsole || !(consoleAttached = AttachConsole(processId))) { consoleAttached = AllocConsole(); } @@ -132,9 +132,8 @@ namespace IPA.Logging private const uint FileShareWrite = 0x00000002; private const uint OpenExisting = 0x00000003; private const uint FileAttributeNormal = 0x80; - private const uint ErrorAccessDenied = 5; - private const int AttachParent = -1; + internal const int AttachParent = -1; #endregion } diff --git a/docs/articles/command-line.md b/docs/articles/command-line.md index ea3e846c..da9cce06 100644 --- a/docs/articles/command-line.md +++ b/docs/articles/command-line.md @@ -26,6 +26,16 @@ Here's a quick list of what they are and what they do. > Makes a console appear with log information at startup. > + > Optionally, an explicit process ID can be specified to start the game with an external console. This allows it to be + > launched via Steam without being interrupted by its "Allow game launch?" if launched directly from the `.exe`. + > + > Example for Beat Saber using PowerShell: + > + > ``` + > .\steam.exe -applaunch 620980 --verbose $PID + > ``` + > + > Do note that this isn't going to work from an elevated terminal. - `--debug` @@ -35,15 +45,13 @@ Here's a quick list of what they are and what they do. > This option also forces BSIPA to show all debug messages in the console, as well as where they were called. > > This overrides the config settings `Debug.ShowDebug` and `Debug.ShowCallSource`. - > - `--trace` - + > Enables trace level messages. By default, they do not ever enter the message queue, and thus cost almost nothing. > When this or the config option is used, they are added and logged with the same rules as Debug messages. > > This overrides the config setting `Debug.ShowTrace`. - > - `--mono-debug` @@ -53,7 +61,6 @@ Here's a quick list of what they are and what they do. > debugger server running on port 10000 on `localhost`. > > Implies `--debug`. - > - `--server` @@ -62,7 +69,6 @@ Here's a quick list of what they are and what they do. > When paired with `--mono-debug`, this option makes the Mono soft debugger act in server mode. It begins listening on > port 10000 on any address, and will pause startup (with no window) until a debugger is connected. I recommend using > SDB, but that is a command line debugger and a lot of people don't care for those. - > - `--no-yeet` @@ -73,7 +79,6 @@ Here's a quick list of what they are and what they do. > behaviour is disabled. > > Overrides the config setting `YeetMods`. - > - `--condense-logs` @@ -85,11 +90,11 @@ Here's a quick list of what they are and what they do. > Overrides the config setting `Debug.CondenseModLogs`. - `--plugin-logs` - + > Causes each plugins' log messages to be written to files in their own folder for ease of debugging. > > This was the default through 4.1.6, however is now disabled by default. > > Overrides the config setting `Debug.CreateModLogs`. - + ***