I am writing a simple program to test out various functions of VB.NET, I will later use this in a much more complex program. The idea is to send a message via the serial port com5. The message will be taken from what is entered in the application's textbox. A background worker (activated when a button labeled "send" is pressed) continuously transmits the message. There is also a label, which does nothing.
Imports System.ComponentModel
Public Class Form1
Dim message
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
message = TextBox1.Text
If BackgroundWorker1.IsBusy = False Then
BackgroundWorker1.RunWorkerAsync()
End If
End Sub
Sub SendSerialData(ByVal data As String)
' Send strings to a serial port.
Try
Using com5 As IO.Ports.SerialPort =
My.Computer.Ports.OpenSerialPort("COM5")
com5.WriteLine(data)
End Using
Catch ioex As System.IO.IOException
TextBox1.Text = ("COM5 Not Found!")
End Try
End Sub
Private Sub BackgroundWorker1_DoWork(sender As Object, e As DoWorkEventArgs) Handles BackgroundWorker1.DoWork
While (True)
SendSerialData(message)
End While
End Sub
End Class
Originally, I used Application.DoEvents(), but it was slow and I heard it was a bad programming practice. (This, however, had no errors.) So I rewrote the program using a background worker. Now, the program runs fine while running, but when closed, this pops up:
System.ObjectDisposedException was unhandled Message: An unhandled exception of type 'System.ObjectDisposedException' occurred in
mscorlib.dll Additional information: Safe handle has been closed
I believe this has something to do with the background worker, because if the program is closed and I have not ran the background worker, the error does not appear. I have tried placing a try-catch inside every sub in the program (including changing the exception type for the catch in SendSerialData to be any exception), to no avail. This is my first post in case of issues feel free to comment.
What you need to do is signal to the BackgroundWorker1_DoWork thread that the program is closing so that it can tidy up properly. This is something that the BackgroundWorker class in the .NET framework already knows how to do.
You need to do the following:
Use a BackgroundWorker to run the task
Configure the BackgroundWorker to accept a cancellation request
Change the While loop in the background task to check for cancellation
On exiting the program request cancellation of the background task and then wait for the thread to complete.
NB: The MSDN page on BackgroundWorker has an example that does almost exactly what you want.
Related
I am using a Windows form application due to added security measures i have to work around for session in my application.Currently i am using a Timer to achieve the functionality I am able to close the form but i need to again restart the application to return to the login form.I am using the below code
Private Sub sessionTimer_Tick(sender As Object, e As EventArgs) Handles sessionTimer.Tick
Try
Me.sessionTimer.Stop()
Me.sessionTimer.Enabled = False
Process.Start(Application.StartupPath + "\application.exe")
Process.GetCurrentProcess().Kill()
Catch ex As Exception
End Try
End Sub
I am getting an exception when i use the above method and it doesn't serve the purpose,also i have already tried using Application.Restart didn't work out.Please help i am new to windows form. Also adding to this in order to reset the timer i am using the below code.
Private Sub frmMain_MouseMove(sender As Object, e As MouseEventArgs) Handles MyBase.MouseMove
Me.sessionTimer.Stop()
Me.sessionTimer.Start()
End Sub
But this doesn't seem to work the main form has menu's which i am using to navigate to other forms so the idle time should not include the time spent in other forms which are opened via the menu's. What event should i use in frmMain to handle this problem.Thanks
Just let the Framework do the work.
Application.Restart()
If your session Timer fires from a background thread (maybe you use System.Timers.Timer instead of System.Windows.Forms.Timer) you propable have to sync with your main thread.
Me.Invoke(new MethodInvoker(Addressof Application.Restart))
If Application.Restart does not work there is propably something wrong with your app. You should try the following.
If you created threads, be sure they are created with "thread.IsBackground = true" otherwise they will keep your process open
Stop your timers and background workers.
Be sure you don't have forms that handle the FormClosing event and set e.Cancel = true. In that case you have to take e.CloseReason into account.
User Mark Hurd posted a great post about what happens during Application.Restart, have a look here
Iam using this code to restart my application. It works very well.
System.Diagnostics.Process.Start(Application.ExecutablePath) 'First start a new instance
Me.Close() 'Close the current application
If this doesnt work. I think there is no way around than using another application which restarts the Process. Here is a code example (second application)
Private Shared Sub RestartApp(pid As Integer, applicationName As String, arguments As String)
' Wait for the process to terminate
Dim process__1 As Process = Nothing
Try
process__1 = Process.GetProcessById(pid)
process__1.WaitForExit(1000)
' ArgumentException to indicate that the
' process doesn't exist?
Catch ex As ArgumentException
End Try
Process.Start(applicationName, arguments)
'Arguments?
End Sub
Source of the code
Researching I found out that Background Worker is a background thread, however when I run the following code Background Worker still runs until the end even when the main procedure is exited. Isn't this feature reserved to foreground threads?
Code:
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'Run background worker
BackgroundWorker1.RunWorkerAsync()
'Display exit message
MsgBox("Main procedure exited")
End Sub
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
'Wait 10 seconds
Threading.Thread.Sleep(10000)
'Modify some numbers
Dim variable = 3
variable -= 1
'Display exit message
MsgBox("Background thread " & variable & " exited")
End Sub
End Class
The Form1_Load method is not the "main procedure", so the message box that you're displaying at the end of it is actually a lie. That's just an event handler method for the Form.Load event, raised when your form is being displayed for the first time.
The "main procedure" is named Main and is defined in a separate file (actually, in VB.NET, it's automatically generated by the compiler and not even visible to you by default). You can find more information on the Main method in Microsoft's VB Programming Guide.
The Main method is still running as long as your program is still running. After the Form1_Load event handler method finishes, Form1 is still on the screen, so clearly your program hasn't closed yet. And since your program's main thread is still running, the BackgroundWorker object's background thread is still running, asynchronously, just like you told it to.
I'm developing an application with an error log when something goes bad. It must send an e-mail with the error details so I can remotely fix and upload a new update with the fix.
I'm using Try Catch Exception but I have a lot of methods to include this option in.
Is there another way to do it without doing so much code?
Since exceptions bubble up to the application instance try using the Application.SetUnhandledExceptionMode Method.
From above MSDN Link:
It is often not feasible to catch all of the exceptions thrown by
Windows Forms. Using this method, you can instruct your application
whether it should catch all unhandled exceptions thrown by Windows
Forms components and continue operating, or whether it should expose
them to the user and halt execution.
Public Shared Sub Main()
' Add the event handler for handling UI thread exceptions to the event.
AddHandler Application.ThreadException, AddressOf Form1_UIThreadException
' Set the unhandled exception mode to force all Windows Forms errors to go through
' our handler.
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException)
' Add the event handler for handling non-UI thread exceptions to the event.
AddHandler AppDomain.CurrentDomain.UnhandledException, AddressOf CurrentDomain_UnhandledException
' Runs the application.
Application.Run(New Form1()) '' This is your applications Main Form
End Sub
Private Shared Sub Form1_UIThreadException(ByVal sender As Object, ByVal t As ThreadExceptionEventArgs)
'Put Error Handling Code here see the MSDN article for an example implementation
End Sub
Private Shared Sub CurrentDomain_UnhandledException(ByVal sender As Object, _
ByVal e As UnhandledExceptionEventArgs)
''Put Error Handling Code here see the MSDN article for an example implementation
End Sub
Sorry, misunderstood your question. Try putting your logic in a method and just try to call that method in every try catch statement you have.
Example:
Public Shared Sub Method1()
Try
'Method logic here
Catch ex As Exception
EmailError(ex)
End Try
End Sub
Public Shared Sub EmailError(ex As Exception)
'your remote error email logic here
End Sub
I'm trying to make a program of mine into a multithreaded application, but I've hit a pair of snags that I documented in the following code. Any help that I can get with this to make it behave properly would be greatly appreciated so I can expand this stub into a more efficient version of my existing application.
Thank you for any advice you have on this matter.
- Aaron
Imports System.Threading
Public Class frmMain
''' <summary>Initializes the multithreaded form</summary>
Private Sub Initialize() Handles MyBase.Load
AddThread(AddressOf Update_UI)
running = True
For Each Thread In ThreadPool
Thread.IsBackground = True
Thread.Start()
Next
End Sub
''' <summary>Terminates the multithreaded form</summary>
Protected Overrides Sub Finalize() Handles MyBase.FormClosing
running = False
For Each Thread In ThreadPool
Thread.Join()
Thread = Nothing
Next
End Sub
''' <summary>Adds a worker thread to the ThreadPool</summary>
''' <param name="pointer">The AddressOf the function to run on a new thread.</param>
Private Sub AddThread(ByRef pointer As System.Threading.ParameterizedThreadStart)
Dim newthread As Integer
If ThreadPool Is Nothing Then newthread = 0 Else newthread = ThreadPool.GetUpperBound(0) + 1
ReDim Preserve ThreadPool(newthread)
ThreadPool(newthread) = New Thread(pointer)
End Sub
''' <summary>Updates the User Interface</summary>
Private Sub Update_UI()
'HELP: The commented out lines in this subroutine make the program work incorrectly when uncommented.
'HELP: It should echo 'output' to the titlebar of frmMain, but it also makes the form unresponsive.
'HELP: When I force the form to quit, the 'termination alert' does not trigger, instead the application hangs completely on Thread.Join (see above).
'HELP: If I remove DoEvents(), the form is unable to be closed...it simply goes unresponsive. Shouldn't the multithreading keep us from needing DoEvents()?
'If Me.InvokeRequired Then
' Me.Invoke(New MethodInvoker(AddressOf Update_UI))
'Else
While running
Dim output As String = System.DateTime.Now + " :: Update_UI() is active!"
Debug.Print(output)
'Application.DoEvents()
'Me.Text = output
End While
Debug.Print(System.DateTime.Now + " :: Termination signal recieved...")
'End If
End Sub
Delegate Sub dlgUpdate_UI()
Private ThreadPool() As Thread
Private running As Boolean
End Class
Yes, none of what you tried can work properly. You correctly identified the need to use Control.Invoke() to run the Me.Text assignment on the main thread. This is what is going wrong:
Your Invoke() call makes the entire method run on the main thread. It will start executing the loop and never exit. Your form goes catatonic since it can't do anything else anymore, like repaint the caption bar to show the changed text or respond to user input
The DoEvents call makes the form come back alive but now you've got a new problem: the user can close the window and your code keeps running. The running flag will never be set to false so the program won't stop. The user interface is gone though. Code would normally bomb on an ObjectDisposedException but not in your specific case, the Text property is stored in a private variable
You could alter the code so that only the Me.Text assignment runs on the main thread. But now you've got a new problem: the main thread will get pummeled by invoke request and doesn't get around to doing its regular (low priority) duties anymore. It goes catatonic. The essential problem is that you are trying to update the caption bar way too fast. There's no point, the user cannot read that fast. Update 20 times per second is plenty and looks smooth to the human eye
Do not use the Finalize() method for tasks like this, the code can easily trigger the 2 second finalizer thread time-out, bombing your program.
Do consider using the BackgroundWorker class, it takes care of some of these details.
It's the do while loop that is burning up all your cycles so you are loosing the battle and keeping the procesor busy no matter how many threads you use. Something like the following will be better suited for what you are trying to achieve.
Imports System.Threading
Public Class Form1
Private t As New Timer(AddressOf DoTimer, Nothing, 1000, 1000)
Private Sub DoTimer(ByVal state As Object)
UpdateUi()
End Sub
''' <summary>Updates the User Interface</summary>
Private Sub UpdateUi()
If InvokeRequired Then
Invoke(New DlgUpdateUi(AddressOf UpdateUi))
Else
Dim output As String = DateTime.Now & " :: Update_UI() is active!"
Debug.Print(output)
Text = output
End If
End Sub
Delegate Sub DlgUpdateUi()
Private Sub Form1_FormClosing(sender As Object, e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
t.Dispose()
End Sub
End Class
If I have said once I have said it a million times. Using Invoke, while useful in many situations, is abused and way overused. If all you want to do is have the progress of the worker threads displayed to a user then using Invoke is not always the best option. And it does not look like the best option here either.
Instead, publish the status text you are assigning to output into a variable that can be accessed via the UI thread. Then use a System.Windows.Forms.Timer to periodically poll its value at a more reasonable rate...maybe every 1 second or so. The Tick event already runs on the UI thread so you can immediately begin using this value to display to the end user by manipulating the various UI controls.
Strings are really easy to pass around from thread to thread because they are immutable which means they are inherently thread-safe. The only thing you really have to worry about is making sure the UI thread sees the most recent reference published to the shared variable. In C# you would use the volatile keyword for this. In VB you can use Thread.VolatileRead from the UI thread and Thread.VolatileWrite from the worker thread. Of course, if you are more comfortable wrapping the reads and writes in a SyncLock that is perfectly acceptable as well.
I'm trying to write a simple program to monitor a folder for new files in VB.NET 2010, and am having some trouble.
Here's a simplified version of what my program looks like:
Imports System.IO
Public Class Main
Public fileWatcher As FileSystemWatcher
Sub btnGo_Click(sender As System.Object, e As System.EventArgs) Handles btnGo.Click
'//# initialize my FileSystemWatcher to monitor a particular directory for new files
fileWatcher = New FileSystemWatcher()
fileWatcher.Path = thisIsAValidPath.ToString()
fileWatcher.NotifyFilter = NotifyFilters.FileName
AddHandler fileWatcher.Created, AddressOf fileCreated
fileWatcher.EnableRaisingEvents = True
End Sub
Private Sub fileCreated(sender As Object, e As FileSystemEventArgs)
'//# program does not exit when I comment the line below out
txtLatestAddedFilePath.Text = e.FullPath
'//# e.FullPath is valid when I set a breakpoint here, but when I step into the next line, the program abruptly halts with no error code that I can see
End Sub
End Class
As you can see, I have a button which will initialize a FileSystemWatcher when clicked. The initialization works, and when I place a new file in the monitored directory, the program reaches the fileCreated sub. I can even see that e.FullPath is set correctly. However, it exits abruptly right after that with no error code (none that I can see, anyways). If I comment everything in the fileCreated sub out, the program continues running as expected.
Any ideas as to why it's dying on me? Any help would be greatly appreciated. I'm fairly new to VS/VB.NET, so maybe I'm just making a silly mistake. Thanks!
Could be a cross-thread operation exception.
Try this:
Private Sub fileCreated(sender As Object, e As FileSystemEventArgs)
me.Invoke(New MethodInvoker(Function() txtLatestAddedFilePath.Text = e.FullPath))
End Sub
or (even better in your context), during fileWatcher initialization:
fileWatcher = New FileSystemWatcher()
fileWatcher.SynchronizingObject = me
[...]
Explanation:
http://www.blackwasp.co.uk/FileSystemWatcher.aspx (see Preventing Cross-Thread Operations)
Excerpt:
By default, when the FileSystemWatcher
object raises notification events, the
delegate calls are made on a thread
from the system thread pool. This will
generally not be the same thread as
that being used to control the form.
As the demonstration application will
require that the file changes be
logged within a visual element of the
form, using the allocated thread to
modify the list box contents would
result in a cross-threading operation
and an IllegalOperationException being
thrown.