VB.Net Pausing Windows Shutdown or Logoff - vb.net

I tried this to show some MsgBox when shutdown or logoff is detected.. like "You're logging off.. .
Public Class frmDetectEnd
Private Sub frmDetectEnd_Load(sender As Object, e As EventArgs) Handles MyBase.Load
AddHandler Microsoft.Win32.SystemEvents.SessionEnding, AddressOf Handler_SessionEnding
End Sub
Public Sub Handler_SessionEnding(ByVal sender As Object, ByVal e As Microsoft.Win32.SessionEndingEventArgs)
If e.Reason = Microsoft.Win32.SessionEndReasons.Logoff Then
MessageBox.Show("User is logging off")
ElseIf e.Reason = Microsoft.Win32.SessionEndReasons.SystemShutdown Then
MessageBox.Show("System is shutting down")
End If
End Sub
End Class
This detection work's OK but I want to stop shutdown/logoff process if MsgBox is shown, because at this point the shutdown/logoff process is executed and it stops with Windows message "This program is preventing to log you off...".
However, I would like that after getting message "User is logging of/System is shutting down" the user can select command button to proces something and then Shutdown or LogOff can continue.

It's pretty much impossible to cancel a shutdown/logoff after it has started. At most you can get the "This program is preventing Windows from shutting down" message, like you said in your question.
A potential solution would be to create system-wide hooks for the InitiateShutdown, InitiateSystemShutdown and InitiateSystemShutdownEx functions (requires C++ or some other kind of low-level language). However, they would need to be coded carefully and tested thoroughly, and even if you managed to get them working most of the times, there's no guarantee that the OS isn't bypassing them via some other, even deeper functions (it contains a lot of both undocumented and name-mangled functions).
Source:
I researched this heavily about two years back in an attempt to make a program that would completely block system shutdown if the user chose to. However, as mentioned above, it has proven to be extremely difficult, if not impossible. None of my attempts were a success.

You could immediately call shutdown /a when a shutdown is detected. (/a stands for "abort").
That's a built-in command-line utility in Windows, so you'd call it the usual way of calling command-line utilities within a vb app. The trick is it has to happen fast enough.
Another more hacky way you could try would be to send a CTRL+ALT+DEL and then the Task Manager (and then hide it. Maybe you can do it all so fast the user would only see a quick flash). That usually aborts a shutdown when I've manually done it.

Related

Need to call functions and subs from windows services in VB.Net

I am new to vb.net and have a project that I have made my first windows service. Now I have a function that retrieves a count of transactions. I would like to call that function and put the results in a text file. I can hard code a stream to put into the text file, but whenever I call the function, the services just crashes. Not errors just dies. What am I doing wrong?
I have tried coding the function inside of the service-nope
I coded the function in a separate class-Nope! dies when I call it
Private Sub BrowserMailSender(obj As Object, e As EventArgs)
Try
FileIO.WriteToFile("service is started:" + Now + vbNewLine)
My_Count() 'service dies here
FileIO.WriteToFile("end" + vbNewLine)
Catch ex As Exception
MsgBox(ex)
End Try
the function works if I call from the main project but I would like the service to run and save the data behind the scenes.
The call to MsgBox is at the root of the problem. A Windows Service runs in a context where it does not have the ability to present a User Interface to the user. You'll have to find another way to communicate errors, such as the Event Log or a log file.
Prior to Vista, the line between services and the user was permeable, partly because the OS wasn't yet designed to keep them isolated, and partly because most users ran with full administrative privileges all the time. From Vista forward, you have to work "in the dark".
There are ways present a UI to the user, and one of the answers here briefly mentions one of them. However, I would caution you against trying to present a UI at all. The main principle of a service is that it sits in the background and does things without requiring the user to interact with it. Presenting a UI for events that the user is not aware are happening at that moment is an asymmetrical relationship. It could block your service indefinitely when a user isn't expecting to have to interact with it to allow it to continue.

How to correctly terminate an application?

