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

Posted

技术标签:

【中文标题】通过命名管道从 Windows 服务(会话#0)连接到桌面应用程序(会话#1)【英文标题】:Connecting via named pipe from windows service (session#0) to desktop app (session #1) 【发布时间】:2011-05-17 05:34:53 【问题描述】:

鉴于: - 应用程序 - 桌面 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 方法。

【问题讨论】:

您确定 Windows 服务在桌面应用程序运行后尝试打开通道吗?当windows服务“看不到”WCF端点时,异常的细节是什么? >#1 : 是的,我敢肯定,因为 winservice 会定期执行此操作(通过 wcf 调用)(无限循环)。 >#2:winservice“看不到”意味着在调用 wcf 代理时出现以下异常:“管道端点 'net.pipe://localhost/HeartBeat' 不能可以在您的本地计算机上找到” 【参考方案1】:

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

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

【讨论】:

【参考方案2】:

最后我找到了一个解决方案——直接使用 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;
    

【讨论】:

以上是关于通过命名管道从 Windows 服务(会话#0)连接到桌面应用程序(会话#1)的主要内容,如果未能解决你的问题,请参考以下文章

从 Windows 上的 C# Service Fabric 应用程序连接到 docker_engine(命名管道)

获取使用 C# 连接到命名管道服务器的客户端的进程 ID

终端服务器会话中的隔离命名管道

通过重叠 IO 的异步命名 Windows 管道通信

从 VB6 到 WCF 的命名管道

使用命名管道的 SQL Server 连接