8

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

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

这就是 Windows 7 中的全部
内容。Windows 服务在 LocalService 下运行(在 session#0 中)。
桌面应用程序正在当前登录的用户下运行(在会话#1 中)。

问题
Windows 服务看不到在桌面应用程序中创建和监听的 WCF 端点(使用 NetNamedPipeBinding)。这意味着通过 wcf 代理进行调用时,我会收到此异常:“在您的本地计算机上找不到管道端点 'net.pipe://localhost/HeartBeat'”

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

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

4

2 回答 2

7

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

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

于 2010-11-29T20:03:57.250 回答
6

最后我找到了一个解决方案——直接使用 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;
    }
}
于 2010-11-29T16:01:35.483 回答