You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

144 lines
5.1 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. using System;
  2. using System.ComponentModel;
  3. using System.IO;
  4. using System.Runtime.InteropServices;
  5. using Microsoft.Win32.SafeHandles;
  6. namespace IPA.Logging
  7. {
  8. // https://stackoverflow.com/a/48864902/3117125
  9. internal static class WinConsole
  10. {
  11. internal static TextWriter ConOut;
  12. internal static TextReader ConIn;
  13. private static SafeFileHandle outHandle;
  14. private static SafeFileHandle inHandle;
  15. internal static IntPtr OutHandle => outHandle.DangerousGetHandle();
  16. internal static IntPtr InHandle => inHandle.DangerousGetHandle();
  17. internal static bool IsInitialized;
  18. public static void Initialize(bool alwaysCreateNewConsole = true)
  19. {
  20. bool consoleAttached = true;
  21. if (alwaysCreateNewConsole
  22. || (AttachConsole(AttachParent) == 0
  23. && Marshal.GetLastWin32Error() != ErrorAccessDenied))
  24. {
  25. consoleAttached = AllocConsole() != 0;
  26. }
  27. if (consoleAttached)
  28. {
  29. InitializeStreams();
  30. IsInitialized = true;
  31. }
  32. }
  33. public static void InitializeStreams()
  34. {
  35. InitializeOutStream();
  36. InitializeInStream();
  37. }
  38. private static void InitializeOutStream()
  39. {
  40. var fs = CreateFileStream("CONOUT$", GenericWrite, FileShareWrite, FileAccess.Write, out outHandle);
  41. if (fs != null)
  42. {
  43. var writer = new StreamWriter(fs) { AutoFlush = true };
  44. ConOut = writer;
  45. Console.SetOut(writer);
  46. Console.SetError(writer);
  47. var handle = GetStdHandle(-11); // get stdout handle (should be CONOUT$ at this point)
  48. if (GetConsoleMode(handle, out var mode))
  49. {
  50. mode |= EnableVTProcessing;
  51. if (!SetConsoleMode(handle, mode))
  52. throw new Win32Exception(Marshal.GetLastWin32Error());
  53. }
  54. else
  55. throw new Win32Exception(Marshal.GetLastWin32Error());
  56. }
  57. }
  58. private static void InitializeInStream()
  59. {
  60. var fs = CreateFileStream("CONIN$", GenericRead, FileShareRead, FileAccess.Read, out inHandle);
  61. if (fs != null)
  62. {
  63. Console.SetIn(ConIn = new StreamReader(fs));
  64. }
  65. }
  66. private static FileStream CreateFileStream(string name, uint win32DesiredAccess, uint win32ShareMode,
  67. FileAccess dotNetFileAccess, out SafeFileHandle handle)
  68. {
  69. var file = new SafeFileHandle(CreateFileW(name, win32DesiredAccess, win32ShareMode, IntPtr.Zero, OpenExisting, FileAttributeNormal, IntPtr.Zero), true);
  70. if (!file.IsInvalid)
  71. {
  72. handle = file;
  73. var fs = new FileStream(file, dotNetFileAccess);
  74. return fs;
  75. }
  76. handle = null;
  77. return null;
  78. }
  79. #region Win API Functions and Constants
  80. [DllImport("kernel32.dll",
  81. EntryPoint = "AllocConsole",
  82. SetLastError = true,
  83. CharSet = CharSet.Auto,
  84. CallingConvention = CallingConvention.StdCall)]
  85. private static extern int AllocConsole();
  86. [DllImport("kernel32.dll",
  87. EntryPoint = "AttachConsole",
  88. SetLastError = true,
  89. CharSet = CharSet.Auto,
  90. CallingConvention = CallingConvention.StdCall)]
  91. private static extern uint AttachConsole(uint dwProcessId);
  92. [DllImport("kernel32.dll",
  93. EntryPoint = "CreateFileW",
  94. SetLastError = true,
  95. CharSet = CharSet.Unicode,
  96. CallingConvention = CallingConvention.StdCall)]
  97. private static extern IntPtr CreateFileW(
  98. string lpFileName,
  99. uint dwDesiredAccess,
  100. uint dwShareMode,
  101. IntPtr lpSecurityAttributes,
  102. uint dwCreationDisposition,
  103. uint dwFlagsAndAttributes,
  104. IntPtr hTemplateFile
  105. );
  106. [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  107. private static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode);
  108. [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  109. private static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode);
  110. [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  111. private static extern IntPtr GetStdHandle(int nStdHandle);
  112. private const uint EnableVTProcessing = 0x0004;
  113. private const uint GenericWrite = 0x40000000;
  114. private const uint GenericRead = 0x80000000;
  115. private const uint FileShareRead = 0x00000001;
  116. private const uint FileShareWrite = 0x00000002;
  117. private const uint OpenExisting = 0x00000003;
  118. private const uint FileAttributeNormal = 0x80;
  119. private const uint ErrorAccessDenied = 5;
  120. private const uint AttachParent = 0xFFFFFFFF;
  121. #endregion
  122. }
  123. }