Reputation: 288
I have a Windows service and a client listening with the following funcionality:
Win service:
...
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;
}
Client:
...
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);
}
The problem appears writting to listbox1
. Form (and its listbox1
) only appears on user screen when the whole process has ended, and it shows the four message at once. I have Thread.Sleep(5000)
in service side in order to evidence that each message is written separately, but I'm not sure if process doesn't wait to Thread
and I'm testing it wrongly or Form is shown with all messages at once by some reason...
Upvotes: 0
Views: 1894
Reputation: 22038
Your problem is the while
loop that is blocking the current Thread
, this thread is also used to refresh the UI.
1) A poor solution is calling DoEvents()
within the while loop. But it would be wise to do more research to implement method 2
2) It's better to create a class that will create a Thread and triggers an event to when a message is received.
For example: (writting online so may contain some syntax/typos) So I will call it PSEUDO code ;-)
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();
}
}
Upvotes: 1