如何在VB.Net中检测Windows Closing in Timer事件代码

时间:2013-11-03 23:02:59

标签: vb.net windows timer

我一直在为社区编写一个程序,并遇到了一个我无法找到答案的问题。

我有一个VB.NET应用程序,它有一个每秒触发的计时器事件来检查一个任务是否存在,如果它已启用,这会运行一些代码,这些代码根据Windows的版本运行schtask.exe,或者jt.exe,都与创建和检查计划任务有关。

现在当用户注销或在我的程序运行时关闭Windows时,我们经常会收到错误“SCHTasks.exe - 应用程序错误。应用程序无法正常启动(0xc0000142)。单击确定关闭应用程序“,我也得到同样的JT.exe错误,我认为这是因为在关机/注销期间定时器事件已经触发,并且Windows不会让相应的程序启动。

我尝试使用以下方法在关闭代码的表单中禁用计时器,但这不起作用。

If e.CloseReason = CloseReason.WindowsShutDown Then Timer2ChkLoggingTask.enabled = False

我在internet上尝试的最新尝试,但这似乎也不起作用,添加处理程序在我的表单加载代码中。 ShuttingDwn是一个公共共享变量。

    AddHandler Microsoft.Win32.SystemEvents.SessionEnding, AddressOf SessionEnding

    Private Sub SessionEnding(ByVal sender As System.Object, ByVal e As Microsoft.Win32.SessionEndingEventArgs)

    ShuttingDwn = True

    End Sub

这是运行schtasks.exe的代码,有一些代码使用了这个函数,但这是实际运行schtasks.exe的唯一一点,我认为检查ShuttingDwn的值会停止了问题,除非是因为当我的代码达到这一点时它实际上还没有被更新。

    Public Function SCHTasksRun(ByVal Arguments As String) As String                'Creates a task using SCHTasks


    If Form1.ShuttingDwn = True Then Return "Void" 'This in theory should stop an error when shutting down.

    ' This is the code for the base process 

    Dim myProcess As New Process()
    ' Start a new instance of this program
    Dim myProcessStartInfo As New ProcessStartInfo("schTasks")
    myProcessStartInfo.Arguments = Arguments
    myProcessStartInfo.UseShellExecute = False
    myProcessStartInfo.CreateNoWindow = True
    myProcessStartInfo.RedirectStandardOutput = True
    myProcessStartInfo.RedirectStandardError = True

    myProcess.StartInfo = myProcessStartInfo

    Try
        myProcess.Start()
    Catch ex As Exception
        MsgBox("There was an error: " & ex.Message)
    End Try
    Dim myStreamReader As StreamReader = myProcess.StandardOutput
    Dim myErrorStreamReader As StreamReader = myProcess.StandardError
    ' Read the standard output of the spawned process. 
    Dim myString As String = myStreamReader.ReadToEnd
    myString = myString & myErrorStreamReader.ReadToEnd
    myProcess.WaitForExit()
    myProcess.Close()


    Return myString

End Function 

所以它永远不会运行两个程序中的任何一个(schtasks.exe或Jt.exe),或者至少是理论,问题是我偶尔会得到错误。

Try / catch也没有产生错误,因为我没有得到我的消息窗口。

所以关于我如何能够阻止这些错误的任何想法,它们只会在关闭或注销时发生,但我仍然想修复它们。

2 个答案:

答案 0 :(得分:1)

您可以handle Windows发送给您的应用程序的WM_QUERYENDSESSION消息,或者(如果您问我更好)是使用新的Windows 7关闭阻止API,例如ShutdownBlockReasonCreate

答案 1 :(得分:0)

在Dan-o的帮助下,我指出了正确的方向,我现在解决了这个问题。我不知道这段代码是否完全正确,但它到目前为止似乎运作良好。

原始计时器是使用Windows.Forms.Timer触发的,它运行在同一个线程中,所以我的任务检查代码阻止了窗口关闭的检测,我现在正在使用System.Timers.Timer,它不会阻止代码。我还修改了用于检查任务的代码,并将其隔离,以便它不能由我的任何其他例程运行。

我在公共类Form1

之后添加了这个
Public Const WM_QUERYENDSESSION As Integer = &H11       'From http://vbcity.com/forums/t/45823.aspx
Public Shared ShuttingDwn As Boolean = False    'If Windows is shutting down or logging off this gets set to true.

Public Structure TaskExistEnabled               'Structure variable used in function for checking if task exists and is enabled.
    Public TaskExists As Boolean
    Public TaskEnabled As Boolean
End Structure

然后将此子句添加到我的主窗体form1。

Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
    'Listens for system closing down - This sets a flag when windows Windows shuts down or logs off
    'From http://vbcity.com/forums/t/45823.aspx
    If m.Msg = WM_QUERYENDSESSION Then
        ShuttingDwn = True
    End If
    MyBase.WndProc(m)
End Sub 'WndProc 

