Session Implementation in windows Forms - vb.net

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

Related

Invoke of ListView runs sub twice

I have been running the following code for a couple days fine but after doing some debugging yesterday I found out that the Sub has been firing twice. Some background - This is a postage app that ships all of our packages. I neede to add a listview to the main form to show various rates that are returned from Easypost. When I first add the listview the app ran great from debug but after a compile it would completely hang after the list view was populated. So after a couple days of searching I added the invoke method which allowed the app GUI to continue after the listview was populated. So first I see if rates are returned and see that when it hits the lvRates.InvokeRequired it does the invoke and and then does the EasyPostGetServices a 2nd time which waste resources and creates to shipments at easypost loading the 2nd rates returned.
Private Sub GetEasyPostServices()
EasyPostGetServices(dictEPFromAddress, dictEPToAddress, dictEPPackage)
If EasyPostShipment.rates.Count > 0 Then
If lvRates.InvokeRequired Then
lvRates.Invoke(New MethodInvoker(AddressOf GetEasyPostServices))
Else
For Each rate As Rate In EasyPostShipment.rates
lvRates.Items.Add(New ListViewItem({rate.carrier, rate.rate, UCase(rate.service), rate.id}))
Next
End If
Else
MsgBox("Based on your inputs or preferences, no shipping services were available. Shipping services shown ignore your preferences. If none are shown, there are no shipping services available for this order.")
End If
End Sub
My question is why is the code running twice and how do I prevent it from happening. I have moved the invoke around but everything else I try the GUI freezes when run from exe.
This was based on your original question. I've added a BackgroundWorker in a button click to cause GetEasyPostServices to start running on a non-UI thread. The crux here is how to properly invoke UI calls. The three methods at the bottom demonstrate that.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
' this method is running on the UI thread as it handles a UI event
Dim bw As New System.ComponentModel.BackgroundWorker()
AddHandler bw.DoWork, New System.ComponentModel.DoWorkEventHandler(Sub(oo, ee) GetEasyPostServices())
' this call causes GetEasyPostServices to run on a non-UI thread
bw.RunWorkerAsync()
End Sub
Private Sub GetEasyPostServices()
' running on a non-UI thread. Any UI calls must be invoked
Cursor.Current = Cursors.WaitCursor
Try
Dim frAddress As New Dictionary(Of String, Object)() From {
{"street1", "12345 Hill Dr"},
{"street2", ""},
{"city", "sometown"}}
Dim toAddress As New Dictionary(Of String, Object)() From {
{"street1", sAdd1},
{"city", sCity},
{"state", sRegion},
{"verifications", New List(Of String)() From {"delivery"}}}
Dim package As New Dictionary(Of String, Object)
If GlobalVariables.giLength > 0 Then
package.Add("length", GlobalVariables.giLength.ToString)
package.Add("width", GlobalVariables.giWidth.ToString)
package.Add("height", GlobalVariables.giHeight.ToString)
package.Add("weight", (sLbs * 16) + sOz)
End If
EasyPostGetServices(frAddress, toAddress, package)
If EasyPostShipment.rates.Count > 0 Then
' hide the UI invocation logic in methods
updateListView()
Else
MsgBox("Based on your inputs or preferences, no shipping services were available. Shipping services shown ignore your preferences. If none are shown, there are no shipping services available for this order.")
clearAndFocusTbOrder()
End If
Finally
Cursor.Current = Cursors.Default
End Try
End Sub
' these methods manage their own UI invocation.
Private Sub updateListView()
If lvRates.InvokeRequired Then
lvRates.Invoke(New Action(AddressOf updateListView))
Else
lvRates.Visible = True
lvRates.Items.Clear()
For Each rate As Rate In EasyPostShipment.rates
lvRates.Items.Add(New ListViewItem({rate.carrier, rate.rate, UCase(rate.service), rate.id}))
Next
SortListView()
lvRates.Items(0).Selected = True
Button1.Visible = True
End If
End Sub
Private Sub SortListView()
If lvRates.InvokeRequired Then
lvRates.Invoke(New Action(AddressOf SortListView))
Else
lvRates.Sort()
End If
End Sub
Private Sub clearAndFocusTbOrder()
If tbOrderID.InvokeRequired Then
tbOrderID.Invoke(New Action(AddressOf clearAndFocusTbOrder))
Else
tbOrderID.Clear()
tbOrderID.Focus()
End If
End Sub
It's all about what, where, and why you invoke. In your example it wasn't clear if GetEasyPostServices started on the UI or not. I caused it not to so I could demonstrate how to properly invoke the UI calls. You then would need to invoke UI calls because it's not currently on the UI thread. That's the "why".
Also, it's key to run a minimal amount of code on the UI thread to prevent unnecessary processing and hanging the UI thread. Your original configuration ran all your code once, then ran it all again, on the UI thread.

Making sure async tasks complete before vb.net application terminates

