Connecting via named pipe from windows service (se

2019-01-09 00:27发布

问题:

Given:
- the application - desktop GUI (WPF) .NET app
- windows service watching for application (.NET also)

The windows service periodically "pings" application to get sure it's healthy (and if it's not winservice will restart it).
I was going to implement "pinging" via named pipes. To make things simpler I decided to do it with WCF. The application hosts a WCF-service (one operation Ping returning something). The windows service is a client for this WCF-service, invokes it periodically based on a timer.

That's all in Windows 7.
Windows service is running under LocalService (in session#0).
Desktop application is running under currently logged in user (in session#1).

The problem:
Windows service can't see WCF endpoint (with NetNamedPipeBinding) created in and being listened in desktop application. That means that on call via wcf proxy I get this exception: "The pipe endpoint 'net.pipe://localhost/HeartBeat' could not be found on your local machine"

I'm sure code is ok, because another desktop application (in session#1) can see the endpoint.

Obviously here I'm dealing with some security stuff for Win32 system object isolation. But I believe there should be a way to workaround restrictions I've encountered with.
I can sacrifice WCF approach and go the raw NamedPipe way.

回答1:

An easier solution might be to use a WCF duplex contract with the Windows service hosting the WCF service. The client App would call an operation on the service to register itself, when it starts up. The Ping would then be an operation invoked periodically by the service on the client's callback contract, to which the App would respond.

Service visibility works this way round, because the Windows service can run with SeCreateGlobalPrivilege, and so the shared memory object via which the pipe name is published by the service can be created in the Global kernel namespace, visible to other sessions. Interactive applications can't easily get that privilege in Windows7, so WCF services in such applications fall back to publishing the pipe in the Local kernel namespace, visible only within their own session.



回答2:

Finally I've found a solution - using Named Pipes from System.IO.Pipes directly. It's seems that WCF's pipes support implementation doesn't use System.IO.Pipes.

Server:

using (var pipeServer = new NamedPipeServerStream("mypipe", PipeDirection.Out, 1))
{
    try
    {
        while (true)
        {
            // #1 Connect:
            try
            {
                pipeServer.WaitForConnection();
            }
            catch (ObjectDisposedException)
            {
                yield break;
            }
            if (ae.IsCanceled())
                return;

            // #2: Sending response:
            var response = Encoding.ASCII.GetBytes(DateTime.Now.ToString());
            try
            {
                pipeServer.Write(response, 0, response.Length);
            }
            catch (ObjectDisposedException)
            {
                return;
            }

            // #3: Disconnect:
            pipeServer.Disconnect();
        }
    }
    finally
    {
        if (pipeServer.IsConnected)
            pipeServer.Disconnect();
    }
}

Client:

using (var pipeClient = new NamedPipeClientStream(".", "mypipe", PipeDirection.In))
{
    try
    {
        try
        {
            pipeClient.Connect(TIMEOUT);
        }
        catch(TimeoutException ex)
        {
            // nobody answers to us
            continue;
        }
        using (var sr = new StreamReader(pipeClient))
        {
            string temp;
            while ((temp = sr.ReadLine()) != null)
            {
                // got response
            }
        }
    }
    catch(Exception ex)
    {
        // pipe error
        throw;
    }
}