1. 程式人生 > >C# 將NamedPipeClientStream封裝為事件驅動的元件

C# 將NamedPipeClientStream封裝為事件驅動的元件

 本封裝的元件,可以方便連線本機或遠端 主機的命名管道。

連線遠端主機時,需要提供帳號和密碼。

同時解決了委託事件中修改介面元素時引起的安全錯誤。

程式碼為【除錯】版,有不少除錯資訊傳遞到呼叫者。

using System;
using System.ComponentModel;
using System.IO.Pipes;
using System.Security.Principal;
using System.Threading;
using System.Drawing;

namespace LeesNamedPipeClient
{
    [DefaultProperty(
"PipeName")] [DefaultEvent("OnPipeReadData")] [ToolboxBitmap("fifo.ico")] public partial class LeesNamedPipeClient : Component { NamedPipeClientStream _PipeClient = null; public LeesNamedPipeClient() { InitializeComponent(); }
public LeesNamedPipeClient(IContainer container) { container.Add(this); InitializeComponent(); } #region 屬性 [Browsable(true), Category("連線設定"), Description("設定連線的主機"), DefaultValue(".")] public string Host { get; set
; } private bool _isLocalHost; [Browsable(true), Category("連線設定"), Description("是否本機"), DefaultValue("true")] public bool IsLocalHost { set { _isLocalHost = value; if (value) { Host = "."; } } get { return _isLocalHost; } } [Browsable(true), Category("連線設定"), Description("連線到遠端主機的帳號,本機連線忽略此項。"), DefaultValue("")] public string UserName { get; set; } [Browsable(true), Category("連線設定"), Description("連線到遠端主機的密碼,本機連線忽略此項。"), DefaultValue("")] public string PassWord { get; set; } [Browsable(true), Category("連線設定"), Description("管道名字"), DefaultValue("PipeName")] public string PipeName { get; set; } [Browsable(true), Category("連線設定"), Description("超時時間(毫秒)"), DefaultValue(1000)] public uint TimeOut { get; set; } [Browsable(false)] /// <summary> /// 獲取一個值,該值指示當前流是否支援讀操作。 /// 如果流支援讀操作,則為 true;否則為 false。 /// </summary> public bool CanRead { get { return _PipeClient.CanRead; } } [Browsable(false)] /// <summary> /// 獲取一個值,該值指示當前管道是否支援寫操作。 /// 如果管道支援寫操作,則為 true;否則為 false。 /// </summary> public bool CanWrite { get { return _PipeClient.CanWrite; } } [Browsable(false)] /// <summary> /// 獲取一個值,該值指示當前管道是否支援查詢操作。 /// 在所有情況下均為 false。 /// </summary> public bool CanSeek { get { return _PipeClient.CanSeek; } } [Browsable(false)] /// <summary> /// 獲取一個值,該值指示當前管道是否已經連線 /// </summary> public bool IsConnected { get { if (_PipeClient == null) return false; return _PipeClient.IsConnected; } } #endregion #region 事件 /// <summary> /// 從管道中讀取資料完成 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> public delegate void PipeReadDataHandle(object sender, PipeReadDataEventArgs e); /// <summary> /// 管道寫完成/可以進行下一次寫 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> public delegate void PipeWriteOverHandle(object sender, EventArgs e); public delegate void PipeConnecteHandle(object sender, EventArgs e); public delegate void PipeColsedHandle(object sender, EventArgs e); public delegate void PipeDisconnectHandle(object sender, EventArgs e); public delegate void PipeErrorHandle(object sender, PipeErrorEventArgs e); [Description("從管道中讀取資料完成")] /// <summary> /// 從管道中讀取資料完成 /// </summary> public event PipeReadDataHandle OnPipeReadData=null; [Description("管道寫完成/可以進行下一次寫")] /// <summary> /// 管道寫完成/可以進行下一次寫 /// </summary> public event PipeWriteOverHandle OnPipeWriteOver = null; [Description("管道已經連線成功")] /// <summary> /// 管道已經連線成功 /// </summary> public event PipeConnecteHandle OnPipeConnect = null; [Description("管道已經連線成功")] /// <summary> /// 主動關閉管道連線 /// </summary> public event PipeColsedHandle OnPipeClosed = null; [Description("管道被動關閉【管道伺服器主動關閉連線】")] /// <summary> /// 管道被動關閉【管道伺服器主動關閉連線】 /// </summary> public event PipeDisconnectHandle OnPipeDisconnect = null; [Description("有錯誤發生")] public event PipeErrorHandle OnPipeError = null; #endregion #region 方法 #region 私有方法 private IntPtr token = IntPtr.Zero; private WindowsIdentity newIdentity; private WindowsImpersonationContext impersonatedUser; private bool _isLogonSuccessed = false; private bool LogonToRemote() { _isLogonSuccessed = false; try { _isLogonSuccessed = Win32Helper.LogonUser(UserName, Host, PassWord, Win32Helper.LOGON32_LOGON_NEW_CREDENTIALS, Win32Helper.LOGON32_PROVIDER_DEFAULT, ref token); newIdentity = new WindowsIdentity(token); impersonatedUser = newIdentity.Impersonate(); #if DEBUG string sMsg= string.Format("UserName:{0} Password:{1} Host:{2} newIdentity={3},impersonatedUser={4}", UserName, PassWord, Host, newIdentity == null ? "NULL" : "Not Null", impersonatedUser == null ? "NULL" : "Not NULL"); MyInvoke(OnPipeError, this, new PipeErrorEventArgs(PipeErrorReson.REMOTELOGON, sMsg)); #endif } catch (Exception e) { //OnPipeError?.Invoke(this, new PipeErrorEventArgs(PipeErrorReson.REMOTELOGON, e.Message)); MyInvoke(OnPipeError, this, new PipeErrorEventArgs(PipeErrorReson.REMOTELOGON, e.Message)); return false; } return _isLogonSuccessed; } private void LogonOut() { if (_isLogonSuccessed) { if (impersonatedUser != null) impersonatedUser.Undo(); if (token != IntPtr.Zero) Win32Helper.CloseHandle(token); } } private void PipeWriteCallback(IAsyncResult ar) { var pipe = (NamedPipeClientStream)ar.AsyncState; pipe.EndWrite(ar); pipe.Flush(); pipe.WaitForPipeDrain(); MyInvoke(OnPipeWriteOver, this, new EventArgs()); //OnPipeWriteOver?.Invoke(this, new EventArgs()); } private void ReleasePipe() { if(_PipeClient!=null) { if(_PipeClient.IsConnected) { _PipeClient.Close(); _PipeClient = null; MyInvoke(OnPipeClosed, this, new EventArgs()); } else { _PipeClient = null; MyInvoke(OnPipeDisconnect, this, new EventArgs()); } } LogonOut(); } private class AsyncReadState { public NamedPipeClientStream Pipe { get; set; } public byte[] Buffer { get; set; } public ManualResetEvent EventHandle { get; set; } } private void ReadThread() { while (true) { if (_PipeClient != null) { try { if (_PipeClient.IsConnected) { AsyncReadState asyncState = new AsyncReadState() { Pipe = _PipeClient, Buffer = new byte[4096], EventHandle = new ManualResetEvent(false) }; MyInvoke(OnPipeError, this, new PipeErrorEventArgs(PipeErrorReson.READ, "beginread")); _PipeClient.BeginRead(asyncState.Buffer, 0, 4096, ReadCallBack, asyncState); asyncState.EventHandle.WaitOne(); } else { break; } } catch(ArgumentException e) { //OnPipeError?.Invoke(this, new PipeErrorEventArgs(PipeErrorReson.READ, e.Message)); MyInvoke(OnPipeError, this, new PipeErrorEventArgs(PipeErrorReson.READ, e.Message)); break; } catch (ObjectDisposedException e)//管道已關閉。 { //OnPipeDisconnect?.Invoke(this, new PipeErrorEventArgs(PipeErrorReson.READ, e.Message)); //OnPipeError?.Invoke(this, new PipeErrorEventArgs(PipeErrorReson.READ, e.Message)); MyInvoke(OnPipeError, this, new PipeErrorEventArgs(PipeErrorReson.READ, e.Message)); break; } catch(InvalidOperationException e)//管道已斷開連線,正在等待連線,或尚未設定控制代碼。 { //OnPipeDisconnect?.Invoke(this, new PipeErrorEventArgs(PipeErrorReson.READ, e.Message)); MyInvoke(OnPipeDisconnect, this, new PipeErrorEventArgs(PipeErrorReson.READ, e.Message)); break; } catch (Exception e) { //OnPipeError?.Invoke(this, new PipeErrorEventArgs(PipeErrorReson.READ, ex.Message)); MyInvoke(OnPipeError, this, new PipeErrorEventArgs(PipeErrorReson.READ, e.Message)); break; } } else break; } MyInvoke(OnPipeError, this, new PipeErrorEventArgs(PipeErrorReson.READ, "PipeReadThreadOver")); ReleasePipe(); } private void ReadCallBack(IAsyncResult ar) { var asyncState = (AsyncReadState)ar.AsyncState; int nReadLength = asyncState.Pipe.EndRead(ar); if (nReadLength == 0) { MyInvoke(OnPipeDisconnect, this, new EventArgs()); ReleasePipe(); } else { MyInvoke(OnPipeReadData, this, new PipeReadDataEventArgs(asyncState.Buffer, nReadLength)); } asyncState.EventHandle.Set(); } #endregion /// <summary> /// 根據設定,連線到管道伺服器 /// </summary> public void Connect() { //bool bRet = false; if(!IsLocalHost) { if(string.IsNullOrEmpty(UserName) || string.IsNullOrEmpty(Host) || string.IsNullOrEmpty(PassWord)) { throw new Exception("遠端連線必須設定主機地址、登入帳號、登入密碼!"); } if(!LogonToRemote()) { return; } } else { if (string.IsNullOrEmpty(Host)) { Host = "."; } } try { if (string.IsNullOrEmpty(PipeName)) { throw new Exception("必須設定管道名稱!"); } _PipeClient = new NamedPipeClientStream(Host, PipeName, PipeDirection.InOut, PipeOptions.Asynchronous, System.Security.Principal.TokenImpersonationLevel.Impersonation); _PipeClient.Connect((int)TimeOut); Thread t = new Thread(new ThreadStart(ReadThread)); t.Name = "PipeRreadThread"; t.IsBackground = true; t.Start(); try { if (_PipeClient.CanTimeout) { _PipeClient.WriteTimeout = (int)TimeOut; _PipeClient.ReadTimeout = (int)TimeOut; } } catch(InvalidOperationException) { } //OnPipeConnect?.Invoke(this, new EventArgs()); MyInvoke(OnPipeConnect, this, new EventArgs()); } catch(TimeoutException ex) { throw ex; } catch(Exception e) { //OnPipeError?.Invoke(this, new PipeErrorEventArgs(PipeErrorReson.CONNECT, e.Message)); MyInvoke(OnPipeError, this, new PipeErrorEventArgs(PipeErrorReson.CONNECT, e.Message)); return; } //return bRet; } public void Close() { ReleasePipe(); } /// <summary> /// 將位元組陣列寫入管道 /// </summary> /// <param name="data">要寫入的位元組陣列</param> public void Write(byte[] data) { Write(data, 0, data.Length); } /// <summary> /// 將位元組陣列寫入管道 /// </summary> /// <param name="data">要寫入的位元組陣列</param> /// <param name="offset">陣列起始位置</param> /// <param name="count">寫入的位元組數</param> public void Write(byte[] data,int offset,int count) { if(_PipeClient==null) { throw new Exception("請在寫資料之前連線管道"); } if(!_PipeClient.IsConnected) { throw new Exception("請在寫資料之前連線管道"); } if(!_PipeClient.CanWrite) { throw new Exception("該管道不支援寫操作"); } if(count>4096) { throw new Exception("要求寫入資料總長度不大於4096"); } if(offset<0||offset>data.Length-1||count<0) { throw new Exception("引數錯誤"); } if (offset +count > data.Length) { throw new Exception("引數錯誤"); } if (count == 0) { //OnPipeWriteOver?.Invoke(this, new EventArgs()); MyInvoke(OnPipeWriteOver, this, new EventArgs()); return; } _PipeClient.BeginWrite(data, offset, count, PipeWriteCallback, _PipeClient); } #endregion protected virtual void MyInvoke(Delegate del, object sender, object args) { if (del != null) { if (del.Target is System.ComponentModel.ISynchronizeInvoke) { //當前委託的例項。如一個Form例項 System.ComponentModel.ISynchronizeInvoke aSynch = del.Target as System.ComponentModel.ISynchronizeInvoke; if (aSynch.InvokeRequired) { //此類物件繫結到特定執行緒並不是執行緒安全。 如果正在從另一個執行緒呼叫的方法,則必須使用Invoke封送到正確的執行緒呼叫的方法。 //如在非UI執行緒中修改UI控制元件 object[] objs = new object[2] { sender, args }; aSynch.BeginInvoke(del, objs); } else { //不涉及執行緒安全問題時,直接呼叫 del.DynamicInvoke(sender, args); } } } } } public enum PipeErrorReson { /// <summary> /// 登入遠端主機 /// </summary> REMOTELOGON, /// <summary> /// 連線到管道伺服器 /// </summary> CONNECT, /// <summary> /// 寫資料 /// </summary> WRITE, /// <summary> /// 讀資料 /// </summary> READ } public class PipeReadDataEventArgs: EventArgs { byte[] _data; int _nDataLength=0; public PipeReadDataEventArgs(byte[] buf,int nLen) { _data = new byte[nLen]; Array.Copy(buf, _data, nLen); _nDataLength = nLen; } /// <summary> /// 返回從管道中讀取的位元組陣列 /// </summary> public byte[] Data { get { return _data; } } /// <summary> /// 返回管道中讀取的位元組陣列長度 /// </summary> public int DataLength { get { return _nDataLength; } } } public class PipeErrorEventArgs:EventArgs { PipeErrorReson _pipeErrorReson ; string _msg; public PipeErrorEventArgs(PipeErrorReson pipeErrorReson,string msg) { _pipeErrorReson = pipeErrorReson; _msg = msg; } /// <summary> /// 返回錯誤引起的原因【由哪個動作導致的錯誤】 /// </summary> public PipeErrorReson ErrorReson { get { return _pipeErrorReson; } } /// <summary> /// 返回錯誤資訊描述 /// </summary> public string Message { get { return _msg; } } } }

 測試介面: