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; } } } }
测试界面:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· 字符编码:从基础到乱码解决
· Open-Sora 2.0 重磅开源!
2007-12-25 无法删除从msx发起的作业(转)