home *** CD-ROM | disk | FTP | other *** search
/ PC Welt 2005 November / PCWELT_11_2005.ISO / pcwsoft / Commandbar-Source.z.exe / ConsoleCtrl / ConsoleCtrl.cs < prev    next >
Encoding:
Text File  |  2002-06-10  |  21.0 KB  |  726 lines

  1. // Pavel Zolnikov[http://www.codeproject.com/script/profile/whos_who.asp?id=35980], 2002
  2.  
  3. using System;
  4. using System.Drawing;
  5. using System.Threading;
  6. using System.Collections;
  7. using System.Diagnostics;
  8. using System.Windows.Forms;
  9. using System.ComponentModel;
  10. using System.Drawing.Drawing2D;
  11. using System.Security.Permissions;
  12. using System.Runtime.InteropServices;
  13. using System.Runtime.CompilerServices;
  14.  
  15. namespace ZCommon
  16. {
  17.     /// <summary>
  18.     /// Custom control that incapsulates command prompt window handling
  19.     /// </summary>
  20.     public class ConsoleCtrl : UserControl
  21.     {
  22.         /// <summary>
  23.         /// Control that hosts command prompt window.
  24.         /// </summary>
  25.         /// <remarks>
  26.         /// Putting command prompt window inside clippingPanel makes it easy to clip unwanted user elements
  27.         /// such as frame borders and caption bar.
  28.         /// </remarks>
  29.         Panel        clippingPanel;
  30.  
  31.                ForegroundWatcher    foregroundWatcher        = new ForegroundWatcher();
  32.         static Process                consoleProcess            = null;
  33.         static ArrayList             consoleControls            = new ArrayList();
  34.         static readonly object        syncRoot                = typeof(ConsoleCtrl);
  35.         
  36.  
  37.         public ConsoleCtrl()
  38.         {
  39.             SetStyle( ControlStyles.AllPaintingInWmPaint, true );
  40.             SetStyle( ControlStyles.UserPaint, true );
  41.             SetStyle( ControlStyles.Selectable, true );
  42.             SetStyle( ControlStyles.ContainerControl, true );
  43.  
  44.             clippingPanel = new Panel();
  45.             clippingPanel.Parent = this;
  46.  
  47.             //position and size clippingPanel according to margins
  48.             Rectangle bounds = ClientRectangle;            
  49.             bounds.Inflate( -consoleMargin.Width , -consoleMargin.Width );
  50.             clippingPanel.Bounds = bounds;
  51.  
  52.             //need to handle these events to resize commandPrompt window accordingly
  53.             this.SizeChanged     += new EventHandler( ControlSizeChanged );
  54.             this.Move             += new EventHandler( ControlSizeChanged );
  55.             this.LocationChanged += new EventHandler( ControlSizeChanged );
  56.             this.Resize             += new EventHandler( ControlSizeChanged );
  57.  
  58.             lock(syncRoot)
  59.             {
  60.                 consoleControls.Add(this);
  61.                 if( consoleControls.Count == 1 )
  62.                 {
  63.                     OpenConsole();
  64.                 }
  65.             }
  66.  
  67.             ConsoleOwnerChanged += new EventHandler( ConsoleOwner_Changed );
  68.             foregroundWatcher.ConsoleGotFocus    += new EventHandler( ConsoleFocused_Changed );
  69.             foregroundWatcher.ConsoleLostFocus    += new EventHandler( ConsoleFocused_Changed );
  70.         }
  71.  
  72.         protected override void Dispose(bool disposing)
  73.         {
  74.             base.Dispose(disposing);
  75.  
  76.             lock(syncRoot)
  77.             {
  78.                 consoleControls.Remove(this);
  79.                 if( consoleControls.Count == 0 )
  80.                 {
  81.                     ConsoleOwner = null;
  82.                     CloseConsole();
  83.                 }
  84.                 else
  85.                     ConsoleOwner = (ConsoleCtrl)consoleControls[0];
  86.             }
  87.         }
  88.  
  89.         public IntPtr MainWindowHandle
  90.         {
  91.             set 
  92.             {
  93.                 Debug.Assert( Win32.IsWindow(value) );
  94.  
  95.                 mainWindowHandle = value; 
  96.                 foregroundWatcher.Start( mainWindowHandle );
  97.             }
  98.             get
  99.             {
  100.                 return mainWindowHandle;
  101.             }
  102.         }IntPtr mainWindowHandle;
  103.         
  104.         /// <summary>
  105.         /// Handle to the main commandPrompt window.
  106.         /// </summary>
  107.         static public IntPtr ConsoleWindowHandle
  108.         {
  109.             get 
  110.             { 
  111.                 return consoleWindowHandle; 
  112.             }
  113.         }static IntPtr consoleWindowHandle;
  114.  
  115.  
  116.         static public event EventHandler ConsoleLostFocus;
  117.         
  118.         static public event EventHandler ConsoleGotFocus;
  119.  
  120.         static public bool ConsoleFocused
  121.         {
  122.             get 
  123.             {
  124.                 return ConsoleOwner!=null && ConsoleOwner.foregroundWatcher.ConsoleFocused; 
  125.             }
  126.         }
  127.  
  128.         static void SetScreenBufferSize(Int16 size)
  129.         {
  130.             IntPtr handleOut = Win32.GetStdHandle((Int32)Win32.STD_HANDLE.OUTPUT);
  131.             Win32.CONSOLE_SCREEN_BUFFER_INFO csbi;
  132.             Win32.GetConsoleScreenBufferInfo( handleOut, out csbi );
  133.  
  134.             Win32.COORD newSize = csbi.dwSize;                
  135.             short windowWidth = (short) (csbi.srWindow.Right - csbi.srWindow.Left);
  136.             newSize.X = size > windowWidth  ? size : windowWidth;
  137.                 
  138.             if( ScreenBufferSize == newSize.X ) return;
  139.  
  140.             Win32.SetConsoleScreenBufferSize(handleOut, newSize);
  141.         }
  142.  
  143.         static public Int16 ScreenBufferSize
  144.         {
  145.             get
  146.             {
  147.                 IntPtr handleOut = Win32.GetStdHandle((Int32)Win32.STD_HANDLE.OUTPUT);
  148.                 Win32.CONSOLE_SCREEN_BUFFER_INFO csbi;
  149.                 Win32.GetConsoleScreenBufferInfo( handleOut, out csbi );
  150.                 return csbi.dwSize.X;
  151.             }
  152.             set
  153.             {
  154.                 SetScreenBufferSize(value);
  155.  
  156.                 if( ConsoleOwner != null )
  157.                 {
  158.                     ConsoleOwner.ResizeConsole();
  159.                     ConsoleOwner.Invalidate();
  160.                 }
  161.             }
  162.         }
  163.  
  164.         static Size ConosleVisibleNonClientSize
  165.         {
  166.             get
  167.             {
  168.                 Win32.WINDOWINFO wi;
  169.                 Win32.GetWindowInfo(ConsoleWindowHandle, out wi);//retreives prompt window info
  170.  
  171.                 //calculates size that is hidden to the left and up of prompt client area
  172.                 Size szConsoleHidden = new Size( 
  173.                     wi.rcClient.left - wi.rcWindow.left,
  174.                     wi.rcClient.top - wi.rcWindow.top );                 
  175.  
  176.                 Rectangle windowRect = Rectangle.FromLTRB( wi.rcWindow.left, wi.rcWindow.top, wi.rcWindow.right, wi.rcWindow.bottom ); 
  177.                 Rectangle clientRect = Rectangle.FromLTRB( wi.rcClient.left, wi.rcClient.top, wi.rcClient.right, wi.rcClient.bottom ); 
  178.  
  179.                 return windowRect.Size - clientRect.Size + szConsoleHidden;
  180.             }
  181.         }
  182.  
  183.         public bool ScreenBufferAutoGrow
  184.             
  185.         {
  186.             get
  187.             {
  188.                 return screenBufferAutoGrow;
  189.             }
  190.             set
  191.             {
  192.                 screenBufferAutoGrow = value;
  193.                 OnSizeChanged(EventArgs.Empty);
  194.             }
  195.         }
  196.         bool screenBufferAutoGrow = true;
  197.  
  198.         public Int16 FittingScreenBufferSize
  199.         { 
  200.             get
  201.             {
  202.                 return (Int16)( (ConsoleAreaSize.Width-ConosleVisibleNonClientSize.Width) / FontSize.Width - 1);
  203.             }
  204.         }
  205.  
  206.         /// <summary>
  207.         /// Copy of commandPrompt window's system menu.
  208.         /// </summary>
  209.         /// <remarks>
  210.         /// When command commandPrompt window is created we remove unwanted menu items from it such as Move, Resize etc.
  211.         /// The rest of menu with all Copy, Paste and Properties commands can be accessed through this property.
  212.         /// </remarks>
  213.         static public ContextMenu SystemMenu
  214.         {
  215.             get
  216.             {
  217.                 lock(syncRoot)
  218.                 {
  219.                     if( systemMenu == null )
  220.                     {
  221.                         systemMenu =new ContextMenu();
  222.                         IntPtr hMenu = Win32.GetSystemMenu( ConsoleWindowHandle, false );
  223.                         BuildMenu( hMenu, systemMenu );
  224.                     }
  225.                     return systemMenu;
  226.                 }
  227.             }
  228.         }static ContextMenu systemMenu;
  229.         /// <summary>
  230.         /// Emulates pressing Esc key so all previously typed symbols are cleared, selection operation canceled etc.
  231.         /// </summary>
  232.         static public void SendEscKey()
  233.         {
  234.             Win32.SendMessage( ConsoleWindowHandle, Win32.WM_KEYDOWN, 0x1B, 1 );
  235.         }
  236.  
  237.         /// <summary>
  238.         /// Sends string to commandPrompt as if it was typed from keyboard.
  239.         /// </summary>
  240.         /// <param name="text"></param>
  241.         /// <param name="showText">Controls whether this string will be visible after command execute.</param>
  242.         /// <param name="execute">Controls whether to press Enter at the end making command execute.</param>
  243.         static public void Output(String text, bool showText, bool execute)
  244.         {
  245.             Debug.Assert( showText | execute );//either show output string or do execute
  246.  
  247.             lock(syncRoot)
  248.             {
  249.                 Win32.COORD pos = new Win32.COORD();
  250.                 if( !showText )
  251.                     pos = RememberPosition();
  252.  
  253.                 foreach(char c in text)
  254.                 {
  255.                     OutputChar( c );
  256.                 }
  257.  
  258.                 if( execute )
  259.                     OutputChar( '\r' );            
  260.  
  261.                 if( !showText )
  262.                     RestorePosition( pos, true );
  263.             }
  264.         }
  265.  
  266.         /// <summary>
  267.         /// Calculates size of the commandPrompt font.
  268.         /// </summary>
  269.         /// <remarks>
  270.         /// Unfortunately I was unable to get exact information so it is only approximate.
  271.         /// The reason is that GetCurrentConsoleFont requires handle to screen buffer and there is no 
  272.         /// documented way to get current screen buffer of the commandPrompt ( no pair API to SetConsoleActiveScreenBuffer ).
  273.         /// I have this hiddenBuffer handle to screen buffer I create myself. It works fine for the default font size,
  274.         /// but if font was changed it is no good anymore.
  275.         /// </remarks>
  276.         static public Size FontSize
  277.         {
  278.             get
  279.             {
  280.                 lock(syncRoot)
  281.                 {
  282.                     if( fontSize.IsEmpty )
  283.                     {
  284.                         IntPtr        hiddenBuffer;
  285.                         //creates hidden commandPrompt screen buffer that we can use to get commandPrompt font size
  286.                         hiddenBuffer = Win32.CreateConsoleScreenBuffer( 
  287.                             Win32.DESIRED_ACCESS.GENERIC_READ | Win32.DESIRED_ACCESS.GENERIC_WRITE,
  288.                             Win32.FILE_SHARE.READ | Win32.FILE_SHARE.WRITE,
  289.                             null,
  290.                             Win32.CONSOLE_TEXTMODE_BUFFER,
  291.                             IntPtr.Zero );
  292.  
  293.                         Win32.CONSOLE_FONT_INFO cfi;
  294.                         bool b = Win32.GetCurrentConsoleFont( 
  295.                             hiddenBuffer,
  296.                             false,
  297.                             out cfi );
  298.  
  299.                         Win32.COORD szFont = Win32.GetConsoleFontSize( hiddenBuffer, cfi.nFont);
  300.                         fontSize = new Size( szFont.X, szFont.Y );
  301.  
  302.                         Win32.CloseHandle( hiddenBuffer );
  303.                     }
  304.  
  305.                     return fontSize;
  306.                 }
  307.             }
  308.         }static Size fontSize;    
  309.         /// <summary>
  310.         /// Changes width of the screen buffer by delta.
  311.         /// </summary>
  312.         /// <remarks>
  313.         /// It appears that cconsole screen buffer can always be increased, but decreased only if windows size is less the bufer size.
  314.         /// If this function fails to decrease size of the bufer by delta it attemts to decreas just by one.
  315.         /// This allows user to set size of the bufer that exactly matches size of the window.
  316.         /// </remarks>
  317.         static public void ResizeConsoleScreenBuffer( Int16 delta )
  318.         {
  319.             IntPtr hOut = Win32.GetStdHandle((Int32)Win32.STD_HANDLE.OUTPUT);
  320.             Win32.CONSOLE_SCREEN_BUFFER_INFO csbi;
  321.             Win32.GetConsoleScreenBufferInfo( hOut, out csbi );
  322.  
  323.             Win32.COORD newSize = csbi.dwSize;
  324.             newSize.X += delta;
  325.  
  326.             if( !Win32.SetConsoleScreenBufferSize(hOut, newSize) && delta<0 )
  327.             {
  328.                 int err = Marshal.GetLastWin32Error();
  329.                 newSize.X += (Int16)(-delta-1);
  330.                 bool b = Win32.SetConsoleScreenBufferSize(hOut, newSize);
  331.             }
  332.  
  333.             if( ConsoleOwner != null )
  334.             {
  335.                 ConsoleOwner.ResizeConsole();
  336.                 ConsoleOwner.Invalidate();
  337.             }
  338.         }
  339.  
  340.         /// <summary>
  341.         /// Raised when cmd.exe consoleProcess exits. Often when user types 'exit' in the command commandPrompt.
  342.         /// </summary>
  343.         static public event EventHandler ConsoleExited;
  344.  
  345.         static public bool ConsoleRunning
  346.         {
  347.             get
  348.             {
  349.                 return consoleRunning;
  350.             }
  351.         }static bool consoleRunning;
  352.  
  353.         static bool MyHandlerRoutine(Win32.ConsoleEvents eventId)
  354.         {
  355.             if( eventId == Win32.ConsoleEvents.CTRL_C_EVENT ||
  356.                 eventId == Win32.ConsoleEvents.CTRL_BREAK_EVENT )
  357.                 return true;//don't let default handler to call ExitProcess
  358.             else
  359.                 return false;
  360.         }
  361.         static Win32.ConsoleHandlerRoutine MyHandlerRoutineDelegate = new Win32.ConsoleHandlerRoutine(MyHandlerRoutine);
  362.  
  363.         /// <summary>
  364.         /// Starts cmd.exe consoleProcess and gets access to its commandPrompt.
  365.         /// </summary>
  366.         /// <param name="dir">Path to the initial file system folder for cmd.exe.</param>
  367.         static public void OpenConsole()
  368.         {
  369.             consoleRunning = true;
  370.  
  371.             Win32.AllocConsole();
  372.             consoleWindowHandle = Win32.GetConsoleWindow();
  373.             Win32.ShowWindow( consoleWindowHandle, 2 );
  374.  
  375.             Win32.SetConsoleCtrlHandler(MyHandlerRoutineDelegate, true );//disables ExitProcess on Ctrl+C and Ctrl+Break
  376.  
  377.             //removes not desirable system menu items
  378.             IntPtr hMenu = Win32.GetSystemMenu( ConsoleWindowHandle, false );
  379.  
  380.             Win32.RemoveMenu( hMenu, 0, 0x400 );//restore
  381.             Win32.RemoveMenu( hMenu, 0, 0x400 );//move
  382.             Win32.RemoveMenu( hMenu, 0, 0x400 );//size
  383.             Win32.RemoveMenu( hMenu, 0, 0x400 );//minimize
  384.             Win32.RemoveMenu( hMenu, 0, 0x400 );//maximize
  385.             Win32.RemoveMenu( hMenu, 0, 0x400 );//separator        
  386.             Win32.RemoveMenu( hMenu, 0, 0x400 );//close
  387.             
  388.             Win32.STARTUPINFO si = new Win32.STARTUPINFO();
  389.             Win32.PROCESS_INFORMATION pi = new Win32.PROCESS_INFORMATION();
  390.  
  391.             bool b = Win32.CreateProcessW( 
  392.                 null,
  393.                 "cmd.exe",
  394.                 IntPtr.Zero,
  395.                 IntPtr.Zero,
  396.                 false,
  397.                 0x0400400,
  398.                 IntPtr.Zero,
  399.                 null,
  400.                 si,
  401.                 pi );
  402.                 
  403.             consoleProcess = Process.GetProcessById( pi.dwProcessId );    
  404.  
  405.             //watching for cmd.exe consoleProcess exit
  406.             consoleProcess.EnableRaisingEvents = true;
  407.             consoleProcess.Exited += new EventHandler( consoleProcess_OnExited );
  408.         }
  409.  
  410.         /// <summary>
  411.         /// Ends cmd.exe consoleProcess, closes handles.
  412.         /// </summary>
  413.         static public void CloseConsole()
  414.         {
  415.             if( !ConsoleRunning ) return;
  416.  
  417.             Win32.FreeConsole();
  418.  
  419.             //Process has this MainWindowHandle property. Unfortunately if I use it, explorer.exe can not finish.
  420.             //explorer.exe consoleProcess just remains runnig with a couple of threads waiting for an Event.
  421.             //even if MainWindowHandle is accessed indirectly - through Process.CloseMainWindow it has the same effect.
  422.             //that is why I am not using it for closing commandPrompt consoleProcess.
  423.             if( ConsoleWindowHandle != IntPtr.Zero )
  424.             {
  425.                 //emulates click on 'Close' commandPrompt system menu item
  426.                 Win32.SendMessage( ConsoleWindowHandle, Win32.WM_COMMAND, 0xf060,0 );
  427.             }
  428.  
  429.             //last resort - if commandPrompt did not close, killing the consoleProcess
  430.             if( consoleProcess!=null && !consoleProcess.HasExited )
  431.             {
  432.                 try
  433.                 {
  434.                     consoleProcess.Kill();
  435.                 }
  436.                 catch( System.ComponentModel.Win32Exception e)
  437.                 {
  438.                     Debug.WriteLine(e.Message);
  439.                     Debug.WriteLine(e.ToString());
  440.                 }
  441.             }                
  442.         }  
  443.  
  444.         static public ConsoleCtrl ConsoleOwner
  445.         {
  446.             get
  447.             {
  448.                 return consoleOwner;
  449.             }
  450.             set
  451.             {
  452.                 lock(syncRoot)
  453.                 {
  454.                     consoleOwner = value;
  455.                     if( ConsoleOwnerChanged!= null ) ConsoleOwnerChanged(null, EventArgs.Empty);
  456.                 }
  457.             }
  458.         }static ConsoleCtrl consoleOwner;
  459.  
  460.         static public event EventHandler ConsoleOwnerChanged;
  461.  
  462.  
  463.         void ConsoleOwner_Changed(Object sender, EventArgs ea )
  464.         {
  465.             if( ConsoleOwner != this )
  466.             {
  467.                 this.Hide();
  468.             }
  469.             else
  470.             {
  471.                 //puts prompt window inside clippingPanel
  472.                 Win32.SetParent( ConsoleWindowHandle, clippingPanel.Handle );
  473.             }
  474.         }
  475.  
  476.         void ConsoleFocused_Changed(Object sender, EventArgs ea )
  477.         {
  478.             if( ConsoleOwner == this )
  479.             {
  480.                 Invalidate();
  481.             }
  482.  
  483.             if(  ConsoleFocused && ConsoleGotFocus  != null ) ConsoleGotFocus (this,ea);
  484.             if( !ConsoleFocused && ConsoleLostFocus != null ) ConsoleLostFocus(this,ea);
  485.         }
  486.  
  487.         void ControlSizeChanged(object sender, EventArgs ea)
  488.         {
  489.             if( ConsoleOwner != this ) return;
  490.  
  491.             if( ScreenBufferAutoGrow )
  492.             {            
  493.                 if( FittingScreenBufferSize > ScreenBufferSize )
  494.                     SetScreenBufferSize( FittingScreenBufferSize );
  495.             }
  496.  
  497.             ResizeConsole();
  498.             Invalidate();
  499.         }
  500.  
  501.         Size ConsoleAreaSize
  502.         {
  503.             get
  504.             {
  505.                 Rectangle r = ClientRectangle;
  506.                 r.Inflate(-consoleMargin.Width, -consoleMargin.Height);
  507.                 return r.Size;
  508.             }
  509.         }
  510.  
  511.         /// <summary>
  512.         /// Resizes prompt window according to the current size of the control.
  513.         /// </summary>
  514.         void ResizeConsole()
  515.         {
  516.             Win32.WINDOWINFO wi;
  517.             Win32.GetWindowInfo(ConsoleWindowHandle, out wi);//retreives prompt window info
  518.  
  519.             
  520.             //calculates size that is hidden to the left and up of prompt client area
  521.             Size szConsoleHidden = new Size( 
  522.                 wi.rcClient.left - wi.rcWindow.left,
  523.                 wi.rcClient.top - wi.rcWindow.top );                 
  524.  
  525.             Size szMaxConsoleClientArea = ConsoleAreaSize - ConosleVisibleNonClientSize;
  526.             
  527.             if( fontSize.Width != 0 )
  528.                 szMaxConsoleClientArea.Width -= (szMaxConsoleClientArea.Width % fontSize.Width);
  529.  
  530.             if( fontSize.Height != 0 )
  531.                 szMaxConsoleClientArea.Height -= (szMaxConsoleClientArea.Height % fontSize.Height);
  532.  
  533.             Size sz = szMaxConsoleClientArea + ConosleVisibleNonClientSize + szConsoleHidden;
  534.             
  535.             Win32.MoveWindow( ConsoleWindowHandle,
  536.                 - szConsoleHidden.Width,
  537.                 - szConsoleHidden.Height,
  538.                 sz.Width,
  539.                 sz.Height,
  540.                 true);
  541.  
  542.             //now we can get actual size of console window
  543.             Win32.GetWindowInfo(ConsoleWindowHandle, out wi);
  544.  
  545.             //resizing clippingPanel so it hides frame borders and caption of prompt 
  546.             clippingPanel.Width = wi.rcWindow.right-wi.rcWindow.left - (int)(wi.cxWindowBorders) - szConsoleHidden.Width - 2;
  547.             clippingPanel.Height = wi.rcWindow.bottom-wi.rcWindow.top - (int)(wi.cyWindowBorders) - szConsoleHidden.Height - 2;
  548.         }
  549.  
  550.         /// <summary>
  551.         /// Margins between prompt/clippingPanel and ConsoleCtrl bounds
  552.         /// </summary>
  553.         /// <remarks>
  554.         /// Set margins slightly larger then size of the border we paint around the prompt. 
  555.         /// This makes border nice and visible even if prompt text backround was changed.
  556.         /// Recalculated every time as we don't want to handle SystemInformation chenged events.
  557.         /// </remarks>
  558.         Size consoleMargin
  559.         {
  560.             get
  561.             {
  562.                 return new Size(
  563.                     SystemInformation.Border3DSize.Width<<1,
  564.                     SystemInformation.Border3DSize.Height<<1 );
  565.             }
  566.         }
  567.  
  568.  
  569.         /// <summary>
  570.         /// Paints border around prompt window.
  571.         /// </summary>
  572.         protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
  573.         {    
  574.             //calculate border rectangle
  575.             Rectangle border = clippingPanel.ClientRectangle;
  576.             border = RectangleToClient(clippingPanel.RectangleToScreen(border));
  577.             border.Inflate( consoleMargin.Width, consoleMargin.Height );
  578.             
  579.             if( !ConsoleFocused )
  580.                 //draws border around prompt window
  581.                 ControlPaint.DrawBorder3D( 
  582.                     e.Graphics,
  583.                     border,
  584.                     Border3DStyle.Etched,
  585.                     Border3DSide.All );
  586.             else
  587.                 ControlPaint.DrawBorder3D( 
  588.                     e.Graphics,
  589.                     border,
  590.                     Border3DStyle.Bump,
  591.                     Border3DSide.All );
  592.         }
  593.  
  594.         protected override void OnVisibleChanged(EventArgs ea)
  595.         {
  596.             base.OnVisibleChanged(ea);
  597.             if( Visible )
  598.             {
  599.                 ConsoleOwner = this;
  600.                 ResizeConsole();
  601.             }
  602.         }
  603.  
  604.  
  605.         /// <summary>
  606.         /// Forwards menu click to the commandPrompt window. (Sends WM_COMMAND with appropriate Id).
  607.         /// </summary>
  608.         static void SystemMenuItem_OnClick(Object sender, EventArgs ea)
  609.         {
  610.             TagMenuItem mi = (TagMenuItem)sender;
  611.             Win32.SendMessage( ConsoleWindowHandle, Win32.WM_COMMAND, (Int32)mi.Tag, 0 );
  612.  
  613.             fontSize = Size.Empty;//there is a chance that console font size was changed
  614.         }
  615.  
  616.         /// <summary>
  617.         ///     Recursively copies commandPrompt window's system menu so it can be accessed through SystemMenu property.
  618.         /// </summary>
  619.         static void BuildMenu(IntPtr hmenu, Menu menu)
  620.         {
  621.             for(Int32 i=0; i<Win32.GetMenuItemCount(hmenu);i++)
  622.             {
  623.                 Win32.MENUITEMINFO mii = new Win32.MENUITEMINFO();
  624.                 mii.fMask = Win32.MIIM.STRING |
  625.                     Win32.MIIM.ID |
  626.                     Win32.MIIM.SUBMENU;
  627.  
  628.                 bool b = Win32.GetMenuItemInfo( hmenu, i, true, mii );
  629.                 MenuItem item =  new TagMenuItem(
  630.                     mii.dwTypeData,
  631.                     mii.wID,
  632.                     new EventHandler(SystemMenuItem_OnClick) );
  633.                         
  634.                 menu.MenuItems.Add((int)i, item );
  635.                 if( mii.hSubMenu != IntPtr.Zero )
  636.                     BuildMenu( mii.hSubMenu, item );
  637.             }
  638.         }
  639.  
  640.         /// <summary>
  641.         /// Sends one character to commandPrompt as if it was typed from keyboard.
  642.         /// </summary>
  643.         static void OutputChar(char c)
  644.         {
  645.             IntPtr hIn = Win32.GetStdHandle( (Int32)Win32.STD_HANDLE.INPUT );
  646.             
  647.             Int32 cnt;
  648.             Win32.KEY_INPUT_RECORD r = new Win32.KEY_INPUT_RECORD();
  649.  
  650.             r.bKeyDown = true;
  651.             r.wRepeatCount = 1;
  652.             r.wVirtualKeyCode = 0;
  653.             r.wVirtualScanCode = 0;
  654.             r.UnicodeChar = c;
  655.             r.dwControlKeyState = 0;
  656.  
  657.             Win32.WriteConsoleInput( hIn ,r, 1, out cnt);//key down event
  658.  
  659.             r.bKeyDown = false;
  660.             Win32.WriteConsoleInput( hIn ,r, 1, out cnt);//key up
  661.         }
  662.  
  663.         /// <summary>
  664.         /// Current cursor position.
  665.         /// </summary>
  666.         static Win32.COORD RememberPosition()
  667.         {
  668.             IntPtr hOut = Win32.GetStdHandle((Int32)Win32.STD_HANDLE.OUTPUT);
  669.             Win32.CONSOLE_SCREEN_BUFFER_INFO csbi;
  670.             bool b = Win32.GetConsoleScreenBufferInfo( hOut, out csbi );
  671.             return csbi.dwCursorPosition;
  672.         }
  673.  
  674.         /// <summary>
  675.         /// Sets cursor position and erases commandPrompt screen that is after new position if requested.
  676.         /// </summary>
  677.         static void RestorePosition(Win32.COORD pos, bool erase)
  678.         {
  679.             IntPtr hOut = Win32.GetStdHandle((Int32)Win32.STD_HANDLE.OUTPUT);
  680.             Win32.CONSOLE_SCREEN_BUFFER_INFO csbi;
  681.             bool b = Win32.GetConsoleScreenBufferInfo( hOut, out csbi );
  682.             
  683.             b = Win32.SetConsoleCursorPosition( hOut, pos );
  684.  
  685.             if( erase )
  686.             {
  687.                 Int32 written;
  688.  
  689.                 //calculates number of characters we need to clean
  690.                 Int32 num = (csbi.dwSize.X) * (csbi.dwCursorPosition.Y - pos.Y ) + pos.X - csbi.dwCursorPosition.X;
  691.             
  692.                 //writes spaces to commandPrompt
  693.                 b = Win32.WriteConsole( hOut, new String(' ',num), num, out written, IntPtr.Zero );
  694.                 //have to set position again
  695.                 b = Win32.SetConsoleCursorPosition( hOut, pos );
  696.             }
  697.         }
  698.         /// <summary>
  699.         /// Complements Exited event.
  700.         /// </summary>
  701.         static void consoleProcess_OnExited(Object sender, EventArgs ea)
  702.         {
  703.             consoleRunning = false;
  704.  
  705.             if( ConsoleExited != null )
  706.                 ConsoleExited(null, EventArgs.Empty );            
  707.         }
  708.  
  709.         [SecurityPermissionAttribute(SecurityAction.LinkDemand, Unrestricted=true)]
  710.         protected override void WndProc(ref System.Windows.Forms.Message m)
  711.         {
  712.             if( m.Msg == 0x0002 )//WM_DESTROY
  713.                 Dispose(true);//idea here is to lose ownership of the console so console window will not be child of our window and not get destroyed. Dispose does just this
  714.  
  715.             base.WndProc(ref m);
  716.         }
  717.  
  718.         protected override void Select(bool directed, bool forward)
  719.         {
  720.             base.Select(directed, forward);
  721.             //as sideeffect also moves keybord focus to console.
  722.             Win32.SetParent( ConsoleWindowHandle, clippingPanel.Handle );
  723.         }
  724.     }
  725. }
  726.