在Sub Form1_Load代码中创建了我的System.Timers.Timer。

    AddHandler Timer2ChkLoggingTask.Elapsed, New System.Timers.ElapsedEventHandler(AddressOf Timer2ChkLoggingTask_Tick) 'Timer for the task checking
    Timer2ChkLoggingTask.Interval = 1000

然后我在主Form1上创建了计时器代码

  Private Sub Timer2ChkLoggingTask_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs)

    'This is only used by the timer for checking the tasks - it runs in a different thread, and so needs to be entirely self contained.
    'It was made to run in parallel so the main code was not blocked, so that system shutting would be picked up.

    Timer2ChkLoggingTask.Stop()         'Stopping the timer stops the code being called more than once if it take longer than the interval to complete.

    Dim TaskExists_oldVal As Boolean = Taskinfo.TaskExists         'Store the old value so we can see if its changed at the end
    Dim TaskEnabled_OldVal As Boolean = Taskinfo.TaskEnabled       'Store the old value so we can see if its changed at the end
    Dim Mystring As String

    If TaskVersion = "V0" Or TaskVersion = "V1" Then          'We have an OS that stores the tasks in Windows/Task

        Taskinfo.TaskExists = (IO.File.Exists(Environment.GetEnvironmentVariable("windir") & "\Tasks\" & LoggingTaskName & ".job"))
        If Taskinfo.TaskExists = True Then
            If ShuttingDwn = False Then  'This in theory should stop an error when shutting down.

                ' This is the code for the base process 
                Dim myProcess As New Process()
                ' Start a new instance of this program

                Dim myProcessStartInfo As New ProcessStartInfo(JT)

                myProcessStartInfo.Arguments = " /LJ " & QUOTE & Environment.GetEnvironmentVariable("windir") & "\Tasks\" & LoggingTaskName & ".job" & QUOTE & " /PJ"
                myProcessStartInfo.UseShellExecute = False
                myProcessStartInfo.CreateNoWindow = True
                myProcessStartInfo.RedirectStandardOutput = True
                myProcess.StartInfo = myProcessStartInfo

                Try
                    myProcess.Start()
                Catch ex As Exception
                    MsgBox(ex.Message)
                End Try
                Dim myStreamReader As StreamReader = myProcess.StandardOutput
                ' Read the standard output of the spawned process. 
                Mystring = myStreamReader.ReadToEnd
                myProcess.WaitForExit()
                myProcess.Close()
                Taskinfo.TaskEnabled = Not Mystring.Contains("Suspend                 = 1")
            End If

        End If

    Else                                                                    'We have Vista upwards and will use schtasks

        If ShuttingDwn = False Then 'This in theory should stop an error when shutting down.

            ' This is the code for the base process 

            Dim myProcess As New Process()
            ' Start a new instance of this program
            Dim myProcessStartInfo As New ProcessStartInfo("schTasks")
            myProcessStartInfo.Arguments = " /Query /TN " & QUOTE & LoggingTaskName & QUOTE
            myProcessStartInfo.UseShellExecute = False
            myProcessStartInfo.CreateNoWindow = True
            myProcessStartInfo.RedirectStandardOutput = True
            myProcessStartInfo.RedirectStandardError = True

            myProcess.StartInfo = myProcessStartInfo

            Try
                myProcess.Start()
            Catch ex As Exception
                MsgBox("There was an error: " & ex.Message)
            End Try
            Dim myStreamReader As StreamReader = myProcess.StandardOutput
            Dim myErrorStreamReader As StreamReader = myProcess.StandardError
            ' Read the standard output of the spawned process. 
            Mystring = myStreamReader.ReadToEnd
            Mystring = Mystring & myErrorStreamReader.ReadToEnd
            myProcess.WaitForExit()
            myProcess.Close()
            Taskinfo.TaskExists = Not Mystring.Contains("ERROR: The system cannot find the file specified.")
            Taskinfo.TaskEnabled = Not Mystring.Contains("Disabled")
        End If

    End If

    If TaskEnabled_OldVal <> Taskinfo.TaskEnabled Or TaskExists_oldVal <> Taskinfo.TaskExists Then  'If either of the values have changed we need to update the buttons.
        Me.BeginInvoke(New TimerStart(AddressOf TimerStartFunction)) 'Whilst a background worker thread can stop a timer it can't start - see here http://stackoverflow.com/questions/18545787/timer-can-be-stopped-in-backgroundworker-but-cant-be-started
    End If
    Timer2ChkLoggingTask.Start()


End Sub

显示按钮/标签的实际更新现在由一个单独的计时器处理,该计时器只查看Taskinfo的值,仅当任何taskinfo值已更改时才启用计时器,然后按钮更新代码将禁用计时器,因此它只在任一个taskinfo变量发生变化时运行一次。

更新。虽然这似乎在W7&amp; W8关闭/注销时,我仍然在Windows XP中出错。