I have a VB function which starts a program as a process and waits for it to complete. It passes a return code of zero if okay or 8 if not. The problem is that it blocks the WPF UI thread and can lead to crashes of Not Enough Quota because the thread has been blocked for too long.
So I’m trying to make it run async so the UI thread isn’t blocked. Unfortunately I have many lines of VB code but all the examples on various web sites these days are C# which I don’t program in and I have far too much code to try and learn C# to convert it all.
I’ve tried Await Task.Run which doesn’t accept parameters. I temporarily removed the Pgm parameter and hardcoded the program name and it will then compile and work. I realise I could use global variables instead but that doesn’t seem good practice.
TaskFactory seems to allow parameters but when I await on StartNew control returns immediately because StartNew creates an outer task and an inner task and the Await only waits for the initial outer task. A C# solution I’ve found suggests using Await Await Task but I can’t seem to convert this to a syntax that VB will accept.
Any help would be appreciated on how I can Await for Startit to complete. I'm using .Net 6 and VS 2022 under Windows 10.
Please excuse any formatting errors. This is my first day on Stack Overflow
The code
Class MainWindow
Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
Call StartitAsync()
MsgBox("Returned from StartitAsync")
End Sub
Private Async Function StartitAsync() As Task(Of Integer)
Dim Startup As Func(Of String, Integer) = AddressOf Startit
Dim tf As New TaskFactory
Dim Rc As Integer = Await tf.StartNew(Startup, "notepad.exe")
MsgBox("Returned from await of Startit, RC is " & Rc)
Return Rc
End Function
Private Function Startit(Pgm As String) As Integer
Dim RC As Integer
Dim Startinfo As New ProcessStartInfo
MsgBox("Pgm is " & Pgm)
Startinfo.WindowStyle = ProcessWindowStyle.Maximized ' Display in a maximised window
Startinfo.FileName = Pgm
Startinfo.Arguments = ""
Using PgmProcess As Process = Process.Start(startInfo:=Startinfo) ' Start the program
PgmProcess.WaitForExit() ' Wait until it ends
If PgmProcess.HasExited = True Then ' If the process has exited
RC = PgmProcess.ExitCode ' Save the exit code
Else
RC = 8
End If
End Using
Return RC
End Function
CodePudding user response:
You should use Task.Run
instead of Task.Factory.StartNew
(or (new TaskFactory()).StartNew
). This is true for C# as well as VB.NET.
My VB is extremely rusty, but I believe this is what you're looking for:
Private Async Function StartitAsync() As Task(Of Integer)
Dim Startup = Function() Startit("notepad.exe")
Dim Rc As Integer = Await Task.Run(Startup)
MsgBox("Returned from await of Startit, RC is " & Rc)
Return Rc
End Function
This uses lambda expressions, which are very useful when using APIs like Task.Run
.
Side note: You shouldn't call MsgBox
from Startit
. Since Task.Run
executes Startit
on the thread pool, it shouldn't access any UI elements (or do things like show message boxes).