【问题标题】:Events not working in Windows Service事件在 Windows 服务中不起作用
【发布时间】:2013-05-08 10:09:34
【问题描述】:

我创建了一个管理应用程序。我的应用程序所做的事情之一是在插件上注册,该插件可以检测指纹读取器是否插入或拔出。该类订阅事件,如下所示:

//Subscribe to the plug, unplug and imageAcquired events from the GrFingerXCtrlClass library.
FingerXCtrlClass.SensorPlug += ReaderPlug;
FingerXCtrlClass.SensorUnplug += ReaderUnplug;
FingerXCtrlClass.ImageAcquired += ImageAcquired;

首先,当我积极参与该程序时,我开发了一个 WPF 应用程序。通过这个应用程序,我可以看到一些列表并切换一些设置,所以我确信我的服务运行良好。在这个 WPF 应用程序中,我通过实例化它来创建我的服务:

ProjectServiceLogic logic = new ProjectServiceLogic();

现在我已经创建了一个安装程序。因此,我有一个 ProjectService 类,用于初始化应用程序。这样做是这样的:

protected override void OnStart(string[] args)
{
    log.Debug("Starting service...");
    _worker = new Thread(new ThreadStart(StartService));
    _worker.IsBackground = true;
    _worker.Name = "ServiceThread";
    _worker.SetApartmentState(ApartmentState.STA);
    _worker.Start();
    log.Debug("Successfully started service");
}

void StartService()
{
    serviceLogic = new ProjectServiceLogic();
    while (!_shutdownEvent.WaitOne(0))
    {

    }
}

程序安装完毕,服务启动。在调试服务时,我注意到订阅代码被执行。但是,当我插入设备时不会触发事件,而在通过 WPF 应用程序在本地运行设备时会触发事件,实例化服务逻辑。为什么现在不行了?

【问题讨论】:

  • 几乎可以肯定,在幕后某处依赖于窗口消息 - 并且服务不倾向于运行窗口消息泵来接收此类消息。
  • @Damien_The_Unbeliever - 是的,'ApartmentState.STA'。
  • @MartinJames - 作为 STA 线程运行表示您将 发送消息 - 据我了解,并不是说它们会以某种方式自动发送。
  • @Damien_The_Unbeliever - 是的,我试图强化你的评论 - 也许我做得不太好:)

标签: c# multithreading service


【解决方案1】:
_worker.SetApartmentState(ApartmentState.STA);

选择单线程单元需要您实现 STA 线程的协定。只有两个基本要求:您永远不能阻塞线程并且您必须泵送消息循环。消息循环是必不可少的,它允许 COM 保证对 COM 对象的方法调用始终由创建对象的线程进行,从而确保线程安全。还有 .NET 中使 Control.BeginInvoke 和 Dispatcher.BeginInvoke 工作的机制。

COM 组件依赖于具有该保证,它通常依赖消息调度程序来处理其自己的线程间封送处理。就像 Dispatcher.BeginInvoke 一样。

当您实际上没有按要求发送消息循环时,会出现两件事。首先,正如预期的那样,您从工作线程对对象进行的任何调用都将死锁。 COM 将使用 PostMessage 要求 STA 线程调度调用。但是当线程没有从消息队列中检索消息时,就不会发生这种情况。出错的第二件事可能是您在此处看到的情况,组件本身使用 PostMessage 在 STA 线程上引发事件。使用永远不会引发事件的故障模式。同样经典的 WebBrowser 行为不端的方式是,您永远不会收到 DocumentCompleted 事件。

您需要泵送一个消息循环,Application.Run()。无论是 Winforms 还是它的 WPF 版本都可以,请自行选择。一个 Winforms 示例is here

【讨论】:

  • 听起来很合理。我想你是对的。我的目标是创建一个 Windows 服务应用程序,所以 Application.Run() 不适用于我这里。 WPF 应用程序仅用于帮助我对该服务应用程序进行编程。现在不涉及 GUI。我怎样才能做到这一点?
  • Application.Run() 并不关心它是在服务中运行还是您实际上没有 GUI。它发送 Windows 消息,仅此而已。你真的必须用它来取得成功。
  • 啊,我明白了,我错过了一个参考。有没有办法检查是否有(正确的)消息循环泵送?我已经按照另一个问题中的建议调整了我的代码,但无济于事。即使应用程序订阅,事件仍然不会被触发。我开始认为我搞砸了我的安装项目......
  • 我不得不说我发现整个方法很神秘。服务无法与用户交互,没有什么比扫描指纹更具交互性了。您将如何告诉用户扫描无法识别?如何区分猫爪和拇指?这应该是您在登录时启动的程序。 NotifyIcon 是将其保留在后台但仍允许交互的好方法。
  • 与阅读器交互的用户将无法理解界面。想想他们与 3 岁孩子相比的精神状态。他们必须知道的一件事是,当他们将手指按在扫描仪上时,门会打开。尽管如此,我确实有一个 GUI,一个 WPF 应用程序,它可以(远程)连接到这个服务并配置一两个东西。如果出现断电或任何情况,计算机重新启动后,它应该立即再次工作。这就是我选择服务而不是登录后启动应用程序的原因。
猜你喜欢
  • 1970-01-01
  • 2020-03-04
  • 2018-08-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多