//實現思想是使用windows api CreatePipe 創建一個匿名管道
//接收控制檯命令的輸出,併產生委託事件。
//具體實現見以下代碼:
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Threading;
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 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>
public struct RdEventArgs
{
//code
public RdEventType eventtype;
//字符串(錯誤時代表錯誤信息,數據時代表數據字符串,stop時代表停止信息)
public string info;
};
/// <summary>
/// 錯誤處理事件類型
/// </summary>
/// <param name="code"></param>
public delegate void RdEvent(RdEventArgs args);
/// 錯誤處理事件類型
/// </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);
}
/// 構造函數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)
{
}
/// 構造函數2
/// </summary>
/// <param name="szCommand"></param>
public Redirect(string szCommand)
:this(szCommand,null)
{
}
/// <summary>
/// 構造函數3
/// </summary>
public Redirect()
:this(null,null)
{
}
/// 構造函數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;
}
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;
}
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;
}
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);
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);
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);
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);
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);
static extern Int32 WaitForSingleObject(IntPtr handle, Int32 milliseconds);
[DllImport("kernel32.dll", SetLastError = true)][return: MarshalAs(UnmanagedType.Bool)]
static extern bool CloseHandle(IntPtr hObject);
static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)][return: MarshalAs(UnmanagedType.Bool)]
static extern bool TerminateProcess(IntPtr hProcess, uint uExitCode);
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;
const int STARTF_USESTDHANDLES = 0x00000100;
const short SW_HIDE = 0;
const int WAIT_OBJECT_0 = 0;
#endregion
/// <summary>
/// 錯誤報告事件
/// </summary>
public event RdEvent RdEventHandler;
/// 錯誤報告事件
/// </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;
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;
/// 啓動
/// </summary>
public void Run()
{
//如果已經運行
if (m_running) return;
SECURITY_ATTRIBUTES SecurityAttributes=new SECURITY_ATTRIBUTES();
STARTUPINFO StartupInfo=new STARTUPINFO();
bool Success;
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;
}
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();
}
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);
}
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>
/// <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>
/// <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();
}
/// 停止
/// </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;
}
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>
private string m_szCommand;
/// <summary>
/// 當前目錄
/// </summary>
private string m_szCurrentDirectory;
/// 當前目錄
/// </summary>
private string m_szCurrentDirectory;
/// <summary>
/// 是否工作中
/// </summary>
private bool m_running;
/// 是否工作中
/// </summary>
private bool m_running;
/// <summary>
/// 是否工作中
/// </summary>
public bool Running
{
get { return m_running; }
}
/// 是否工作中
/// </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; }
}
/// 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>
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>
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; }
}
/// 讀取管道數據的毫秒數
/// </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; }
}
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;
}
/// 重載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);
}
{
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
}
}
// 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
}
}