I'm creating a vb.net desktop application. This application includes some asynchronous functions. When the user closes the application via the red X in the upper-right corner, there is some logic to possibly run one or more of these async functions. The problem is, the program terminates before they are complete. I figured using "Await" in my call would do that, but apparently not.
I found this thread that talks about using ManualResetEvent, but I'm having trouble understanding all of it, especially since the question is in the context of a console app, and the MSDN documentation the answer links to is about specifying threads, not simply using async tasks. As an attempt at using it anyway, I tried adding this to my main form:
Public resetEvent As ManualResetEvent = New ManualResetEvent(False)
And immediately after the call to one of these functions, I added this (quote includes the call):
Await activeCount.SerializeAsync(activeCount)
resetEvent.WaitOne()
And at the end of my async function itself, before returning the Task, added this:
frmMain.resetEvent.Set()
I don't think I'm using that right, though. The program still terminates before it's complete anyway.
Even before that, I figured the best place for such a thing would be in ApplicationEvents MyApplication_Shutdown, but I'm not sure how to know if such a function is still running at that point.
So what is the best way to make sure all my async functions complete before the application terminates in this situation?
Thank you!
UPDATE AFTER ACCEPTED ANSWER:
Though F0r3v3r-A-N00b's answer worked, I realized I need to use a dialog in certain cases. I couldn't call that within the background worker because the dialog is on the GUI thread, not the background thread. I tried moving things around so I'd call the dialog first, then make the background worker and all that, but for whatever reason I couldn't get it to work.
Long story short, I got around it by simply making a synchronous version of my functions, and so I could say 'if the user terminated the program and I need to call any of these functions before closing, call the synchronous versions instead'. That works. Thanks!
Try this. Create a new project. Add 1 label and backgroundworker to your form. Paste this in your form's code area:
Public Class Form1
Dim taskCompleted As Boolean = False
Dim taskIsrunning As Boolean = False
Private Sub BackgroundWorker1_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Threading.Thread.Sleep(5000)
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
taskCompleted = True
taskIsRunning = False
Label1.Text = "Background task completed."
Me.Close()
End Sub
Private Sub Form1_FormClosing(sender As Object, e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
If taskIsRunning Then
e.Cancel = True
Exit Sub
End If
If Not taskCompleted Then
taskIsRunning = True
Label1.Text = "Starting background task."
BackgroundWorker1.RunWorkerAsync()
Label1.Text = "Background task is running."
e.Cancel = True
End If
End Sub
End Class

Cross Thread Error Trying To Open New Form Instance Inside Timer

I am trying to create little notification popups for my application and have created a new form that fades in and out and sits on top of my main form (seems to work okay).
My problem is that I have some code that sits inside a timer event that does some data checking every minute or so. Depending on the data results, I sometimes need to show a notification. However, it is causing me Cross-Thread errors (which is understandable), but I'm not sure how to get around it.
Example (in a nutshell) of what I am trying to do is:
Private Sub RefreshData(sender As Object, e As System.Timers.ElapsedEventArgs)
Try
MainRefreshTimer.Interval = GetInterval()
MainRefreshTimer.Start()
'Do some data checking here...
If data returns true then
Dim notify as New frmNewNotification("Some Text", 10) '<== Show some text for 10 seconds then close the form automatically
notify.Show() '<== Cross Thread Error occurs from this
End If
...
End Sub
I would try one of this ideas:
Shorcut: Put this in your Form_Load
Control.CheckForIllegalCrossThreadCalls = False
Or, better, something like:
Private Sub delRefreshData(data as Object)
If Me.InvokeRequired Then
' Invoke(New MethodInvoker(AddressOf delRefreshData)) ' no params
Invoke(New MethodInvoker(Sub() delRefreshData(data)))
Else
'Do some data checking here...
If data returns true then
Dim notify as New frmNewNotification("Some Text", 10)
notify.Show() '
End If
End if
Using InvokeRequired vs control.InvokeRequired
Edited:
To avoid in future be blamed for that Shorcut, I have to say that
Control.CheckForIllegalCrossThreadCalls isn't a good advice/solution, as is discussed here:
Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on

System.ObjectDisposedException in Multithreaded Application

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.

Using thread to open form

I am currently studying VB.NET and I got question about using thread to open the form.
For example, when I click open button, then tread will start and open another form to adding or changing data.
Therefore, I tried to implement this part such as
Private Sub menu_Click(sender As Object, e As EventArgs) Handles menu.Click
Dim A As System.Threading.Thread = New Threading.Thread(AddressOf Task_A)
A.Start()
End Sub
Public Sub Task_A()
frmBuild.Show()
End Sub
However, I am getting error to open the frmBuild by thread. Do I need to use other method to open form?
And, How can we kill the thread when fromBuild closes?
This is almost always a bad idea. You shouldn't try to use a separate thread to open a Form - instead, open all of your forms on the main UI thread, and move the "work" that would otherwise block onto background threads. BackgroundWorker is a common means of handling the work.
That being said, if you need to do this for some unusual reason, you need to do two other things.
First, you need to set the apartment state of that thread. You also need to use Application.Run to display the form, and that form must be created on the proper thread:
Private Sub menu_Click(sender As Object, e As EventArgs) Handles menu.Click
Dim th As System.Threading.Thread = New Threading.Thread(AddressOf Task_A)
th.SetApartmentState(ApartmentState.STA);
th.Start()
End Sub
Public Sub Task_A()
frmBuild = New YourForm() ' Must be created on this thread!
Application.Run(frmBuild)
End Sub
In order to close the Form from the other thread, you can use:
frmBuild.BeginInvoke(New Action(Sub() frmBuild.Close()))
And, How can we kill the thread when fromBuild closes ?
The thread will automatically shut down when the form is closed, if it's written as shown above.