【问题标题】:Using EventWaitHandles between Windows Service and Windows Application在 Windows 服务和 Windows 应用程序之间使用 EventWaitHandles
【发布时间】:2017-09-07 23:45:57
【问题描述】:

我需要使用命名事件将信号从 Windows 服务发送到 Windows 控制台/窗体应用程序。我创建了一个通用的 NamedEvents 类库,它具有创建事件的实现。

问题是我的 Windows 服务没有创建事件。如果我创建另一个 Windows 窗体应用程序(代替 Windows 服务)来创建事件,那么它可以正常工作。但是对于 Windows 服务,它似乎不起作用。如果我的代码中缺少某些内容,有人可以告诉我

public static class NamedEvents
{
    public static EventWaitHandle OpenOrCreate(string name, bool  initialState, EventResetMode mode)
    {
        EventWaitHandle ewh = null;
        try
        {                
            ewh = EventWaitHandle.OpenExisting(name);
        }
        catch (WaitHandleCannotBeOpenedException)
        {                
            ewh = new EventWaitHandle(initialState, mode, name);
        }

        return ewh;
    }

    public static EventWaitHandle OpenOrWait(string name)
    {
        EventWaitHandle ewh = null;

        while (null == ewh)
        {
            try
            {
                ewh = EventWaitHandle.OpenExisting(name);
            }
            catch (WaitHandleCannotBeOpenedException)
            {
                Thread.Sleep(50);
            }
        }

        return ewh;
    }
} 

Windows 服务代码:

public void SetSignalToClient()
{
    EventWaitHandle completedA = NamedEvents.OpenOrCreate("CompletedA", false, EventResetMode.ManualReset);                     
    completedA.Set();
    completedA.Close();
}

Windows 窗体应用程序:

public Form1()
{
    InitializeComponent();
    new Task((o) => SubscribeToAsyncEvents(),
            new System.Threading.CancellationToken()).Start(); 
}

private void SubscribeToAsyncEvents()
{            
    while (true)
    {
        EventWaitHandle completedA = NamedEvents.OpenOrWait("CompletedA");
        completedA.WaitOne();
        if (textBox1.InvokeRequired)
        {
            textBox1.Invoke((MethodInvoker)delegate { textBox1.Text = "received"; });
        }
        completedA.Close();               
    }
}

【问题讨论】:

    标签: c# windows forms


    【解决方案1】:

    这在MSDN article for CreateEvent 中有记录。为了在多个终端服务会话(也称为远程桌面会话)中使用事件对象,您必须创建一个全局事件对象,您可以通过以前缀 Global\ 开头的名称来执行此操作,例如 Global\MyEventName

    虽然文档没有提及,但据我所知,.NET EventWaitHandle 类会将事件名称原封不动地传递给 CreateEvent。所以同样的机制应该可以工作。

    Windows 服务在会话 0 中运行,用户应用程序在会话 1 或更高版本中运行。因此,要让服务和用户应用程序进行通信,它们必须使用全局对象。

    请注意,如果用户应用程序未以管理员身份运行,则服务可能还需要显式设置事件对象的权限。

    【讨论】:

    • 感谢您的回复,哈利。根据您的建议,我在创建事件以及打开事件(从 Windows 窗体)时在我的服务中使用了全局前缀,但它仍然没有工作。因此,按照您的另一个建议设置事件对象的权限,现在我的服务因异常而崩溃:System.Security.Principal.IdentityNotMappedException 为了设置权限,我使用了链接中的 sn-p [msdn.microsoft.com/en-us/library/z4c9z2kt(v=vs.110).aspx]
    • 将我的服务帐户类型从本地系统更改为“用户”后,它现在按预期工作。它在我的系统上运行良好,但我不属于将服务帐户类型更改为用户的情况,在客户位置运行此服务时会不会有任何其他未知的并发症?
    • 该代码 sn-p 做错了事,它为当前用户(即运行服务的用户)设置了明确的访问规则。您需要向将运行应用程序的特定用户授予访问权限,或者授予用户组(如用户)或通用安全身份(如 INTERACTIVE)的访问权限。此外,该代码拒绝访问信号或等待事件,您可能希望允许访问一个或两个。
    • 感谢 Harry,我尝试按照您的建议使用 InteractiveSID,但没有成功。我使用了 LocalSystemSid,它工作了一次,然后它根本没有调用 Set()。下面是我的代码 var users = new SecurityIdentifier(WellKnownSidType.LocalSystemSid, null); var rule = new EventWaitHandleAccessRule(users, EventWaitHandleRights.Synchronize | EventWaitHandleRights.Modify, AccessControlType.Allow);
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-09
    • 1970-01-01
    • 2011-07-04
    • 1970-01-01
    相关资源
    最近更新 更多