/PoshConsole/Console.cs

http://poshconsole.codeplex.com · C# · 366 lines · 226 code · 50 blank · 90 comment · 19 complexity · 627bad2b3dc550079ecd371e32d6be8d MD5 · raw file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using System.Runtime.InteropServices;
  5. using System.Threading;
  6. namespace Huddled.PoshConsole
  7. {
  8. internal class NativeMethods {
  9. [DllImport("kernel32")]
  10. [return: MarshalAs(UnmanagedType.Bool)]
  11. public static extern bool AllocConsole();
  12. [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
  13. [return: MarshalAs(UnmanagedType.Bool)]
  14. public static extern bool FreeConsole();
  15. [DllImport("kernel32.dll")]
  16. public static extern IntPtr GetConsoleWindow();
  17. [DllImport("user32.dll")]
  18. [return: MarshalAs(UnmanagedType.Bool)]
  19. public static extern bool ShowWindow(IntPtr hWnd, ShowState nCmdShow);
  20. [DllImport("kernel32.dll")]
  21. [return: MarshalAs(UnmanagedType.Bool)]
  22. public static extern bool SetStdHandle(StdHandle nStdHandle, IntPtr hHandle);
  23. [DllImport("kernel32.dll", SetLastError = true)]
  24. [return: MarshalAs(UnmanagedType.Bool)]
  25. public static extern bool CloseHandle(IntPtr hHandle);
  26. [DllImport("kernel32.dll", SetLastError=true)]
  27. [return: MarshalAs(UnmanagedType.Bool)]
  28. public static extern bool CreatePipe(out IntPtr hReadPipe, out IntPtr hWritePipe, ref SECURITY_ATTRIBUTES lpPipeAttributes, uint nSize);
  29. [DllImport("kernel32.dll", SetLastError = true)]
  30. public static extern bool DuplicateHandle(IntPtr hSourceProcessHandle,
  31. IntPtr hSourceHandle, IntPtr hTargetProcessHandle, out IntPtr lpTargetHandle,
  32. uint dwDesiredAccess, bool bInheritHandle, uint dwOptions);
  33. [DllImport("kernel32.dll", SetLastError = true)]
  34. public static extern bool ReadFile(
  35. IntPtr hFile, // handle to file
  36. byte[] lpBuffer, // data buffer
  37. int nNumberOfBytesToRead, // number of bytes to read
  38. out int lpNumberOfBytesRead, // number of bytes read
  39. IntPtr overlapped // overlapped buffer
  40. );
  41. [DllImport("kernel32.dll", SetLastError = true)]
  42. public static extern int WriteFile(IntPtr hFile, byte[] buffer,
  43. int numBytesToWrite, out int numBytesWritten, IntPtr lpOverlapped);
  44. [DllImport("user32.dll", SetLastError = true)]
  45. public static extern int GetWindowLong(IntPtr hWnd, GwlIndex nIndex);
  46. [DllImport("user32.dll")]
  47. public static extern int SetWindowLong(IntPtr hWnd, GwlIndex nIndex, int dwNewLong);
  48. [DllImport("user32.dll")]
  49. public static extern bool SetLayeredWindowAttributes(IntPtr hwnd, int crKey, byte bAlpha, int dwFlags);
  50. public const int WS_EX_TRANSPARENT = 0x00000020;
  51. public const int WS_EX_LAYERED = 0x80000;
  52. public const int LWA_ALPHA = 0x2;
  53. public const int LWA_COLORKEY = 0x1;
  54. [StructLayout(LayoutKind.Sequential)]
  55. public struct SECURITY_ATTRIBUTES
  56. {
  57. public int nLength;
  58. public IntPtr lpSecurityDescriptor;
  59. [MarshalAs(UnmanagedType.Bool)]
  60. public bool bInheritHandle;
  61. }
  62. public enum GwlIndex : int
  63. {
  64. Id = (-12),
  65. Style = (-16),
  66. ExStyle = (-20)
  67. }
  68. public enum ShowState : int
  69. {
  70. SW_HIDE = 0
  71. /// and lots of others
  72. }
  73. public enum StdHandle : int
  74. {
  75. /// <summary>
  76. /// The standard input device
  77. /// </summary>
  78. INPUT_HANDLE = -10, //(DWORD)-10 The standard input device.
  79. /// <summary>
  80. /// The standard output device.
  81. /// </summary>
  82. OUTPUT_HANDLE = -11, //(DWORD)-11 The standard output device.
  83. /// <summary>
  84. /// The standard error device.
  85. /// </summary>
  86. ERROR_HANDLE = -12 //(DWORD)-12 The standard error device.
  87. }
  88. public const UInt32 DUPLICATE_SAME_ACCESS = 0x00000002;
  89. }
  90. public class Console : IDisposable
  91. {
  92. public delegate void OutputDelegate(string text);
  93. public event OutputDelegate WriteOutput;
  94. public event OutputDelegate WriteError;
  95. private EventWaitHandle EndOutput = new ManualResetEvent(false);
  96. private EventWaitHandle EndError = new ManualResetEvent(false);
  97. private Thread outputThread, errorThread;
  98. // A nice handle to our console window
  99. private IntPtr handle;
  100. // And our process
  101. private System.Diagnostics.Process process;
  102. // Track whether Dispose has been called.
  103. private bool disposed = false;
  104. private IntPtr stdOutRead, stdOutWrite, stdInRead, stdInWrite, stdErrRead, stdErrWrite;
  105. private IntPtr stdOutReadCopy, stdInWriteCopy, stdErrReadCopy;
  106. /// <summary>
  107. /// Initializes a new instance of the <see cref="Console"/> class.
  108. /// </summary>
  109. public Console()
  110. {
  111. // Make ourselves a nice console
  112. NativeMethods.AllocConsole();
  113. // hide the window ...
  114. handle = NativeMethods.GetConsoleWindow();
  115. NativeMethods.ShowWindow(handle, NativeMethods.ShowState.SW_HIDE);
  116. //NativeMethods.SetWindowLong(handle, NativeMethods.GwlIndex.ExStyle, (NativeMethods.GetWindowLong(handle, NativeMethods.GwlIndex.ExStyle) | NativeMethods.WS_EX_LAYERED | NativeMethods.WS_EX_TRANSPARENT));
  117. //NativeMethods.SetLayeredWindowAttributes(handle, 0, 0, NativeMethods.LWA_ALPHA);
  118. process = System.Diagnostics.Process.GetCurrentProcess();
  119. NativeMethods.SECURITY_ATTRIBUTES saAttr;
  120. // Set the bInheritHandle flag so pipe handles are inherited.
  121. saAttr.nLength = Marshal.SizeOf(typeof(NativeMethods.SECURITY_ATTRIBUTES));
  122. saAttr.bInheritHandle = true;
  123. saAttr.lpSecurityDescriptor = IntPtr.Zero;
  124. // The steps for redirecting STDOUT:
  125. // * Create anonymous pipe to be STDOUT for us.
  126. // * Set STDOUT of our process to be WRITE handle to the pipe.
  127. // * Create a (noninheritable) duplicate of the read handle and close the inheritable read handle.
  128. if (!NativeMethods.CreatePipe(out stdOutRead, out stdOutWrite, ref saAttr, 0))
  129. {
  130. System.Diagnostics.Trace.TraceError("Couldn't create the STDOUT pipe");
  131. }
  132. if (!NativeMethods.SetStdHandle(NativeMethods.StdHandle.OUTPUT_HANDLE, stdOutWrite))
  133. {
  134. System.Diagnostics.Trace.TraceError("Couldn't redirect STDOUT!");
  135. }
  136. // Create noninheritable read handle and close the inheritable read handle.
  137. if( !NativeMethods.DuplicateHandle(process.Handle, stdOutRead, process.Handle, out stdOutReadCopy, 0, false, NativeMethods.DUPLICATE_SAME_ACCESS))
  138. {
  139. System.Diagnostics.Trace.TraceError("Couldn't Duplicate STDOUT Handle");
  140. }
  141. NativeMethods.CloseHandle(stdOutRead);
  142. // For the output handles we need a thread to read them
  143. outputThread = new Thread(OutputThread);
  144. outputThread.Start();
  145. // The steps for redirecting STDERR are the same:
  146. // * Create anonymous pipe to be STDERR for us.
  147. // * Set STDERR of our process to be WRITE handle to the pipe.
  148. // * Create a (noninheritable) duplicate of the read handle and close the inheritable read handle.
  149. if (!NativeMethods.CreatePipe(out stdErrRead, out stdErrWrite, ref saAttr, 0))
  150. {
  151. System.Diagnostics.Trace.TraceError("Couldn't create the STDERR pipe");
  152. }
  153. if (!NativeMethods.SetStdHandle(NativeMethods.StdHandle.ERROR_HANDLE, stdErrWrite))
  154. {
  155. System.Diagnostics.Trace.TraceError("Couldn't redirect STDERR!");
  156. }
  157. // Create noninheritable read handle and close the inheritable read handle.
  158. if (!NativeMethods.DuplicateHandle(process.Handle, stdErrRead, process.Handle, out stdErrReadCopy, 0, false, NativeMethods.DUPLICATE_SAME_ACCESS))
  159. {
  160. System.Diagnostics.Trace.TraceError("Couldn't Duplicate STDERR Handle");
  161. }
  162. NativeMethods.CloseHandle(stdErrRead);
  163. // For the output handles we need a thread to read them
  164. errorThread = new Thread(ErrorThread);
  165. errorThread.Start();
  166. // The steps for redirecting STDIN:
  167. // * Create anonymous pipe to be STDIN for us.
  168. // * Set STDIN of our process to be READ handle to the pipe.
  169. // * Create a (noninheritable) duplicate of the WRITE handle and close the inheritable WRITE handle.
  170. if (!NativeMethods.CreatePipe(out stdInRead, out stdInWrite, ref saAttr, 0))
  171. {
  172. System.Diagnostics.Trace.TraceError("Couldn't create the StdIn pipe");
  173. }
  174. if (!NativeMethods.SetStdHandle(NativeMethods.StdHandle.INPUT_HANDLE, stdInRead))
  175. {
  176. System.Diagnostics.Trace.TraceError("Couldn't redirect StdIn!");
  177. }
  178. // Create noninheritable read handle and close the inheritable read handle.
  179. if (!NativeMethods.DuplicateHandle(process.Handle, stdInWrite, process.Handle, out stdInWriteCopy, 0, false, NativeMethods.DUPLICATE_SAME_ACCESS))
  180. {
  181. System.Diagnostics.Trace.TraceError("Couldn't Duplicate StdIn Handle");
  182. }
  183. NativeMethods.CloseHandle(stdInWrite);
  184. //if (!NativeMethods.CreatePipe(out stdInRead, out stdInWrite, ref saAttr, 0))
  185. //{
  186. // System.Diagnostics.Trace.TraceError("Couldn't create a pipe");
  187. //}
  188. //buffer.WriteOutput(this.myUI.RawUI.ForegroundColor, myUI.RawUI.BackgroundColor, System.Console.In.ReadToEnd(), true);
  189. //buffer.WriteOutput(this.myUI.RawUI.ForegroundColor, myUI.RawUI.BackgroundColor, System.Console.In.ReadToEnd(), true);
  190. }
  191. /// <summary>
  192. /// The OutputThread ThreadStart delegate
  193. /// </summary>
  194. private void OutputThread()
  195. {
  196. int BytesRead;
  197. byte[] BufBytes = new byte[4096];
  198. // consider wrapping this in a System.IO.FileStream
  199. try
  200. {
  201. while (NativeMethods.ReadFile(stdOutReadCopy, BufBytes, 4096, out BytesRead, IntPtr.Zero))
  202. {
  203. if (WriteOutput != null)
  204. {
  205. WriteOutput(System.Text.UTF8Encoding.Default.GetString(BufBytes, 0, BytesRead));
  206. }
  207. }
  208. }
  209. catch (ThreadAbortException){}
  210. finally
  211. {
  212. NativeMethods.CloseHandle(stdOutWrite);
  213. NativeMethods.CloseHandle(stdOutReadCopy);
  214. }
  215. }
  216. /// <summary>
  217. /// The ErrorThread ThreadStart delegate
  218. /// </summary>
  219. private void ErrorThread()
  220. {
  221. int BytesRead;
  222. byte[] BufBytes = new byte[4096];
  223. // consider wrapping this in a System.IO.FileStream
  224. try
  225. {
  226. while (NativeMethods.ReadFile(stdErrReadCopy, BufBytes, 4096, out BytesRead, IntPtr.Zero))
  227. {
  228. if (WriteError != null)
  229. {
  230. WriteError(System.Text.UTF8Encoding.Default.GetString(BufBytes, 0, BytesRead));
  231. }
  232. }
  233. }
  234. catch (ThreadAbortException) { }
  235. finally
  236. {
  237. NativeMethods.CloseHandle(stdErrWrite);
  238. NativeMethods.CloseHandle(stdErrReadCopy);
  239. }
  240. }
  241. /// <summary>
  242. /// Writes the input.
  243. /// </summary>
  244. /// <param name="input">The input.</param>
  245. public void WriteInput(string input)
  246. {
  247. byte[] bytes = System.Text.UTF8Encoding.Default.GetBytes(input);
  248. int written;
  249. int hresult = NativeMethods.WriteFile(stdInWriteCopy, bytes, bytes.Length, out written, IntPtr.Zero);
  250. if ( hresult != 1 )
  251. {
  252. throw new Exception("Error Writing to StdIn, HRESULT: " + hresult.ToString());
  253. }
  254. }
  255. /// <summary>
  256. /// Implement IDisposable
  257. /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
  258. /// </summary>
  259. public void Dispose()
  260. {
  261. Dispose(true);
  262. // This object will be cleaned up by the Dispose method. Therefore, we call GC.SupressFinalize
  263. // to tell the runtime we dont' need to be finalized (we would clean up twice)
  264. GC.SuppressFinalize(this);
  265. }
  266. /// <summary>
  267. /// Handles actual cleanup actions, under two different scenarios
  268. /// </summary>
  269. /// <param name="disposing">if set to <c>true</c> we've been called directly or
  270. /// indirectly by user code and can clean up both managed and unmanaged resources.
  271. /// Otherwise it's been called from the destructor/finalizer and we can't
  272. /// reference other managed objects (they might already be disposed).
  273. ///</param>
  274. private void Dispose(bool disposing)
  275. {
  276. // Check to see if Dispose has already been called.
  277. if (!this.disposed)
  278. {
  279. // // If disposing equals true, dispose all managed resources ALSO.
  280. if (disposing){
  281. errorThread.Abort();
  282. outputThread.Abort();
  283. //WriteInput("\n");
  284. byte[] bytes = System.Text.UTF8Encoding.Default.GetBytes("\n" + (char)26); int written;
  285. NativeMethods.WriteFile(stdErrWrite, bytes, bytes.Length, out written, IntPtr.Zero);
  286. NativeMethods.WriteFile(stdOutWrite, bytes, bytes.Length, out written, IntPtr.Zero);
  287. //errorThread.Join();
  288. //outputThread.Join();
  289. }
  290. // Clean up UnManaged resources
  291. // If disposing is false, only the following code is executed.
  292. NativeMethods.FreeConsole();
  293. //NativeMethods.CloseHandle(stdOutWrite);
  294. //NativeMethods.CloseHandle(stdOutReadCopy);
  295. //NativeMethods.CloseHandle(stdErrWrite);
  296. //NativeMethods.CloseHandle(stdErrReadCopy);
  297. NativeMethods.CloseHandle(stdInWriteCopy);
  298. NativeMethods.CloseHandle(stdInRead);
  299. }
  300. disposed = true;
  301. }
  302. /// <summary>
  303. /// Releases unmanaged resources and performs other cleanup operations before the
  304. /// <see cref="Console"/> is reclaimed by garbage collection.
  305. /// Use C# destructor syntax for finalization code.
  306. /// This destructor will run only if the Dispose method does not get called.
  307. /// </summary>
  308. /// <remarks>NOTE: Do not provide destructors in types derived from this class.</remarks>
  309. ~Console()
  310. {
  311. // Instead of cleaning up in BOTH Dispose() and here ...
  312. // We call Dispose(false) for the best readability and maintainability.
  313. Dispose(false);
  314. }
  315. }
  316. }