通过命名管道从Windows服务(会话#0)连接到桌面应用程序(会话#1)

时间:2010-11-29 11:07:21

标签: wcf security winapi named-pipes netnamedpipebinding

鉴于
  - 应用程序 - 桌面GUI(WPF).NET应用程序
  - 监视应用程序的Windows服务(.NET也)

Windows服务定期“ping”应用程序以确保它是健康的(如果它不是winservice将重新启动它)。
我打算通过命名管道实现“pinging”。为了简单起见,我决定使用WCF。该应用程序托管一个WCF服务(一个操作Ping返回一些东西)。 Windows服务是此WCF服务的客户端,基于计时器定期调用它。

这一切都在Windows 7中 Windows服务在LocalService下运行(在会话#0中) 桌面应用程序在当前登录的用户下运行(在会话#1中)。

问题
Windows服务无法在桌面应用程序中看到创建并正在侦听的WCF端点(使用NetNamedPipeBinding)。这意味着通过wcf代理调用时,我得到了这个异常:“在本地计算机上找不到管道端点'net.pipe:// localhost / HeartBeat'”

我确信代码没问题,因为另一个桌面应用程序(在会话#1中)可以看到端点。

显然,我在处理Win32系统对象隔离的一些安全性问题。 但我相信应该有办法解决我遇到过的限制 我可以牺牲WCF方法并采用原始的NamedPipe方式。

2 个答案:

答案 0 :(得分:6)

更简单的解决方案可能是与托管WCF服务的Windows服务使用WCF双工合同。客户端App会在启动时调用服务上的操作来注册自己。然后,Ping将是服务在客户端的回调合约上定期调用的操作,应用程序将响应该操作。

服务可见性以这种方式工作,因为Windows服务可以使用SeCreateGlobalPrivilege运行,因此服务发布管道名称的共享内存对象可以在全局内核名称空间中创建,对其他会话可见。交互式应用程序无法在Windows7中轻松获得该特权,因此此类应用程序中的WCF服务可以回退到在本地内核命名空间中发布管道,仅在其自己的会话中可见。

答案 1 :(得分:4)

最后,我找到了一个解决方案 - 直接使用System.IO.Pipes中的命名管道。 WCF的管道支持实现似乎不使用System.IO.Pipes。

服务器

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

<强>客户端

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