使用命名管道,如何将客户端收到的消息一一写入?
Using named pipes, how to write messages client receives in its form one by one?
我有一个 Windows 服务和一个使用以下功能监听的客户端:
赢得服务:
- 创建命名管道
- 创建客户端进程
- 等待连接
- 写入客户端进程
- 从客户端读取
- 写入客户端进程
- 从客户端读取
...
public static bool StartProcessAsCurrentUser()
{
var hUserToken = IntPtr.Zero;
var sInfo = new STARTUPINFO();
var procInfo = new PROCESS_INFORMATION();
var pEnv = IntPtr.Zero;
int iResultOfCreateProcessAsUser;
string cmdLine = "ClientNamedPipeForm.exe";
sInfo.cb = Marshal.SizeOf(typeof(STARTUPINFO));
byte[] buffer = new byte[BUFSIZE];
try
{
var tSecurity = new SECURITY_ATTRIBUTES();
tSecurity.nLength = Marshal.SizeOf(tSecurity);
var pSecurity = new SECURITY_ATTRIBUTES();
pSecurity.nLength = Marshal.SizeOf(pSecurity);
pSecurity.bInheritHandle = true; //For controling handles from child process
IntPtr pointer = Marshal.AllocHGlobal(Marshal.SizeOf(pSecurity));
Marshal.StructureToPtr(pSecurity, pointer, true);
PipeSecurity ps = new PipeSecurity();
System.Security.Principal.SecurityIdentifier sid = new System.Security.Principal.SecurityIdentifier(System.Security.Principal.WellKnownSidType.WorldSid, null);
PipeAccessRule par = new PipeAccessRule(sid, PipeAccessRights.ReadWrite, System.Security.AccessControl.AccessControlType.Allow);
ps.AddAccessRule(par);
NamedPipeServerStream pipeServer = new NamedPipeServerStream("testpipe", PipeDirection.Out, 1, PipeTransmissionMode.Byte, PipeOptions.WriteThrough, 10, 10, ps);
StreamWriter sw = new StreamWriter(pipeServer);
if (!CreateProcessAsUser(hUserToken,
null, // Application Name
cmdLine, // Command Line
IntPtr.Zero,
IntPtr.Zero,
true,
dwCreationFlags,
pEnv,
null, // Working directory
ref sInfo,
out procInfo))
{
throw new Exception("StartProcessAsCurrentUser: CreateProcessAsUser failed.\n");
}
try
{
pipeServer.WaitForConnection();
sw.WriteLine("Waiting");
sw.Flush();
pipeServer.WaitForPipeDrain();
Thread.Sleep(5000);
sw.WriteLine("Waiting2");
sw.Flush();
pipeServer.WaitForPipeDrain();
Thread.Sleep(5000);
sw.WriteLine("Waiting32");
sw.Flush();
pipeServer.WaitForPipeDrain();
Thread.Sleep(5000);
sw.WriteLine("QUIT");
sw.Flush();
pipeServer.WaitForPipeDrain();
}
catch (Exception ex) { throw ex; }
finally
{
if (pipeServer.IsConnected) { pipeServer.Disconnect(); }
}
}
finally
{
//Closing things
}
return true;
}
客户:
- 创建命名管道
- 连接
- 从服务读取
- 以其形式书写
- 写入服务
- 从服务读取
- 以其形式书写
- 写入服务
...
private void Client()
{
try
{
IntPtr hPipe;
string dwWritten;
byte[] buffer = new byte[BUFSIZE];
NamedPipeClientStream pipeClient = new NamedPipeClientStream(".","testpipe", PipeDirection.In, PipeOptions.WriteThrough);
if (pipeClient.IsConnected != true) { pipeClient.Connect(); }
StreamReader sr = new StreamReader(pipeClient);
string temp;
bool cont = true;
while (cont)
{
temp = "";
temp = sr.ReadLine();
if (temp != null)
{
listBox1.Items.Add(temp);
listBox1.Refresh();
}
if (temp != "QUIT")
{
sw.WriteLine("Response");
sw.Flush();
pipeClient.WaitForPipeDrain();
}
else
{
sw.WriteLine("Response");
cont = false;
}
}
}
catch (Exception ex)
{
throw new Exception("Exception: " + ex.Message);
}
写入 listbox1
时出现问题。表单(及其 listbox1
)仅在整个过程结束后才出现在用户屏幕上,并且一次显示四个消息。我在服务端有 Thread.Sleep(5000)
以证明每条消息都是单独编写的,但我不确定进程是否没有等待 Thread
并且我错误地测试了它或显示了表格出于某种原因一次处理所有消息...
您的问题是 while
循环阻塞了当前 Thread
,该线程还用于刷新 UI。
1) 一个糟糕的解决方案是在 while 循环中调用 DoEvents()
。但是做更多的研究来实现方法 2
是明智的
2) 最好创建一个 class,它将创建一个线程并在收到消息时触发一个事件。
例如:(网上写的,所以可能会包含一些syntax/typos)所以我将其称为伪代码;-)
public class MessageEventArgs : EventArgs
{
public string Message { get; private set;}
public MessageEventArgs(string message)
{
Message = message;
}
}
public class MyReceiver : IDisposable
{
private Thread _thread;
private ManualResetEvent _terminating = new ManualResetEvent(false);
public void Start()
{
_thread = new Thread(() =>
{
try
{
IntPtr hPipe;
string dwWritten;
byte[] buffer = new byte[BUFSIZE];
NamedPipeClientStream pipeClient = new NamedPipeClientStream(".","testpipe", PipeDirection.In, PipeOptions.WriteThrough);
if (pipeClient.IsConnected != true) { pipeClient.Connect(); }
StreamReader sr = new StreamReader(pipeClient);
string temp;
while(!_terminating.WaitOne(0))
{
temp = "";
temp = sr.ReadLine();
if (temp != null)
{
OnMessage?.Invoke(temp);
}
if (temp != "QUIT")
{
sw.WriteLine("Response");
sw.Flush();
pipeClient.WaitForPipeDrain();
}
else
{
sw.WriteLine("Response");
_terminating.Set();
}
}
}
catch (Exception ex)
{
throw new Exception("Exception: " + ex.Message);
}
});
_thread.Start();
}
public void Dispose()
{
_terminating.Set();
_thread.Join();
}
public event EventHandler<MessageEventArgs> OnMessage;
}
// Example of how to use the Receiver class.
public class Form1: Form
{
MyReceiver _receiver;
public Form1()
{
InitializeComponent();
this.FormClosed += FormClosed;
_receiver = new MyReceiver();
_receiver.OnMessage += MessageReceived;
_receiver.Start();
}
public void MessageReceived(object sender, MessageEventArgs e)
{
// You need to invoke this, because the event is run on other than the UI thread.
this.Invoke(new Action(() =>
{
listBox1.Items.Add(e.Message);
});
}
public void FormClosed(object sender, EventArgs e)
{
_receiver.Dispose();
}
}
我有一个 Windows 服务和一个使用以下功能监听的客户端:
赢得服务:
- 创建命名管道
- 创建客户端进程
- 等待连接
- 写入客户端进程
- 从客户端读取
- 写入客户端进程
- 从客户端读取
...
public static bool StartProcessAsCurrentUser() { var hUserToken = IntPtr.Zero; var sInfo = new STARTUPINFO(); var procInfo = new PROCESS_INFORMATION(); var pEnv = IntPtr.Zero; int iResultOfCreateProcessAsUser; string cmdLine = "ClientNamedPipeForm.exe"; sInfo.cb = Marshal.SizeOf(typeof(STARTUPINFO)); byte[] buffer = new byte[BUFSIZE]; try { var tSecurity = new SECURITY_ATTRIBUTES(); tSecurity.nLength = Marshal.SizeOf(tSecurity); var pSecurity = new SECURITY_ATTRIBUTES(); pSecurity.nLength = Marshal.SizeOf(pSecurity); pSecurity.bInheritHandle = true; //For controling handles from child process IntPtr pointer = Marshal.AllocHGlobal(Marshal.SizeOf(pSecurity)); Marshal.StructureToPtr(pSecurity, pointer, true); PipeSecurity ps = new PipeSecurity(); System.Security.Principal.SecurityIdentifier sid = new System.Security.Principal.SecurityIdentifier(System.Security.Principal.WellKnownSidType.WorldSid, null); PipeAccessRule par = new PipeAccessRule(sid, PipeAccessRights.ReadWrite, System.Security.AccessControl.AccessControlType.Allow); ps.AddAccessRule(par); NamedPipeServerStream pipeServer = new NamedPipeServerStream("testpipe", PipeDirection.Out, 1, PipeTransmissionMode.Byte, PipeOptions.WriteThrough, 10, 10, ps); StreamWriter sw = new StreamWriter(pipeServer); if (!CreateProcessAsUser(hUserToken, null, // Application Name cmdLine, // Command Line IntPtr.Zero, IntPtr.Zero, true, dwCreationFlags, pEnv, null, // Working directory ref sInfo, out procInfo)) { throw new Exception("StartProcessAsCurrentUser: CreateProcessAsUser failed.\n"); } try { pipeServer.WaitForConnection(); sw.WriteLine("Waiting"); sw.Flush(); pipeServer.WaitForPipeDrain(); Thread.Sleep(5000); sw.WriteLine("Waiting2"); sw.Flush(); pipeServer.WaitForPipeDrain(); Thread.Sleep(5000); sw.WriteLine("Waiting32"); sw.Flush(); pipeServer.WaitForPipeDrain(); Thread.Sleep(5000); sw.WriteLine("QUIT"); sw.Flush(); pipeServer.WaitForPipeDrain(); } catch (Exception ex) { throw ex; } finally { if (pipeServer.IsConnected) { pipeServer.Disconnect(); } } } finally { //Closing things } return true; }
客户:
- 创建命名管道
- 连接
- 从服务读取
- 以其形式书写
- 写入服务
- 从服务读取
- 以其形式书写
- 写入服务
...
private void Client() { try { IntPtr hPipe; string dwWritten; byte[] buffer = new byte[BUFSIZE]; NamedPipeClientStream pipeClient = new NamedPipeClientStream(".","testpipe", PipeDirection.In, PipeOptions.WriteThrough); if (pipeClient.IsConnected != true) { pipeClient.Connect(); } StreamReader sr = new StreamReader(pipeClient); string temp; bool cont = true; while (cont) { temp = ""; temp = sr.ReadLine(); if (temp != null) { listBox1.Items.Add(temp); listBox1.Refresh(); } if (temp != "QUIT") { sw.WriteLine("Response"); sw.Flush(); pipeClient.WaitForPipeDrain(); } else { sw.WriteLine("Response"); cont = false; } } } catch (Exception ex) { throw new Exception("Exception: " + ex.Message); }
写入 listbox1
时出现问题。表单(及其 listbox1
)仅在整个过程结束后才出现在用户屏幕上,并且一次显示四个消息。我在服务端有 Thread.Sleep(5000)
以证明每条消息都是单独编写的,但我不确定进程是否没有等待 Thread
并且我错误地测试了它或显示了表格出于某种原因一次处理所有消息...
您的问题是 while
循环阻塞了当前 Thread
,该线程还用于刷新 UI。
1) 一个糟糕的解决方案是在 while 循环中调用 DoEvents()
。但是做更多的研究来实现方法 2
2) 最好创建一个 class,它将创建一个线程并在收到消息时触发一个事件。
例如:(网上写的,所以可能会包含一些syntax/typos)所以我将其称为伪代码;-)
public class MessageEventArgs : EventArgs
{
public string Message { get; private set;}
public MessageEventArgs(string message)
{
Message = message;
}
}
public class MyReceiver : IDisposable
{
private Thread _thread;
private ManualResetEvent _terminating = new ManualResetEvent(false);
public void Start()
{
_thread = new Thread(() =>
{
try
{
IntPtr hPipe;
string dwWritten;
byte[] buffer = new byte[BUFSIZE];
NamedPipeClientStream pipeClient = new NamedPipeClientStream(".","testpipe", PipeDirection.In, PipeOptions.WriteThrough);
if (pipeClient.IsConnected != true) { pipeClient.Connect(); }
StreamReader sr = new StreamReader(pipeClient);
string temp;
while(!_terminating.WaitOne(0))
{
temp = "";
temp = sr.ReadLine();
if (temp != null)
{
OnMessage?.Invoke(temp);
}
if (temp != "QUIT")
{
sw.WriteLine("Response");
sw.Flush();
pipeClient.WaitForPipeDrain();
}
else
{
sw.WriteLine("Response");
_terminating.Set();
}
}
}
catch (Exception ex)
{
throw new Exception("Exception: " + ex.Message);
}
});
_thread.Start();
}
public void Dispose()
{
_terminating.Set();
_thread.Join();
}
public event EventHandler<MessageEventArgs> OnMessage;
}
// Example of how to use the Receiver class.
public class Form1: Form
{
MyReceiver _receiver;
public Form1()
{
InitializeComponent();
this.FormClosed += FormClosed;
_receiver = new MyReceiver();
_receiver.OnMessage += MessageReceived;
_receiver.Start();
}
public void MessageReceived(object sender, MessageEventArgs e)
{
// You need to invoke this, because the event is run on other than the UI thread.
this.Invoke(new Action(() =>
{
listBox1.Items.Add(e.Message);
});
}
public void FormClosed(object sender, EventArgs e)
{
_receiver.Dispose();
}
}