WCF NetNamedPipeBinding确定客户端的进程ID

时间:2015-11-06 19:26:44

标签: c# wcf

是否可以使用NetNamedPipeBinding作为绑定来确定连接到ServiceHost的客户端进程ID?

1 个答案:

答案 0 :(得分:1)

布赖恩,

花了几个小时研究这个我是相对积极的,这是不可能的。利用评论中引用的GetNamedPipeClientProcessId似乎总是会返回主机PID,无论您与管道有多接近。

要扩展一点 - 使用GetNamedPipeClientProcessId时,我们首先需要一个指定管道的句柄。由于WCF没有使这个特别容易找到(参见here),我们需要按摩内存映射文件以确定命名管道的GUID。 Divi的答案在这里:Programically get the system name of named pipe演示了如何获得这个值。

一旦我们获得GUID值,我们就可以实际使用GetNamedPipeClientProcessId之类的(请原谅垃圾代码):

// 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);

我的第一个想法是直接在OperationContract函数内部运行 - 不幸的是,它返回了主机的PID,而不是客户端的PID。继续前进,我试图找到有关GetNamedPipeClientProcessId的更多信息,但只能确定它返回存储在与命名管道相关联的内核结构中的信息 - 我无法找到更多关于如何/在何处/为何值已设定。

由于WCF将字节直接编组到Message对象中,我想我们可能需要在返回正确的客户端PID之前更接近管道。实现一个非常简单的自定义消息编码器,并试图像这样拉出PID:

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);
}

也不成功,再次返回主机的PID。总的来说,我不确定在使用WCF时是否有办法真正提取这些信息。如果其他人能找到办法做到这一点,我也有兴趣知道。也许基于NetNamedPipeBinding的自定义绑定可以工作。

原始答案 - 2015年11月6日

我不肯定你能直接从管道中获取PID。我相信最简单的方法就是下面......

服务器:

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();
            }
        }
    }
}

客户:

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);
        }
    }
}