將控制檯輸出重定向到textbox的dotnet類

//實現思想是使用windows api CreatePipe 創建一個匿名管道
//接收控制檯命令的輸出,併產生委託事件。
//具體實現見以下代碼:
 
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Threading;
namespace Hob.Toolbox
{
    /// <summary>
    /// 重定向的類
    /// </summary>
    public class Redirect : IDisposable
    {
        /// <summary>
        /// 重定向類的代碼
        /// </summary>
        public enum RdEventType
        {
            //錯誤事件
            ErrorEvent,
            //輸出事件
            DataEvent,
            //stop事件
            StopEvent
        }
        /// <summary>
        /// 事件參數
        /// </summary>
        public struct RdEventArgs
        {
            //code
            public RdEventType eventtype;
            //字符串(錯誤時代表錯誤信息,數據時代表數據字符串,stop時代表停止信息)
            public string info;
        };
        /// <summary>
        /// 錯誤處理事件類型
        /// </summary>
        /// <param name="code"></param>
        public delegate void RdEvent(RdEventArgs args);
        /// <summary>
        /// 構造函數1
        /// </summary>
        /// <param name="szCommand">命令行</param>
        /// <param name="szCurrentDirectory">當前目錄</param>
        public Redirect(string szCommand, string szCurrentDirectory)
        {
            m_szCommand = szCommand;
            m_szCurrentDirectory = szCurrentDirectory;
            m_running = false;
            //默認100毫秒
            m_dwMilliseconds = 100;
            m_processInfo = new PROCESS_INFORMATION();
            m_PipeData = new byte[BUF_SIZE];
            m_readtimer = new System.Windows.Forms.Timer();
            m_readtimer.Enabled = false;
            m_readtimer.Interval = (int)m_dwMilliseconds;
            m_readtimer.Tick += new EventHandler(timertick);
        }
        /// <summary>
        /// 構造函數2
        /// </summary>
        /// <param name="szCommand"></param>
        public Redirect(string szCommand)
            :this(szCommand,null)
        {
        }
        /// <summary>
        /// 構造函數3
        /// </summary>
        public Redirect()
            :this(null,null)
        {
        }
        #region windows api
        [StructLayout(LayoutKind.Sequential)]
        class PROCESS_INFORMATION
        {
            public IntPtr hProcess;
            public IntPtr hThread;
            public int dwProcessId;
            public int dwThreadId;
        }
        [StructLayout(LayoutKind.Sequential)]
        class SECURITY_ATTRIBUTES
        {
            public int nLength;
            public IntPtr lpSecurityDescriptor;
            public int bInheritHandle;
        }
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        class STARTUPINFO
        {
            public int cb;
            public string lpReserved;
            public string lpDesktop;
            public string lpTitle;
            public int dwX;
            public int dwY;
            public int dwXSize;
            public int dwYSize;
            public int dwXCountChars;
            public int dwYCountChars;
            public int dwFillAttribute;
            public int dwFlags;
            public short wShowWindow;
            public short cbReserved2;
            public IntPtr lpReserved2;
            public IntPtr hStdInput;
            public IntPtr hStdOutput;
            public IntPtr hStdError;
        }
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        static extern bool CreateProcess(string lpApplicationName, string lpCommandLine, SECURITY_ATTRIBUTES lpProcessAttributes,
            SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles, int dwCreationFlags, IntPtr lpEnvironment,
            string lpCurrentDirectory, STARTUPINFO lpStartupInfo, PROCESS_INFORMATION lpProcessInformation);
        [DllImport("kernel32.dll")]
        static extern bool CreatePipe(out IntPtr hReadPipe, out IntPtr hWritePipe,
           SECURITY_ATTRIBUTES lpPipeAttributes, uint nSize);
        [DllImport("kernel32.dll", EntryPoint = "PeekNamedPipe", SetLastError = true)]
        static extern bool PeekNamedPipe(IntPtr handle, byte[] buffer, uint nBufferSize, ref uint bytesRead,
                    ref uint bytesAvail, ref uint BytesLeftThisMessage);
        [DllImport("kernel32.dll")]
        static extern bool ReadFile(IntPtr hFile, byte[] lpBuffer,
           uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, IntPtr lpOverlapped);
        [DllImport("kernel32", SetLastError = true, ExactSpelling = true)]
        static extern Int32 WaitForSingleObject(IntPtr handle, Int32 milliseconds);
        [DllImport("kernel32.dll", SetLastError = true)][return: MarshalAs(UnmanagedType.Bool)]
        static extern bool CloseHandle(IntPtr hObject);
        [DllImport("kernel32.dll", SetLastError = true)][return: MarshalAs(UnmanagedType.Bool)]
        static extern bool TerminateProcess(IntPtr hProcess, uint uExitCode);
        const int STARTF_USESHOWWINDOW = 0x00000001;
        const int STARTF_USESTDHANDLES = 0x00000100;
        const short SW_HIDE = 0;
        const int WAIT_OBJECT_0 = 0;
        #endregion
        /// <summary>
        /// 錯誤報告事件
        /// </summary>
        public event RdEvent RdEventHandler;
        private IntPtr m_PipeReadHandle;
        private IntPtr m_PipeWriteHandle;
        private PROCESS_INFORMATION m_processInfo;
        byte[] m_PipeData;
        /// <summary>
        /// buffer的大小
        /// </summary>
        private const int BUF_SIZE = 8192;
        /// <summary>
        /// 啓動
        /// </summary>
        public void Run()
        {
            //如果已經運行
            if (m_running) return;
            SECURITY_ATTRIBUTES SecurityAttributes=new SECURITY_ATTRIBUTES();
            STARTUPINFO StartupInfo=new STARTUPINFO();
            bool Success;
            //創建pipe
            SecurityAttributes.nLength = Marshal.SizeOf(SecurityAttributes);
            SecurityAttributes.bInheritHandle = 1;
            SecurityAttributes.lpSecurityDescriptor =IntPtr.Zero;
            Success = CreatePipe(out m_PipeReadHandle, out m_PipeWriteHandle, SecurityAttributes, 0);
            if (!Success)
            {
                RaiseRdEvent(RdEventType.ErrorEvent, "CreatePipe failed.");
                return;
            }
            //創建進程
            StartupInfo.cb = Marshal.SizeOf(StartupInfo);
            StartupInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
            StartupInfo.wShowWindow = SW_HIDE;
            StartupInfo.hStdOutput = m_PipeWriteHandle;
            StartupInfo.hStdError = m_PipeWriteHandle;
            Success = CreateProcess(null, m_szCommand, null, null, true, 0, IntPtr.Zero,
                m_szCurrentDirectory, StartupInfo, m_processInfo);
            if (!Success)
            {
                RaiseRdEvent(RdEventType.ErrorEvent, "CreateProcess failed.");
                return;
            }
            //成功,啓動時鐘週期性的讀取管道
            m_running = true;
            m_readtimer.Start();
        }
        //觸發事件
        private void RaiseRdEvent(RdEventType eventtype,string info)
        {
            RdEventArgs args = new RdEventArgs();
            args.eventtype = eventtype;
            args.info = info;
            if (RdEventHandler != null)
                RdEventHandler(args);
        }
        /// <summary>
        /// 時鐘觸發
        /// </summary>
        /// <param name="state"></param>
        private void timertick(object sender, EventArgs e)
        {
            uint NumBytesRead = 0;
            uint TotalBytesAvailable = 0;
            uint BytesLeftThisMessage = 0;
            //查看數據
            bool Success = PeekNamedPipe(m_PipeReadHandle,m_PipeData,1,ref NumBytesRead,
                ref TotalBytesAvailable,ref BytesLeftThisMessage);
            if(!Success)
            {
                RaiseRdEvent(RdEventType.ErrorEvent, "PeekNamedPipe failed.");
                return;
            }
            //如果讀到數據
            if(NumBytesRead>0)
            {
                Success = ReadFile(m_PipeReadHandle,m_PipeData,BUF_SIZE,out NumBytesRead,IntPtr.Zero);
                if(!Success)
                {
                    RaiseRdEvent(RdEventType.ErrorEvent,"ReadFileError failed.");
                    return;
                }
                //處理得到的數據
                dealReadData(m_PipeData, NumBytesRead);
                return;
            }
            //如果沒有讀到數據,查看進程是否結束了
            if(WaitForSingleObject(m_processInfo.hProcess,0)==WAIT_OBJECT_0)//結束了
            {
                m_running = false;
                RaiseRdEvent(RdEventType.StopEvent, "terminated normal.");
                m_readtimer.Stop();
                FreeHandle();
            }
            //沒有結束,do nothing
        }
        /// <summary>
        /// 處理得到的數據
        /// </summary>
        /// <param name="data"></param>
        /// <param name="num"></param>
        private void dealReadData(byte[] data,uint num)
        {
            Array.Resize(ref data, (int)num);
            //轉換爲string byte[]中是dbcs,控制檯輸出的爲dbcs字符
            Encoding ec = Encoding.Default;//使用當前系統默認的ansi代碼頁
            string str = ec.GetString(data);
            //替換'\b' backspace 爲空格
            str.Replace('\b', ' ');
            //產生事件
            RaiseRdEvent(RdEventType.DataEvent, str);
        }
        /// <summary>
        /// 停止
        /// </summary>
        public void Stop()
        {
            if (!m_running) return;
            //置標誌
            m_running = false;
            m_readtimer.Stop();
            //結束進程
            TerminateProcess(m_processInfo.hProcess, 0);
            RaiseRdEvent(RdEventType.StopEvent, "terminated by user.");
            //釋放
            FreeHandle();
        }
        //釋放handle
        private void FreeHandle()
        {
            CloseHandle(m_processInfo.hThread);
            m_processInfo.hThread = IntPtr.Zero;
            CloseHandle(m_processInfo.hProcess);
            m_processInfo.hProcess = IntPtr.Zero;
            CloseHandle(m_PipeReadHandle);
            m_PipeReadHandle = IntPtr.Zero;
            CloseHandle(m_PipeWriteHandle);
            m_PipeWriteHandle = IntPtr.Zero;
        }
        /// <summary>
        /// 命令行
        /// </summary>
        private string m_szCommand;
        /// <summary>
        /// 當前目錄
        /// </summary>
        private string m_szCurrentDirectory;
        /// <summary>
        /// 是否工作中
        /// </summary>
        private bool m_running;
        /// <summary>
        /// 是否工作中
        /// </summary>
        public bool Running
        {
            get { return m_running; }
        }
        /// <summary>
        /// command屬性
        /// </summary>
        public string Command
        {
            set
            {
                if (m_running) return;
                m_szCommand = value;
            }
            get { return m_szCommand; }
        }
        /// <summary>
        /// 當前目錄
        /// </summary>
        public string CurrentDirectory
        {
            set
            {
                if (m_running) return;
                m_szCurrentDirectory = value;
            }
            get { return m_szCurrentDirectory; }
        }
        /// <summary>
        /// 讀取管道數據的毫秒數
        /// </summary>
        private uint m_dwMilliseconds;
        /// <summary>
        /// 讀取時鐘,使用windows時鐘,避免事件產生在線程中
        /// </summary>
        private System.Windows.Forms.Timer m_readtimer;
        /// <summary>
        /// 讀取管道數據的毫秒數
        /// </summary>
        public uint ReadMilliseconds
        {
            set
            {
                if (m_running) return;
                m_dwMilliseconds = value;
            }
            get { return m_dwMilliseconds; }
        }
        #region Dispose實現
        private bool disposed = false;
        public bool IsDisposed
        {
            get { return disposed; }
        }
        /// <summary>
        /// 重載dispose,釋放非託管資源
        /// </summary>
        /// <param name="disposing"></param>
        protected virtual void Dispose(bool disposing)
        {
            //do something
            if (!IsDisposed)
            {
                if (disposing){/*free managed resource*/}
                //free unmanaged resource
                Stop();
                m_readtimer.Dispose();
            }
            disposed = true;
        }
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        // Use C# destructor syntax for finalization code.
        // This destructor will run only if the Dispose method
        // does not get called.
        // It gives your base class the opportunity to finalize.
        // Do not provide destructors in types derived from this class.
        ~Redirect()     
        {
            // Do not re-create Dispose clean-up code here.
            // Calling Dispose(false) is optimal in terms of
            // readability and maintainability.
            Dispose(false);
        }
        #endregion
    }
}
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章