我一直在为社区编写一个程序,并遇到了一个我无法找到答案的问题。
我有一个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也没有产生错误,因为我没有得到我的消息窗口。
所以关于我如何能够阻止这些错误的任何想法,它们只会在关闭或注销时发生,但我仍然想修复它们。
答案 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中出错。