3

Is it possible to determine the client process ID connecting to a ServiceHost using NetNamedPipeBinding as the binding?

3
  • Couldn't you just send the data from the client over the pipe after the client has connected?
    – cjones26
    Commented Nov 6, 2015 at 20:00
  • I need the 'server' to determine the PID, lets assume we can't trust the client. I know I can get the PID from NamedPipeStreamServer using link. So I figured it might be possible with a WCF named pipe server.
    – Bryan
    Commented Nov 7, 2015 at 20:59
  • Understood, I'll see if I can dig anything up tomorrow.
    – cjones26
    Commented Nov 8, 2015 at 20:06

1 Answer 1

3

Bryan,

Just spent a few hours looking into this an I'm relatively positive it is not possible. Utilizing the GetNamedPipeClientProcessId referenced in your comment always seems to return the host PID no matter how close to the pipe you are.

To expand a little bit--when using the GetNamedPipeClientProcessId, we will first always need a handle to the named pipe. Since WCF doesn't make this particularly easy to find (see here), we need to massage the memory mapped file to determine the GUID of the named pipe. Divi's answer here: Programically get the system name of named pipe demonstrates how to get this value.

Once we have the GUID value, we are able to actually utilize the GetNamedPipeClientProcessId like so (excuse the crap code):

// Real name of the named pipe, thanks Divi!
string pipeAccess = String.Format(@"\\.\pipe\{0}", Program.pipeGuid);

// Get our handle to the named pipe
IntPtr pipe = CreateFile(pipeAccess, FileAccess.ReadWrite,
    0, IntPtr.Zero, FileMode.Open, FileAttributes.Normal, IntPtr.Zero);

uint pid = 0;
GetNamedPipeClientProcessId(pipe, out pid);
Console.WriteLine("Real client PID: {0}", pid);

My first thought was to run the above directly inside the OperationContract function--unfortunately this returned the host's PID, rather than the client's PID. Moving along, I attempted to find more information regarding GetNamedPipeClientProcessId but was only able to determine that it returns information stored in a kernel structure associated with the named pipe--I was unable to find more on how / where / why the value is set.

Since WCF marshals the bytes directly into a Message object, I figured we may need to get closer to the pipe before the proper client PID is returned. Implementing a very simple custom message encoder and attempting to pull the PID like so:

public override Message ReadMessage(ArraySegment<byte> buffer, BufferManager bufferManager, string contentType)
{
    string pipeAccess = String.Format(@"\\.\pipe\{0}", Program.pipeGuid);

    IntPtr pipe = CreateFile(pipeAccess, FileAccess.ReadWrite,
        0, IntPtr.Zero, FileMode.Open, FileAttributes.Normal, IntPtr.Zero);

    uint pid = 0;
    GetNamedPipeClientProcessId(pipe, out pid);
    Console.WriteLine("Real client PID: {0}", pid);

    return _encoder.ReadMessage(buffer, bufferManager, contentType);
}

Was also unsuccessful, once again returning the host's PID. Overall, I'm not sure there is a way to actually pull this information while utilizing WCF. If anyone else can find a way to do this I'd be interested to know as well. Maybe a custom binding based on NetNamedPipeBinding would work.

Original Answer - 11/6/2015

I'm not positive you would be able to get the PID directly from the pipe. I believe the easiest way would be something like below...

Server:

namespace WCFServer
{
    [ServiceContract]
    public interface IUtils
    {
        [OperationContract]
        void PostPID(int value);
    }

    public class Utils : IUtils
    {
        public void PostPID(int value)
        {
            // Do something with value here
            Console.WriteLine(value);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            using (ServiceHost host = new ServiceHost(
              typeof(Utils),
              new Uri[]{
          new Uri("net.pipe://localhost")
        }))
            {
                host.AddServiceEndpoint(typeof(IUtils),
                  new NetNamedPipeBinding(),
                  "Pipepipe");

                host.Open();

                Console.WriteLine("Service is available. " +
                  "Press <ENTER> to exit.");
                Console.ReadLine();

                host.Close();
            }
        }
    }
}

And the client:

namespace WCFClient
{
    [ServiceContract]
    public interface IUtils
    {
        [OperationContract]
        void PostPID(int value);
    }

    class Program
    {
        static void Main(string[] args)
        {
            ChannelFactory<IUtils> pipeFactory =
              new ChannelFactory<IUtils>(
                new NetNamedPipeBinding(),
                new EndpointAddress(
                  "net.pipe://localhost/Pipepipe"));

            IUtils pipeProxy =
              pipeFactory.CreateChannel();

            pipeProxy.PostPID(Process.GetCurrentProcess().Id);
        }
    }
}
2
  • In case you were curious, your attempt failed because you made a second connection to a second pipe instance of the same named pipe name, and queried for the information on this second connection. You would need to get the actual HANDLE for the server end of the pipe connection that WCF is using. Client PID is per-connection not per-namedpipe-name. The TCP analogy would be using a brand new socket for making a new connection to the same TCP port.
    – Ben Voigt
    Commented Sep 24, 2020 at 18:25
  • In other words, the comment "Get our handle to the named pipe" is sadly incorrect.
    – Ben Voigt
    Commented Sep 24, 2020 at 18:28

Not the answer you're looking for? Browse other questions tagged or ask your own question.