Execute a subroutine before exiting the application - vb.net

I am designing a client-server application, where the client requests various data and the server retrieves it from the local SQL server.
I need to execute a function when the application exits in order to tell associated services that the server is offline, but I am unable to figure out exactly how to do this.
This related question seems to have the answer:
handle the AppDomain.ProcessExit event, which is raised when the application's parent process exits.
however when using the below code the onExit subroutine does not get executed on application closure, probably because I am using the wrong approach to this problem or that Environment.Exit isn't executed when the application exits:
Dim myConn As SqlConnection
Dim cmd As New SqlCommand
Sub Main()
AddHandler AppDomain.CurrentDomain.ProcessExit, AddressOf onExit
End Sub
Public Sub onExit(sender As Object, e As EventArgs)
'Unrelated code removed here.
End Sub
To clarify, I need it to execute onExit when the user closes the application; I am not looking to close the application via a console command.
I've also thought that perhaps I could add a Handle to onExit but I have no idea what to use here.
Is it possible to reliably execute a subroutine or function when the user attempts to close the application via the standard Windows Explorer user interface?
This is a console application, I know this can be very easily done in Windows Forms and thought it would be easy in console. Obviously not.

You need to import the SetConsoleCtrlHandler like in this sample:
Module Module1
Public Declare Function SetConsoleCtrlHandler Lib "kernel32" (Handler As ConsoleCtrlDelegate, Add As Boolean) As Boolean
Public Delegate Sub ConsoleCtrlDelegate()
Sub Main()
SetConsoleCtrlHandler(New ConsoleCtrlDelegate(AddressOf OnExit), True)
Console.WriteLine("Please try to close down...")
Console.ReadLine()
End Sub
Sub OnExit()
MsgBox("Help I'm being closed!")
End Sub
End Module

Related

Have an IF statement constantly check

Is there anyway to have an IF statement constantly checking? I'm working with a List and I need to check if the list count exceeds a variable. I've since noticed the easiest way to make the program run smoothly is to have an IF statement checking constantly while the program is running.
The best approach for this would be to change from 'List' to 'BindingList'. This is an event enabled list which will fire events when the list changes:
Private WithEvents mList As New System.ComponentModel.BindingList(Of String)
Public Sub Main()
mList.Add("An Item")
End Sub
Private Sub mList_AddingNew(sender As Object, e As System.ComponentModel.AddingNewEventArgs) Handles mList.AddingNew
If mList.Count > 100 Then
MessageBox.Show("Threshold exceeded")
End If
End Sub
Alernatively you could start a thread / timer that polls this, however you'll have to watch out for synchronization issues.

VB.Net Multi-Threading InvokeRequired and Passing Thread

I need to be able to pass the thread name into a subroutine in order to abort it when I need to.
So I have this so far:
Dim BrowserSpawn As New System.Threading.Thread(AddressOf BrowserSub)
BrowserSpawn.Start()
Private Async Sub BrowserSub(BrowserSpawn)
...
End Sub
Because the subroutine creates a browser within Form1 groups I needed to invoke access to these controls within the sub.
Note: This works fine when I'm not passing in the thread name.
If Me.GroupNamehere.InvokeRequired Then
Me.GroupNamehere.Invoke(New MethodInvoker(AddressOf BrowserSub))
Else
'Do nothing
End If
When I'm passing in the thread name these become a problem when trying to compile:
Method does not have a signature compatible with delegate 'Delegate Sub MethodInvoker()'.
I'm hoping this is just a syntax thing but I can't seem to get it to work. Is there any way I'm able to pass in this thread name without breaking my invokerequired check?
If I try and change it to the obvious:
Me.GroupNamehere.Invoke(New MethodInvoker(AddressOf BrowserSub(BrowserSpawn)))
It tells me Addressof operand must be the name of a method (without parentheses). Although without the parentheses it's not happy either so I don't know where to go from here.
/edit:
Stumbled across How can I create a new thread AddressOf a function with parameters in VB?
Which seems to confirm what I was trying passing something like:
Private Sub test2(Threadname As Thread)
' Do something
End Sub
And the sub seems happy with that. But I'm not sure how to do that without breaking the invoker part.
Me.GroupNameHere.Invoke(New MethodInvoker(AddressOf SubNameHere))
Works normally. If SubNameHere() becomes SubNameHere(threadname as thread) then that seems happy enough but the invoke line breaks and doesn't want more than the address of.
Two slight syntax changes sorted it:
Private Async Sub SubName(ThreadNameAs Thread)
and
GroupName.Invoke(New MethodInvoker(Sub() Me.SubName(ThreadName)))

