在 Windows 服务中从事件处理程序启动计时器

Posted

技术标签:

【中文标题】在 Windows 服务中从事件处理程序启动计时器【英文标题】:Start Timer from an Event Handler, in Windows Service 【发布时间】:2021-07-23 18:50:27 【问题描述】:

我有一个内置于 vb.net 的 Windows 服务,用于接收来自 MQTT 服务器的消息。通过附加到事件处理程序的 Sub 接收和处理消息。工作正常。

另外,我需要启动一个定时器,它会同时向“蜂鸣器”发送一个信号,蜂鸣器会发出哔哔声、停止、哔哔声……这个子例程本身就可以正常工作。

在 Windows 服务和 Windows 窗体中,它都不能完美运行。 Timer 必须由链接到 Event Handler 的 Sub 启用(一旦收到并处理 MQTT 消息,启动计时器)。定时器通常设置为 1000ms。子程序在 Timer 到达 1000ms 之前完成,并且永远不会触发 Tick 事件。

在 Windows 窗体中,我使用了 Me.Invoke。 Timer's Tick 事件按预期触发。但是 Windows 服务中不存在“me.invoke”。我不知道如何继续触发 Tick 事件。

在 Windows 窗体中,我能够创建一个委托并使用 Me.Invoke 调用 RefreshMqttScreen...

所以问题是:一旦 MqttMessageReceived 子程序完成运行,如何让计时器继续运行?

如果 VB 不能满足我的需求,我可以使用 C#...

谢谢

    AddHandler _mqttClient.ApplicationMessageReceived, AddressOf MqttMessageReceived



Public Sub MqttMessageReceived(ByVal sender As Object, ByVal e As MqttApplicationMessageReceivedEventArgs)
       RefreshMQTTScreen(some parameters here)
End Sub


Private Sub RefreshMQTTScreen(....)
    indexBuzzer = 1
    TimerBuzzer.Interval = buzzer.intDuree
    TimerBuzzer.Enabled = True
End Sub

Private Sub TimerBuzzer_Tick(sender As Object, e As EventArgs) Handles TimerBuzzer.Tick
    TimerBuzzer.Enabled = False
    indexBuzzer += 1

    If collBuzzerActif.Count >= indexBuzzer Then
        Dim buzzer As Derby.ClsBuzzer
        buzzer = collBuzzerActif(indexBuzzer)
        If buzzer.boolOn And buzzer.intDuree > 0 Then
            ChangerStatutDataLogger(True)
            If collBuzzerActif.Count >= indexBuzzer + 1 Then
                TimerBuzzer.Interval = buzzer.intDuree
                TimerBuzzer.Enabled = True
            End If
        Else
            ChangerStatutDataLogger(False)
            If collBuzzerActif.Count >= indexBuzzer + 1 Then
                TimerBuzzer.Interval = buzzer.intDuree
                TimerBuzzer.Enabled = True
            End If
        End If
    End If

End Sub

【问题讨论】:

服务默认使用系统管理员运行,没有环境变量,也没有凭据。最好的办法是使用用户帐户启动服务。打开 Windows 服务控制台(开始菜单 > 控制面板 > 管理工具 > 服务)。 ...单击登录选项卡。 ...单击此帐户单选按钮。 ...单击“浏览”按钮后,将出现“选择用户或服务帐户”对话框。 【参考方案1】:

您可以使用任务和一些手动重置事件来代替计时器。当您的服务启动时执行此 ONCE,

_TimerBuzzerTask = Task.Run(Sub() TimerBuzzer_Tick())

然后进行这些更改

Private Sub RefreshMQTTScreen(....)
    indexBuzzer = 1
    TimerBuzzerInterval = buzzer.intDuree
    _TimerBuzzer_Enabled.Set()
End Sub

Private _TimerBuzzerTask As Task
Private _TimerBuzzer_Enabled As New Threading.ManualResetEvent(False)
Private _TimerBuzzer As New Threading.ManualResetEvent(False) '_TimerBuzzer.Set to end task
Private TimerBuzzerInterval As Integer = 1000

Private Sub TimerBuzzer_Tick()
    Do
        _TimerBuzzer_Enabled.WaitOne() 'to enable  _TimerBuzzer_Enabled.Set()
        _TimerBuzzer_Enabled.Reset()
        If collBuzzerActif.Count >= indexBuzzer Then
            Dim buzzer As Derby.ClsBuzzer
            buzzer = collBuzzerActif(indexBuzzer)

            If buzzer.boolOn AndAlso buzzer.intDuree > 0 Then
                ChangerStatutDataLogger(True)
                If collBuzzerActif.Count >= indexBuzzer + 1 Then
                    TimerBuzzerInterval = buzzer.intDuree
                    _TimerBuzzer_Enabled.Set()
                End If
            Else
                ChangerStatutDataLogger(False)
                If collBuzzerActif.Count >= indexBuzzer + 1 Then
                    TimerBuzzerInterval = buzzer.intDuree
                    _TimerBuzzer_Enabled.Set()
                End If
            End If

        End If
    Loop While _TimerBuzzer.WaitOne(TimerBuzzerInterval) 'ticks every second
End Sub

如果您的服务停止执行此操作,

 _TimerBuzzer.Set

由于显而易见的原因,我无法对此进行测试。

【讨论】:

以上是关于在 Windows 服务中从事件处理程序启动计时器的主要内容,如果未能解决你的问题,请参考以下文章

c# 在windows服务中 使用定时器

Windows服务的安装及配合定时器编写简单的程序

在Twisted,Oscar中从外部事件处理程序发送ICQ消息

C# Windows 服务 - 我的计时器不工作,获得多个线程

在 Windows 服务上调用公共方法

Python Qt GUI设计:QTimer计时器类QThread多线程类和事件处理类(基础篇—8)