In the past I've usually added the following line to the KeyDown sub in order to end my applications:
If e.KeyCode = Keys.Escape Then End
However, upon reading the documentation on what End actually does, it turns out that it probably is one of the worst ways to end the application in terms of releasing resources, etc.
So now I'm trying to find the recommended way to terminate the application which will properly handle all resources, etc. I've found these two questions on SO (here and
here) but I can't conclude what the right answer actually is.
The accepted answer on the first link says that applying Close() to all forms is the correct way and will release all resources used correctly. This isn't convenient in applications with multiple forms, and further down Application.Exit() is suggested, and even:
Application.Exit()
End
which will definitely make sure the program ends even if the first line fails.
On the other hand, the accepted answer on the second link says "you should never have to call Application.Exit() in a properly-designed application", contradicting the above and an answer further down which says Application.Exit() calls Close() on all forms in later versions of .Net.
This has lead me to confusion — what is wrong with Application.Exit()?
If there is no problem with it, then am I right in thinking the best one to go with is:
Application.Exit()
End
or is that overdoing it? Otherwise, when does Application.Exit() fail to work (except when I write code which cancels it)?
Note: Although this question applies to all programs I make, including ones with multiple forms, I recently have started using Sockets (with the TcpClient/Listener classes) to make connections between computers and would appreciate any additional information relating to this when terminating the program in the middle of a connection. A comment in my recent question assures me that calling Socket.Close() isn't even necessary, but now I realise that this may not be completely true since I was using End to terminate before.
Application.Exit posts an exit message to all message loops for that application.
It's a perfectly acceptable way of closing an application and will cause all forms to attempt to shutdown. Individual forms can override this behaviour, for instance if they have unsaved work. This will leave your application running afterward so you need to decide if that needs to be handled.
Saying that I only use it if an external actor needs to shutdown my application. Otherwise I leave the app to exit when its main form closes.
You also need to consider the state of any foreground threads you have as these can allow all your forms to close but leave the thread processing in the background.
End is a very brute force technique and should be used as a last resot. A well designed application should be able to shutdown by closing the forms or by calling application.exit. I have used the approach in the past of launching a timer which will call End just before I call Application.Exit ... at least I give it a chance to complete gracefully.
Note: Application.Exit doesn't block. So Application.Exit : End might as well be End which is not ideal.
Here's the timer I use:
Dim forceExitTimer = New Threading.Timer(Sub() End, Nothing, 2500, Timeout.Infinite)
Application.Exit()

Application.restart - Puzzling behaviour in VB.Net