Optionally launch form in VB.Net console application

So I've set my application to a console type application and pointed it to a module containing just Sub Main, i.e.
Module mdlConsole
Sub Main(ByVal cmdArgs() As String)
If cmdArgs.Length = 0 Then
Dim frm As New frmMain
frm.Show()
End If
End Sub
End Module
Ideally if no arguments are supplied then the program would simply launch the primary form. The goal is to make this program (optionally) script-able from the command line. If arguments are supplied then the application form is not loaded and processes its features based off the command line arguments supplied.
As it is now, the program runs, briefly launches the form (frmMain) and then closes. What am I doing wrong or missing?
If you're not keen on giving me the answer, I'd be happy to be pointed in the right direction also. I don't expect anyone to just supply answers. I need to learn also.
Thanks!
For Winforms, you need to 'run' the App object, passing a form to use:
Sub Main(ByVal cmdArgs() As String)
If cmdArgs.Length = 0 Then
Dim frm As New frmMain
Application.Run(frm)
Else
' cmd line version
End If
End Sub
I see in your comment that you'd like to remove the console window that appears when running the form version of the program with the solution currently proposed. I cannot comment due to lack of reputation, so I will make this a full-fledged answer.
Consider approaching this from an inverse perspective: if you write the program as a forms application, opening it by default will bring up the form. But in the Form1_Load event, check the command line arguments; if they are greater than 0, simply run your (abbreviated) code logic here. At the end of the code, simply run Application.Exit(), like so:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
If My.Application.CommandLineArgs.Count > 0 Then
' Execute (abbreviated) code logic
' When finished, exit the program
Application.Exit()
End If
End Sub
This can also make your code cleaner and more practical if you're relying on a user-interface, because you can still access the values of form elements that the user would otherwise be modifying - but without the form showing on the screen (unless you prompt it to with a MsgBox or such).
This also works very nicely for Scheduled Tasks, as the user can run them manually with a user-interface, while the program executes without being visible via a scheduled task.
Kind of a follow-on to Chad's solution above I used the steps defined in How to have an invisible start up form? to avoid showing my form.
In short, create an Overrides subroutine that gets launched before Form1_Load:
This worked for me:
Protected Overrides Sub SetVisibleCore(ByVal value As Boolean)
If Not Me.IsHandleCreated Then
Me.CreateHandle()
value = False
MyBase.SetVisibleCore(value)
Else
Exit Sub
End If
If My.Application.CommandLineArgs.Count > 0 Then
MsgBox("Argument Sensed!")
' Execute (abbreviated) code logic
' When finished, exit the program
Me.Close()
Application.Exit()
Else
MyBase.SetVisibleCore(True)
End If
End Sub

VB.NET Service Skipping Over Sub Procedure

I'm writing a Windows Service in VB using Visual Studio 2010. The service is going to check for changes to a table in a database every minute or so. I have experience with VB6 but am new to both VB.NET and creating services, so I might be overlooking something obvious here. When the service is stopped, paused, started, or resumed, a sub procedure is supposed to be called. The problem is that the program skips right over the call to the sub procedure. I set break points at OnPause() and OnContinue() and stepped through the code, to confirm that the program is skipping right over the calls to AgentStopped() and AgentStarted(). What am I doing wrong?
Protected Overrides Sub OnPause()
Log.WriteEntry("Agent paused.") 'This is written to the log
AgentStopped() 'This line is skipped
End Sub
Protected Overrides Sub OnContinue()
Log.WriteEntry("Agent restarted.") 'This is written to the log
AgentStarted() 'This line is skipped
End Sub
Private Sub AgentStarted()
Log.WriteEntry("AgentStarted called.") 'This never written to the log
End Sub
Private Sub AgentStopped()
Log.WriteEntry("AgentStopped called.") 'This never written to the log
End Sub
As you understand, there is no way for the lines you imply to be skipped since the containing method is called. Something else is happening. I suppose the code you are showing is simplified. Right?
I would bet that either you are running a wrong version of your code by mistake, or on your real code an exception occurs inside the called methods (AgentStarted and AgentStopped).
In order to debug this, I would follow these steps:
Change the current log messages and add messages after the call to the functions
Protected Overrides Sub OnPause()
Log.WriteEntry("Agent pause beginning.")
AgentStopped()
Log.WriteEntry("Agent pause ending.")
End Sub
Protected Overrides Sub OnContinue()
Log.WriteEntry("Agent restart beginning.")
AgentStarted()
Log.WriteEntry("Agent restart ending.")
End Sub
If you see the new messages, you will be able to verify that you are actually running the right version of the code and not an older version deployed by mistake.
You should use exception handling with try - catch. I am not really sure (I have never used it), but perhaps using the UnhandledException event to get info for possible uncaught exceptions could be helpful. Another option would be to check the windows event log for errors occuring while executing a web service.
Hope I helped!

