Question

I am trying to make a Windows Service that simply sits and listens for messages on a named pipe. But it's not working. Trying to connect to the named pipe simply times out, and when I view the process handles of the service's process in Process Explorer, I do not see the named pipe.

Here is what I have so far:

namespace Service
{
    class Service : ServiceBase
    {
        Thread workerThread;
        static bool serviceStarted = false;
        static PipeSecurity pipeSecurity = new PipeSecurity();        

        public Service()
        {
            this.ServiceName = "Service";
            this.EventLog.Log = "Application";
            this.EventLog.Source = "ServiceName";
            this.CanHandlePowerEvent = false;
            this.CanHandleSessionChangeEvent = false;
            this.CanPauseAndContinue = false;
            this.CanShutdown = true;
            this.CanStop = true;
        }

        static void Main()
        {
            ServiceBase.Run(new Service());
        }

        protected override void OnStart(string[] args)
        {                
            base.OnStart(args);

            pipeSecurity.AddAccessRule(new PipeAccessRule("Authenticated Users", PipeAccessRights.ReadWrite, AccessControlType.Allow));
            pipeSecurity.AddAccessRule(new PipeAccessRule(WindowsIdentity.GetCurrent().User, PipeAccessRights.FullControl, AccessControlType.Allow));

            ThreadStart threadStart = new ThreadStart(WorkerThread);
            workerThread = new Thread(threadStart);
            workerThread.Start();
            serviceStarted = true;    
        }

        protected override void OnStop()
        {                
            base.OnStop();
            serviceStarted = false;
            workerThread.Join(new TimeSpan(0, 0, 30)); 
        }

        protected override void OnShutdown()
        {
            base.OnShutdown();
            serviceStarted = false;
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
        }

        private void WorkerThread()
        {
            while (serviceStarted)
            {
                try
                {
                    using (NamedPipeServerStream pipeServerStream = new NamedPipeServerStream("PipeName", PipeDirection.InOut, 1, PipeTransmissionMode.Message, PipeOptions.None, 128, 128, pipeSecurity))
                    {
                        pipeServerStream.WaitForConnection();
                        byte[] buffer = new byte[128];
                        StringBuilder message = new StringBuilder();
                        do
                        {
                            int len = pipeServerStream.Read(buffer, 0, buffer.Length);
                            message.Append(Encoding.UTF8.GetString(buffer, 0, len));
                        }
                        while (!pipeServerStream.IsMessageComplete);
                        EventLog.WriteEntry(message.ToString());
                    }
                }
                catch(Exception ex)
                {

                }
                finally
                {
                    if (serviceStarted)
                        Thread.Sleep(100);
                }
            }
            Thread.CurrentThread.Abort();
        }
    }
}

It seems like the named pipe is just not getting created. Any ideas? When I attempt to connect via Powershell:

PS C:\> $Client = New-Object System.IO.Pipes.NamedPipeClientStream(".", "PipeName", [System.IO.Pipes.PipeDirection]::Out)
PS C:\> $Client.Connect(1000)

Exception calling "Connect" with "1" argument(s): "The operation has timed out."
At line:1 char:1
+ $Client.Connect(1000)
Was it helpful?

Solution

   new NamedPipeServerStream("PipeName", ...)

That pipe is only accessible to processes that run in the same session. Which is session 0 for services. Your Powershell program runs on the desktop session. To make it visible to all sessions, you must prefix the name with "Global\\"

Btw, don't hesitate to add some logging so you have positive proof that the pipe server is about to call WaitForConnection(). That removes considerable doubt on your (and our) end that the pipe is actually listening. Using empty catch blocks creates too much FUD.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top