OK guys, what's going on here?
In this VB code:
Module Module1
Sub Main()
If MsgBox("Restart?", MsgBoxStyle.OkCancel) = MsgBoxResult.Ok Then
Application.Restart()
MsgBox("restarting")
Else
MsgBox("Cancel")
End If
End Sub
End Module
If this code is contained within a module, Application.Restart does not end the running application till the End Sub is hit. Any code that appears before then is executed - eg the 'Restarting' messagebox appears.
However, if the equivalent code is run within a form then Application.Restart terminates the running application immediately.
(Both cases correctly start a new instance). This behaviour does not appear to be documented anywhere - the implication in the docs is that it's synonymous with 'End' as far as the termination of the running instance is concerned. Am I missing something?
The best way to answer these questions it to look at the code itself using Reflector (or Microsoft's free for debugging code, when it is available).
With Reflector, you can see (in .NET Framework 4.0) System.Windows.Forms.Application.Restart looks for four different types of applications:
the initial check that Assembly.GetEntryAssembly is Nothing, throwing a NotSupportedException if it is;
the Process.GetCurrentProcess.MainModule.FileName is ieexec.exe in the same folder as the current .NET Framework (specifically the folder where the module defining Object is);
ApplicationDeployment.IsNetworkDeployed is True; and
the general case.
All three supported cases determine the method to start the process again, calls Application.ExitInternal and starts the process again.
Application.ExitInternal closes open forms, including the check for a form attempting to abort the close by setting FormClosingEventArgs.Cancel to True. If no form attempts to cancel, the forms are closed and, using ThreadContext.ExitApplication, all ThreadConnexts are cleaned up (Disposed or their ApplicationContext.ExitThread is called).
NB No Thread.Abort is called, so threads are NOT explicitly ended in any way. Also the Windows.Forms ModalApplicationContext, does not even call the ThreadExit "event" that a normal ApplicationContext does.
(Note that all three supported cases in Application.Restart ignore the result of Application.ExitInternal, so if a form does attempt to abort all that happens is any other forms don't get a chance to close, and the ThreadContexts are not cleaned up!)
Importantly for your question, it does NOT attempt to actually exit the current threads or the entire application (other than closing open forms and thread contexts).
However, by the time your MsgBox("restarting") executes the new application has been started.
You need to manually exit the application after calling Application.Restart. In the case of "run[ing] within a form" (you don't show the code where you tested this) either the form is closed and that is what you considered as the current application ending, or extra stuff that Windows.Forms (or VB) sets up means the application is exited by one of the "events" that throw when the clean up that does occur runs.
In other words, before testing it I expected the MsgBox to appear even when this code is in say the Click event of a form, with the form disappearing first, and the application restarting at the same time.
Having tested it, the MsgBox tries to appear, as I hear the beep that corresponds to it, and if I comment it out the beep does not occur. So something causes the application to exit even though it should have a message box open, and even putting a MsgBox in a Finally outside of the Application.Run does not appear on a Restart. (Note a similar effect is seen if you call MsgBox after Application.Exit.)
So something set up by Windows.Forms (or VB) does actually call something like Environment.Exit which calls the Win32Api ExitProcess and does not regard Finally or call Dispose or Finalize.
Note the Application.Restart documentation implies it is not for Console Applications though it currently works fine (except for the not quitting straight away, which is not implied by Application.Exit).
I am able to restart the application by closing and disposing all open forms, except the one that is calling.
For j As Integer = Application.OpenForms.Count - 1 To 0 Step -1
Dim frm = Application.OpenForms(j)
If frm.Text <> callingForm.Text Then
frm.Close()
frm.Dispose()
End If
Next
Application.Restart()
This is going to be, admittedly, a bit of a guess based on some fairly top-level reading I've done about Application.Restart(), but I think this is occurring due to the way Restart operates internally.
I think Restart() tries to do as much "intelligent" cleanup as it can for a process that is being terminated, and in what may be considered a fairly simplistic implementation, tracks certain of the things to be "cleaned up," possibly calling Dispose() on them (if applicable), which normally is a reasonable step to take. In your case, I'm going to make the guess that a background thread, or form, holds a reference to something - can't say what - that prevents the code from shutting down. It may become aware that it is executing inside a method, and wants to give that method a chance to complete before killing it - waiting on the completion of that sub/method.
I've seen other instances of Restart actually causing a really strange "Collection was Modified" error when no collection was involved. That's suggesting to me, probably naively, that the internal cleanup Restart is trying to achieve is reposed in a simple list, but in certain circumstances, the cleanup modifies the element in an unexpected way, a way that modifies the collection, causes the exception to be thrown, and aborts the exit/restart.

Can I avoid a possible hang by limiting the time a method is allowed to take?

I am using an external DLL (pdfsharp) to open (then manipulate) lots of PDF files. I use:
Dim inputDocument = Pdf.IO.PdfReader.Open(PDFPath, IO.PdfDocumentOpenMode.ReadOnly)
Problem is - it seems to hang on certain, rare files. I don't seem to get any timeout - it just hangs for hours on this line. We read thousands of files with this code, always on tiny files, so I was thinking that a quick workaround might be to somehow timeout if the this method takes more than a second or two. But I don't see a simple way to do this. I am hoping to avoid spinning up a worker thread.
Any thoughts on how I might limit this threads allowed execution time, or is there a better (but simple) way?
The Open() call should not hang. Never. If you provide us with a file that causes Open() to hang, we can investigate this.
Does your program run on a server? Do you use a DEBUG build of PDFsharp? Maybe it's just a simple Debug.Assert() that is triggered, but noone can answer it. Using a RELEASE build would solve this.
We ended up working around this problem by creating an AbortableBackgroundWorker. I am not sure whose code this ended up being - but we found it online and sharing it here. In the rare case where one of the PDF's hangs the PdfSharp Open() call, we abort the background worker.
Public Class AbortableBackgroundWorker
Inherits BackgroundWorker
Private workerThread As Thread
Protected Overrides Sub OnDoWork(e As DoWorkEventArgs)
workerThread = Thread.CurrentThread
Try
MyBase.OnDoWork(e)
Catch generatedExceptionName As ThreadAbortException
e.Cancel = True
'We must set Cancel property to true!
'Prevents ThreadAbortException propagation
Thread.ResetAbort()
End Try
End Sub
Public Sub Abort()
If workerThread IsNot Nothing Then
workerThread.Abort()
workerThread = Nothing
End If
End Sub
End Class

How to prevent UI from freezing during lengthy process?

I need to write a VB.Net 2008 applet to go through all the fixed-drives looking for some files. If I put the code in ButtonClick(), the UI freezes until the code is done:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
'TODO Find way to avoid freezing UI while scanning fixed drives
Dim drive As DriveInfo
Dim filelist As Collections.ObjectModel.ReadOnlyCollection(Of String)
Dim filepath As String
For Each drive In DriveInfo.GetDrives()
If drive.DriveType = DriveType.Fixed Then
filelist = My.Computer.FileSystem.GetFiles(drive.ToString, FileIO.SearchOption.SearchAllSubDirectories, "MyFiles.*")
For Each filepath In filelist
'Do stuff
Next filepath
End If
Next drive
End Sub
Google returned information on a BackGroundWorker control: Is this the right/way to solve this issue?
If not, what solution would you recommend, possibly with a really simple example?
FWIW, I read that Application.DoEvents() is a left-over from VBClassic and should be avoided.
Thank you.
The BackgroundWorker is a good way to solve your problem. Actually the documentation states this:
The BackgroundWorker class allows you to run an operation on a separate, dedicated thread. Time-consuming operations like downloads and database transactions can cause your user interface (UI) to seem as though it has stopped responding while they are running. When you want a responsive UI and you are faced with long delays associated with such operations, the BackgroundWorker class provides a convenient solution.
Put the process into a separate thread....
...using the BackgroundWorker component.
Disable UI components that should not be usable while the process workd.
Finished - the UI will still be responsive.
The key is to seperate the UI code from the actual functionality code.
The time-consuming functionality should run on a seperate thread. To achieve this, you can either:
Create and start a Thread object by
yourself
Create a Delegate and use
asynchronous invokation (using
BeginInvoke).
Create and start a BackgroundWorker.
As you mentioned, you should avoid Application.DoEvents(). A proper breakdown of the application's functionality will allow you to create an application which is designed to be responsive, rather than creating a non-responsive application with DoEvents "fixes" (which is costly, considered bad practice, and implies a bad design).
Since your method doesn't return a value and doesn't update the UI, the fastest solution might be creating a Delegate and using "fire and forget" asynchronous invokation:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Call New Action(AddressOf DrivesIteration).BeginInvoke(Nothing, Nothing)
End Sub
Private Sub DrivesIteration()
Dim drive As DriveInfo
Dim filelist As Collections.ObjectModel.ReadOnlyCollection(Of String)
Dim filepath As String
For Each drive In DriveInfo.GetDrives()
If drive.DriveType = DriveType.Fixed Then
filelist = My.Computer.FileSystem.GetFiles(drive.ToString, FileIO.SearchOption.SearchAllSubDirectories, "MyFiles.*")
For Each filepath In filelist
DoStuff(...)
Next
End If
Next
End Sub
BTW, For..Next blocks no longer have to end with "Next (something)", it is obsolete - VB now infers the (something) by itself, so there is no need to state it explicitly.
A. put up a PROGRESS BAR... update it and .REFRESH it ... If all you want is to show that your not dead.
B. DoEvents is evil sounds A LOT like "NEVER USE A GOTO..." pleeeeze pleeeze pleeeze there are times and circumstances where any language's syntax can be harmful AND helpful. Why jump through a million hoops just to essentially do "A" above?
<soapbox>
If you know that something takes a LONG TIME and you also know that no other operations can take place WHILE YOUR WAITING (i.e. it is essentially a serial process) than if you do ANYTHING like that and push it into "the background" then you'll be sprinkling "ITS_OK_TO_CONTINUE" booleans all through the rest of your code just waiting for the file process to end anyway.... whats the point of that? All you've done is complicate your code for the sake of... hmm... "good programming?" Not in my book.
Who cares if DoEvents is "left over" from the ICE AGE. Its EXACTLY the right thing in MANY circumstances. For example: The framework gives you ProgressBar.Refresh but you'll see that its not exactly "working" unless you post-pend a few DoEvents after it.
</soapbox>
C. A background task is just that -- background; and you generally use it to operate on NON-SERIAL tasks or at least asynchronous tasks that MAY or MAY NOT update the foreground at some point. But I'd argue that anytime a so-called background task HALTS the foreground then it is (almost) by definition --- a FOREGROUND task; regardless of HOW LONG it takes.