本封装的组件,可以方便连接本机或远程 主机的命名管道。
连接远程主机时,需要提供帐号和密码。
同时解决了委托事件中修改界面元素时引起的安全错误。
代码为【调试】版,有不少调试信息传递到调用者。
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; } }
}
}
测试界面: