WindowsConsoleDriver.cs 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. using System;
  2. using System.IO;
  3. using System.Linq;
  4. using System.Reflection;
  5. using System.Text;
  6. using BepInEx.ConsoleUtil;
  7. using HarmonyLib;
  8. using Microsoft.Win32.SafeHandles;
  9. using UnityInjector.ConsoleUtil;
  10. namespace BepInEx
  11. {
  12. internal class WindowsConsoleDriver : IConsoleDriver
  13. {
  14. public TextWriter StandardOut { get; private set; }
  15. public TextWriter ConsoleOut { get; private set; }
  16. public bool ConsoleActive { get; private set; }
  17. public bool ConsoleIsExternal => true;
  18. public void Initialize(bool alreadyActive)
  19. {
  20. ConsoleActive = alreadyActive;
  21. StandardOut = Console.Out;
  22. }
  23. // Apparently on some versions of Unity (e.g. 2018.4) using old mono causes crashes on game close if
  24. // IntPtr overload is used for file streams (check #139).
  25. // On the other hand, not all Unity games come with SafeFileHandle overload for FileStream
  26. // As such, we're trying to use SafeFileHandle when it's available and go back to IntPtr overload if not available
  27. private static readonly ConstructorInfo FileStreamCtor = new[]
  28. {
  29. AccessTools.Constructor(typeof(FileStream), new[] { typeof(SafeFileHandle), typeof(FileAccess) }),
  30. AccessTools.Constructor(typeof(FileStream), new[] { typeof(IntPtr), typeof(FileAccess) }),
  31. }.FirstOrDefault(m => m != null);
  32. private static FileStream OpenFileStream(IntPtr handle)
  33. {
  34. var fileHandle = new SafeFileHandle(handle, false);
  35. var ctorParams = AccessTools.ActualParameters(FileStreamCtor, new object[] { fileHandle, fileHandle.DangerousGetHandle(), FileAccess.Write });
  36. return (FileStream)Activator.CreateInstance(typeof(FileStream), ctorParams);
  37. }
  38. public void CreateConsole(uint codepage)
  39. {
  40. ConsoleWindow.Attach();
  41. // Make sure of ConsoleEncoding helper class because on some Monos
  42. // Encoding.GetEncoding throws NotImplementedException on most codepages
  43. // NOTE: We don't set Console.OutputEncoding because it resets any existing Console.Out writers
  44. ConsoleEncoding.ConsoleCodePage = codepage;
  45. // If stdout exists, write to it, otherwise make it the same as console out
  46. // Not sure if this is needed? Does the original Console.Out still work?
  47. var stdout = GetOutHandle();
  48. if (stdout == IntPtr.Zero)
  49. {
  50. StandardOut = TextWriter.Null;
  51. ConsoleOut = TextWriter.Null;
  52. return;
  53. }
  54. var originalOutStream = OpenFileStream(stdout);
  55. StandardOut = new StreamWriter(originalOutStream, new UTF8Encoding(false))
  56. {
  57. AutoFlush = true
  58. };
  59. var consoleOutStream = OpenFileStream(ConsoleWindow.ConsoleOutHandle);
  60. // Can't use Console.OutputEncoding because it can be null (i.e. not preference by user)
  61. ConsoleOut = new StreamWriter(consoleOutStream, ConsoleEncoding.OutputEncoding)
  62. {
  63. AutoFlush = true
  64. };
  65. ConsoleActive = true;
  66. }
  67. private IntPtr GetOutHandle()
  68. {
  69. switch (ConsoleManager.ConfigConsoleOutRedirectType.Value)
  70. {
  71. case ConsoleManager.ConsoleOutRedirectType.ConsoleOut:
  72. return ConsoleWindow.ConsoleOutHandle;
  73. case ConsoleManager.ConsoleOutRedirectType.StandardOut:
  74. return ConsoleWindow.OriginalStdoutHandle;
  75. case ConsoleManager.ConsoleOutRedirectType.Auto:
  76. default:
  77. return ConsoleWindow.OriginalStdoutHandle != IntPtr.Zero ? ConsoleWindow.OriginalStdoutHandle : ConsoleWindow.ConsoleOutHandle;
  78. }
  79. }
  80. public void DetachConsole()
  81. {
  82. ConsoleWindow.Detach();
  83. ConsoleOut.Close();
  84. ConsoleOut = null;
  85. ConsoleActive = false;
  86. }
  87. public void SetConsoleColor(ConsoleColor color)
  88. {
  89. SafeConsole.ForegroundColor = color;
  90. Kon.ForegroundColor = color;
  91. }
  92. public void SetConsoleTitle(string title)
  93. {
  94. ConsoleWindow.Title = title;
  95. }
  96. }
  97. }