erikest

StdOut redirection with Console and GUI support

Jan 30th, 2012
2,421
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 12.38 KB | None | 0 0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Data;
  5. using System.Drawing;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Windows.Forms;
  9. using System.Diagnostics;
  10. using System.IO;
  11. using System.IO.Pipes;
  12. using System.Runtime.InteropServices;
  13. using System.Text;
  14. using System.Threading;
  15. using Microsoft.Win32.SafeHandles;
  16.  
  17. namespace ConsoleRedirector
  18. {
  19.     public partial class Form1 : Form
  20.     {
  21.         public Form1()
  22.         {
  23.             InitializeComponent();
  24.  
  25.             ConsoleRedirector.attach(null, false);
  26.             Console.WriteLine("Still goes to the console");
  27.             ConsoleRedirector.detatch();
  28.             Console.WriteLine("So does this");
  29.             ConsoleRedirector.attach(null, true);
  30.             Console.WriteLine("But not this");
  31.             ConsoleRedirector.detatch();
  32.         }
  33.  
  34.         void WriteStdOutputToTextBox(object sender, ProgressChangedEventArgs e)
  35.         {
  36.             txtFromStandardOut.AppendText((string)e.UserState);
  37.         }
  38.  
  39.         private void btnSendToConsole_Click(object sender, EventArgs e)
  40.         {
  41.             Console.WriteLine(txtToStdOut.Text);
  42.         }
  43.  
  44.         private void btnDetach_Click(object sender, EventArgs e)
  45.         {
  46.             ConsoleRedirector.detatch();
  47.         }
  48.  
  49.         private void btnAttach_Click(object sender, EventArgs e)
  50.         {
  51.            
  52.             ConsoleRedirector.attach(WriteStdOutputToTextBox, true);
  53.         }
  54.  
  55.     }
  56.  
  57.     public class ConsoleRedirector : IDisposable
  58.     {
  59.         private static ConsoleRedirector _instance;
  60.  
  61.         public static void attach(ProgressChangedEventHandler handler, bool forceConsoleRedirection)
  62.         {
  63.             Debug.Assert(null == _instance);
  64.             _instance = new ConsoleRedirector(handler, forceConsoleRedirection);
  65.  
  66.         }
  67.  
  68.         public static void detatch()
  69.         {
  70.             _instance.Dispose();
  71.             _instance = null;
  72.         }
  73.  
  74.         public static bool isAttached
  75.         {
  76.             get
  77.             {
  78.                 return null != _instance;
  79.             }
  80.         }
  81.  
  82.         private static void ResetConsoleOutStream()
  83.         {
  84.             //Force console to recreate its output stream the next time Write/WriteLine is called
  85.             typeof(Console).GetField("_out", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic).SetValue(null, null);
  86.         }
  87.  
  88.         private const int PERIOD = 500;
  89.         private const int BUFFER_SIZE = 4096;
  90.         private volatile bool _isDisposed;
  91.         private BackgroundWorker _worker;
  92.         private readonly IntPtr _stdout;
  93.         private readonly Mutex _sync;
  94.         private readonly System.Threading.Timer _timer;
  95.         private readonly char[] _buffer;
  96.         private readonly AnonymousPipeServerStream _outServer;
  97.         private readonly TextReader _outClient;
  98.         private readonly bool _forceConsoleRedirection;
  99.  
  100.         private StreamWriter _consoleStandardOut;
  101.  
  102.         private ConsoleRedirector(ProgressChangedEventHandler handler, bool forceConsoleRedirection)
  103.         {
  104.             bool ret;
  105.             _forceConsoleRedirection = forceConsoleRedirection;
  106.  
  107.             if (!_forceConsoleRedirection)
  108.             {
  109.                 //Make sure Console._out is initialized before we redirect stdout, so the redirection won't affect it
  110.                 TextWriter temp = Console.Out;
  111.             }
  112.  
  113.             AnonymousPipeClientStream client;
  114.            
  115.             _worker = new BackgroundWorker();
  116.             _worker.ProgressChanged += handler;
  117.             _worker.DoWork += _worker_DoWork;
  118.             _worker.WorkerReportsProgress = true;
  119.  
  120.             _stdout = GetStdHandle(STD_OUTPUT_HANDLE);
  121.            
  122.             _sync = new Mutex();
  123.             _buffer = new char[BUFFER_SIZE];
  124.  
  125.             _outServer = new AnonymousPipeServerStream(PipeDirection.Out);
  126.             client = new AnonymousPipeClientStream(PipeDirection.In, _outServer.ClientSafePipeHandle);
  127.             Debug.Assert(_outServer.IsConnected);
  128.             _outClient = new StreamReader(client, Encoding.Default);
  129.             ret = SetStdHandle(STD_OUTPUT_HANDLE, _outServer.SafePipeHandle.DangerousGetHandle());
  130.             Debug.Assert(ret);
  131.  
  132.             if (_forceConsoleRedirection)
  133.             {
  134.                 ResetConsoleOutStream(); //calls to Console.Write/WriteLine will now get made against the redirected stream
  135.             }
  136.  
  137.             _worker.RunWorkerAsync(_outClient);
  138.  
  139.             _timer = new System.Threading.Timer(flush, null, PERIOD, PERIOD);
  140.  
  141.         }
  142.  
  143.         void _worker_DoWork(object sender, DoWorkEventArgs e)
  144.         {
  145.             BackgroundWorker worker = (BackgroundWorker)sender;
  146.             TextReader client = (TextReader)e.Argument;
  147.             try
  148.             {
  149.                 while (true)
  150.                 {
  151.                     int read = client.Read(_buffer, 0, BUFFER_SIZE);
  152.                     if (read > 0)
  153.                         worker.ReportProgress(0, new string(_buffer, 0, read));
  154.                 }
  155.             }
  156.             catch (ObjectDisposedException)
  157.             {
  158.                 // Pipe was closed... terminate
  159.                
  160.             }
  161.             catch (Exception ex)
  162.             {
  163.  
  164.             }
  165.         }
  166.  
  167.         private void flush(object state)
  168.         {
  169.             _outServer.Flush();
  170.         }
  171.  
  172.         public void Dispose()
  173.         {
  174.             Dispose(true);
  175.         }
  176.  
  177.         ~ConsoleRedirector()
  178.         {
  179.             Dispose(false);
  180.         }
  181.  
  182.         private void Dispose(bool disposing)
  183.         {
  184.             if (!_isDisposed)
  185.             {
  186.                 lock (_sync)
  187.                 {
  188.                     if (!_isDisposed)
  189.                     {
  190.                         _isDisposed = true;
  191.                         _timer.Change(Timeout.Infinite, Timeout.Infinite);
  192.                         _timer.Dispose();
  193.                         flush(null);
  194.                        
  195.                         try { SetStdHandle(STD_OUTPUT_HANDLE, _stdout);}
  196.                         catch (Exception) { }
  197.                         _outClient.Dispose();
  198.                         _outServer.Dispose();
  199.  
  200.                         if (_forceConsoleRedirection)
  201.                         {
  202.                             ResetConsoleOutStream(); //Calls to Console.Write/WriteLine will now get redirected to the original stdout stream
  203.                         }
  204.                                                
  205.                     }
  206.                 }
  207.             }
  208.         }
  209.  
  210.         private const int STD_OUTPUT_HANDLE = -11;
  211.  
  212.         [DllImport("kernel32.dll")]
  213.         [return: MarshalAs(UnmanagedType.Bool)]
  214.         private static extern bool SetStdHandle(int nStdHandle, IntPtr hHandle);
  215.  
  216.         [DllImport("kernel32.dll")]
  217.         private static extern IntPtr GetStdHandle(int nStdHandle);
  218.     }
  219.  
  220.     partial class Form1
  221.     {
  222.         /// <summary>
  223.         /// Required designer variable.
  224.         /// </summary>
  225.         private System.ComponentModel.IContainer components = null;
  226.  
  227.         /// <summary>
  228.         /// Clean up any resources being used.
  229.         /// </summary>
  230.         /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
  231.         protected override void Dispose(bool disposing)
  232.         {
  233.             if (disposing && (components != null))
  234.             {
  235.                 components.Dispose();
  236.             }
  237.             base.Dispose(disposing);
  238.         }
  239.  
  240.         #region Windows Form Designer generated code
  241.  
  242.         /// <summary>
  243.         /// Required method for Designer support - do not modify
  244.         /// the contents of this method with the code editor.
  245.         /// </summary>
  246.         private void InitializeComponent()
  247.         {
  248.             this.txtFromStandardOut = new System.Windows.Forms.TextBox();
  249.             this.txtToStdOut = new System.Windows.Forms.TextBox();
  250.             this.label1 = new System.Windows.Forms.Label();
  251.             this.btnSendToConsole = new System.Windows.Forms.Button();
  252.             this.btnDetach = new System.Windows.Forms.Button();
  253.             this.btnAttach = new System.Windows.Forms.Button();
  254.             this.label2 = new System.Windows.Forms.Label();
  255.             this.SuspendLayout();
  256.             //
  257.             // txtFromStandardOut
  258.             //
  259.             this.txtFromStandardOut.Location = new System.Drawing.Point(9, 25);
  260.             this.txtFromStandardOut.Multiline = true;
  261.             this.txtFromStandardOut.Name = "txtFromStandardOut";
  262.             this.txtFromStandardOut.Size = new System.Drawing.Size(193, 181);
  263.             this.txtFromStandardOut.TabIndex = 0;
  264.             //
  265.             // txtToStdOut
  266.             //
  267.             this.txtToStdOut.Location = new System.Drawing.Point(290, 25);
  268.             this.txtToStdOut.Name = "txtToStdOut";
  269.             this.txtToStdOut.Size = new System.Drawing.Size(116, 22);
  270.             this.txtToStdOut.TabIndex = 1;
  271.             //
  272.             // label1
  273.             //
  274.             this.label1.AutoSize = true;
  275.             this.label1.Location = new System.Drawing.Point(210, 30);
  276.             this.label1.Name = "label1";
  277.             this.label1.Size = new System.Drawing.Size(74, 17);
  278.             this.label1.TabIndex = 2;
  279.             this.label1.Text = "To Stdout:";
  280.             //
  281.             // btnSendToConsole
  282.             //
  283.             this.btnSendToConsole.Location = new System.Drawing.Point(215, 53);
  284.             this.btnSendToConsole.Name = "btnSendToConsole";
  285.             this.btnSendToConsole.Size = new System.Drawing.Size(191, 23);
  286.             this.btnSendToConsole.TabIndex = 3;
  287.             this.btnSendToConsole.Text = "Call Console.WriteLine";
  288.             this.btnSendToConsole.UseVisualStyleBackColor = true;
  289.             this.btnSendToConsole.Click += new System.EventHandler(this.btnSendToConsole_Click);
  290.             //
  291.             // btnDetach
  292.             //
  293.             this.btnDetach.Location = new System.Drawing.Point(331, 183);
  294.             this.btnDetach.Name = "btnDetach";
  295.             this.btnDetach.Size = new System.Drawing.Size(75, 23);
  296.             this.btnDetach.TabIndex = 4;
  297.             this.btnDetach.Text = "Detach";
  298.             this.btnDetach.UseVisualStyleBackColor = true;
  299.             this.btnDetach.Click += new System.EventHandler(this.btnDetach_Click);
  300.             //
  301.             // btnAttach
  302.             //
  303.             this.btnAttach.Location = new System.Drawing.Point(209, 183);
  304.             this.btnAttach.Name = "btnAttach";
  305.             this.btnAttach.Size = new System.Drawing.Size(75, 23);
  306.             this.btnAttach.TabIndex = 5;
  307.             this.btnAttach.Text = "Attach";
  308.             this.btnAttach.UseVisualStyleBackColor = true;
  309.             this.btnAttach.Click += new System.EventHandler(this.btnAttach_Click);
  310.             //
  311.             // label2
  312.             //
  313.             this.label2.AutoSize = true;
  314.             this.label2.Location = new System.Drawing.Point(6, 5);
  315.             this.label2.Name = "label2";
  316.             this.label2.Size = new System.Drawing.Size(201, 17);
  317.             this.label2.TabIndex = 6;
  318.             this.label2.Text = "Redirected StdOut shows here";
  319.             //
  320.             // Form1
  321.             //
  322.             this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
  323.             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
  324.             this.ClientSize = new System.Drawing.Size(416, 219);
  325.             this.Controls.Add(this.label2);
  326.             this.Controls.Add(this.btnAttach);
  327.             this.Controls.Add(this.btnDetach);
  328.             this.Controls.Add(this.btnSendToConsole);
  329.             this.Controls.Add(this.label1);
  330.             this.Controls.Add(this.txtToStdOut);
  331.             this.Controls.Add(this.txtFromStandardOut);
  332.             this.Name = "Form1";
  333.             this.Text = "Form1";
  334.             this.ResumeLayout(false);
  335.             this.PerformLayout();
  336.  
  337.         }
  338.  
  339.         #endregion
  340.  
  341.         private System.Windows.Forms.TextBox txtFromStandardOut;
  342.         private System.Windows.Forms.TextBox txtToStdOut;
  343.         private System.Windows.Forms.Label label1;
  344.         private System.Windows.Forms.Button btnSendToConsole;
  345.         private System.Windows.Forms.Button btnDetach;
  346.         private System.Windows.Forms.Button btnAttach;
  347.         private System.Windows.Forms.Label label2;
  348.     }
  349. }
Advertisement
Add Comment
Please, Sign In to add comment