Search code examples
c#winformsservicenamed-pipes

Named Pipes Between Winforms and Service Can Only Connect Once


I am making a simple app to try out named pipes for an app I am making. I can connect to the pipe initially and write to the file specified with the service that is listening for inputs. If I restart my winforms app and try to connect to the pipe it just hangs on the .Connect() method. If I restart my service, then I am able to connect again with my winforms app... but only once(until I restart the service again).

Here is my NamedPipeServerStream on my Windows Service.

protected override void OnStart(string[] args)
{
    StartPipe();
}

protected override void OnStop()
{
}

public void StartPipe()
{
    FileStream fs = new FileStream(@"C:\Users\Nate\Desktop\PipeService.txt", FileMode.OpenOrCreate, FileAccess.Write);
    Task.Run(() =>
    {
        var server = new NamedPipeServerStream("PipeService3");
        server.WaitForConnection();
        StreamReader reader = new StreamReader(server);
        while (!reader.EndOfStream)
        {
            var line = reader.ReadLine();
            StreamWriter writer = new StreamWriter(fs);
            writer.WriteLine(line);
            writer.Flush();
        }
    });
}

This is my NamePipeClientStream section from my Winforms App

public StreamWriter writer;
public NamedPipeClientStream client;

public Form1()
{
    InitializeComponent();

    client = new NamedPipeClientStream("PipeService3");
    client.Connect();

    writer = new StreamWriter(client);
}

private void sendButton_Click(object sender, EventArgs e)
{
    string line = textBox1.Text;
    writer.WriteLine(line);
    writer.Flush();
}

this was my workaround but I feel like there is a legit way of doing this rather than stopping and starting the service every time someone opens the winforms app.

try
{
    client.Connect(1000);
}
catch
{
    var serviceController = ServiceController.GetServices();
    var pipeService = serviceController.Where(s => s.DisplayName == "PipeService").FirstOrDefault();
    pipeService.Stop();
    pipeService.WaitForStatus(ServiceControllerStatus.Stopped);
    pipeService.Start();
    pipeService.WaitForStatus(ServiceControllerStatus.Running);
    client.Connect();
}

Solution

  • I was able to solve the issue by setting the pipe to be recursive, so anytime the client disocnnects (in my case it will only ever be one client) the named pipe is disposed of and a new pipe with the same pipe is named. (also included is allowing my winforms app as a non administator to still connect to the pipe which was started with the service with elevated permissions)

      public void StartPipe()
        {
            System.Threading.Tasks.Task.Run(() =>
            {
                RecursivePipe();
            });
        }
        public void RecursivePipe()
        {
            PipeSecurity pipeSecurity = new PipeSecurity();
            pipeSecurity.AddAccessRule(new PipeAccessRule(new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null), PipeAccessRights.ReadWrite | PipeAccessRights.CreateNewInstance, AccessControlType.Allow));
            pipeSecurity.AddAccessRule(new PipeAccessRule(new SecurityIdentifier(WellKnownSidType.LocalSystemSid, null), PipeAccessRights.FullControl, AccessControlType.Allow));
    
            var server = new NamedPipeServerStream("PipeService7", PipeDirection.InOut, 10, PipeTransmissionMode.Message, PipeOptions.WriteThrough, 1024, 1024, pipeSecurity);
    
            server.WaitForConnection();
            StreamReader reader = new StreamReader(server);
            while (!reader.EndOfStream)
            {
                var line = reader.ReadLine();
                var lineArray = line.Split('$');
                if (lineArray.Length == 4)
                {
                    SetkeyValue(lineArray);
                }
                else if (lineArray.Length == 5)
                {
                    setStartupStrat(lineArray);
                }
                else
                {
                    DeleteKeyValue(lineArray);
                }
    
    
            }
            server.Dispose();
            server.Close();
            RecursivePipe();
        }