How to make a very simple asynchronous method call in vb.net

I just have a simple vb.net website that need to call a Sub that performs a very long task that works with syncing up some directories in the filesystem (details not important).
When I call the method, it eventually times out on the website waiting for the sub routine to complete. However, even though the website times out, the routine eventually completes it's task and all the directories end up as they should.
I want to just prevent the timeout so I'd like to just call the Sub asynchronously. I do not need (or even want) and callback/confirmation that it ran successfully.
So, how can I call my method asynchronously inside a website using VB.net?
If you need to some code:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Call DoAsyncWork()
End Sub
Protected Sub DoAsyncWork()
Dim ID As String = ParentAccountID
Dim ParentDirectory As String = ConfigurationManager.AppSettings("AcctDataDirectory")
Dim account As New Account()
Dim accts As IEnumerable(Of Account) = account.GetAccounts(ID)
For Each f As String In My.Computer.FileSystem.GetFiles(ParentDirectory)
If f.EndsWith(".txt") Then
Dim LastSlashIndex As Integer = f.LastIndexOf("\")
Dim newFilePath As String = f.Insert(LastSlashIndex, "\Templates")
My.Computer.FileSystem.CopyFile(f, newFilePath)
End If
Next
For Each acct As Account In accts
If acct.ID <> ID Then
Dim ChildDirectory As String = ConfigurationManager.AppSettings("AcctDataDirectory") & acct.ID
If My.Computer.FileSystem.DirectoryExists(ChildDirectory) = False Then
IO.Directory.CreateDirectory(ChildDirectory)
End If
My.Computer.FileSystem.DeleteDirectory(ChildDirectory, FileIO.DeleteDirectoryOption.DeleteAllContents)
My.Computer.FileSystem.CopyDirectory(ParentDirectory, ChildDirectory, True)
Else
End If
Next
End Sub
I wouldn't recommend using the Thread class unless you need a lot more control over the thread, as creating and tearing down threads is expensive. Instead, I would recommend using a ThreadPool thread. See this for a good read.
You can execute your method on a ThreadPool thread like this:
System.Threading.ThreadPool.QueueUserWorkItem(AddressOf DoAsyncWork)
You'll also need to change your method signature to...
Protected Sub DoAsyncWork(state As Object) 'even if you don't use the state object
Finally, also be aware that unhandled exceptions in other threads will kill IIS. See this article (old but still relevant; not sure about the solutions though since I don't reaslly use ASP.NET).
You could do this with a simple thread:
Add :
Imports System.Threading
And wherever you want it to run :
Dim t As New Thread(New ThreadStart(AddressOf DoAsyncWork))
t.Priority = Threading.ThreadPriority.Normal
t.Start()
The call to t.Start() returns immediately and the new thread runs DoAsyncWork in the background until it completes. You would have to make sure that everything in that call was thread-safe but at first glance it generally seems to be so already.
I also was looking for information on Asynchronous programming in VB. In addition to this thread, I also found the following: beginning with Visual Studio 2012 and .Net Framework 4.5, VB was given two new keywords to make a method asynchronous right in the declaration, without using Thread or Threadpool. The new keywords are "Async" and "Await". You may refer to the following links if you wish:
http://msdn.microsoft.com/library/hh191443%28vs.110%29.aspx
https://msdn.microsoft.com/en-us/library/hh191564%28v=vs.110%29.aspx
This is an older thread, but I figured I'd add to it anyway as I recently needed to address this. If you want to use the ThreadPool to call a method with parameters, you can modify #Timiz0r's example as such:
System.Threading.ThreadPool.QueueUserWorkItem(Sub() MethodName( param1, param2, ...))