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.
Related
I want to execute a function of an object when my application crashes. I have this third party library TMCTL which I am using in one of my form Main. This library is used to communicate with another windows application. Whenever my application is closed, its .Finish(CommID) method has to be executed. I have put this method in form's closing event. But sometimes, my application crashes due to unknown reasons. At that time this function doesn't get executed. This creates problem, when application is restarted.
I want to know is there any other way to execute this function apart from closing event. I tried using UnhandledException event in ApplicationEvents.vb. But I am not sure how to call TMCTL object in ApplicationEvents.vb from my form. I have declared it as Public in my form's code (Public cTmctl As TMCTL). It gives error if I try to call using Main.cTmctl in ApplicationEvents.vb
I don't think can be done this way, and if someone does come up with something I wouldn't trust it.
There are a couple of ways you could do this.
remember when you've started your 3rd party session (setting file, database, JSON file . . .), when the application closes properly you close the session and mark it as closed.
When your app starts again it can check was the last session closed. If yes, it starts another session, if no, it could (if possible) re-use the last session, or close the last session and create another.
or
Have a second service that mediates between your app and the 3rd party service. The new service (lets call it Middle Man), is called by your app to open the session on the 3rd party and to close it. The MM service monitors your app, if it has stopped running - or isn't responding - and there's an open session then it closes that session.
The second approach is helpful if you want the session to time out after some period of inactivity.
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.
My company's main software package includes a hefty configuration library which loads on startup. This config library includes some mandatory settings which, if not supplied (via command line arguments), cause the entire application to exit.
This has never been an issue for our users, who launch the software via scripts which have the needed command line arguments supplied automatically. But sometimes when debugging our software we developers forget to specify the necessary arguments in Visual Studio's debug options; it's then very annoying to be greeted with the message Config specification invalid -- missing required parameters X, Y, and Z -- shutting down (I'm paraphrasing, of course).
It's not really a big deal, just an annoyance. Still, I felt it worthwhile to throw together a little form to make this process a little less painful; it notifies the user which parameters are missing and allows him/her to specify values for those parameters directly on the form, without having to restart the application.
My intentions were good (I think?), but it seems I can't get this solution to actually work. The problem is that after I've launched our software with missing settings, the form pops up and prompts me as expected; but after I've entered the required parameters and it's time for the application to "really" start, I get this InvalidOperationException:
SetCompatibleTextRenderingDefault must
be called before the first
IWin32Window object is created in the
application.
I think I understand what's going on here: the VB.NET project I'm working on is doing something like this "behind the scenes"*:
Sub Main()
Application.EnableVisualStyles()
Application.SetCompatibleTextRenderingDefault(False)
Application.Run(New MainForm)
End Sub
That call to SetCompatibleTextRenderingDefault is, apparently, throwing an exception because a form was already created and displayed prior to its execution.
Is there any way around this? Is there perhaps a more "proper" solution to this problem that I'm not thinking of (i.e., should I not be trying to collect user input via a form at all)?
*This is a best guess based on what I've seen in C# WinForms projects. Strangely, unless I'm missing something, it seems that VB.NET WinForms projects completely hide this from the developer.
Do make sure that you have the application framework option turned off and Sub Main selected as the starting method. Make it look similar to this:
Sub Main(ByVal args() As String)
Application.EnableVisualStyles()
Application.SetCompatibleTextRenderingDefault(False)
If args.Length = 0 Then
Using dlg As New OptionsDialog
If dlg.ShowDialog <> DialogResult.OK Then Return
'' Use dlg result...
End Using
End If
Application.Run(New MainForm)
End Sub
Perhaps you could use the static Debugger.IsAttached (or even a #DEBUG directive) in your program's "main" function that feeds in some input file (say an XML file) into your parsed args collection instead?
We have an unattended app w/o a user interface that is is periodically run.
It is a VB.NET app. Instead of it being developed as a service, or a formless Windows application, it was developed with a form and all the code was placed in the form_load logic, with an "END" statement as the last line of code to terminate the program.
Other than producing a program that uses unneeded Windows form resources, is there a compelling reason to send this code back for rework to be changed to put the start up logic in a MAIN sub of a BAS file?
If the program is to enter and exit the mix (as opposed to running continuously) is there any point in making it a service?
If the app is developed with a Form do I have to worry about a dialog box being presented that no one will respond to even if there are no MessageBox commands in the app?
I recall there used to be something in VB6 where you could check an app as running unattended, presumably to avoid dialogs.
I don't know whether there are conditions where this will not run.
However, if the code was delivered by someone you will work with going forward, I would look at this as an opportunity to help them understand best practices (which this is not), and to help them understand that you expect best-practice code to be delivered.
First of all, you don't need it to be run in a Form.
Forms are there for Presentation, so it should not be done there.
If you don't want to mess with converting the application a Service (not difficult, but not very easy neither), you shoud create a Console Application, and then, schedule it with Windows Task Scheduler.
This way, you create a Console Application, with a Main function, that does exactly what you need.
Anyway, the programmer could show windows, so there should not be any messagebox. Any communication should be done via Logging to: local files, windows events, database.
If you want more information on any of them, ask me.
If you don't want it to be a service, nothing says that it has to be a windows service. Scheduling it to run via the Task Scheduler or something similar is a valid option.
However, it does sound like the developer should have choose a "Console App" project, instead of a "Windows Forms" project to create this app.
Send it back. The application is bulkier and slower than it needs to be, although that won't be much of an issue. It is somewhat more likely to run out of resources. But the main reason: converting it to a console app is very easy.
If you don't prefer for the Console window to popup, simply do the following.
Create a new class "Program.vb", add a public shared Main() method, and move the "OnLoad" logic from the form to this method.
Next delete the form, and change the project start up object (Available in the project properties window) to use the Program.Main instead of the Form.
This will have the same effect, without the windows forms resources being used. You can then remove the references to System.Windows.Form and System.Drawing.
I am writing a windows service that needs to be running 24/7. It is a pretty simple service that monitors a directory where files are dropped into and processes those files. I need to restart the service if an unhandled exception is thrown.
Is there a way for a service to restart itself in the event of an unhandled exception?
The Services applet has many different recovery features:
It can take different actions on the first, second, and subsequent failures:
Restart the service, after a configurable delay
Run a Program (passing command line parameters, possibly including the failure count)
Restart the Computer (after a configurable delay, and with a particular message being sent)
The program that runs should be able to look in the event log and see the reason for failure (especially if you log it), and should therefore be able to disable the service if the exception is one that is not recoverable.
And, of course, in the meantime, the service should be logging what's going on, which should enable any management tool to notify Operations of what's going on.
I agree that you should probably not configure "third and subsequent" to be "restart service", or you could wind up in a loop.
Have you tried using the Recovery tab of the Service entry - you can set rules for failures, including "Restart the Service" - by default this is on "No Action"
This is able to be done programatically if you wanted, this code was not written by me. I am posting the link to the Authors CodeProject page that contains the source / binaries. Below the link I have explained how I implemented the authors code.
http://www.codeproject.com/KB/install/sercviceinstallerext.aspx
Add a reference to the DLL.
Open ProjectInstaller.Designer.vb in notepad
In the InitializeComponent Sub
CHANGE
Me.ServiceProcessInstaller1 = New System.ServiceProcess.ServiceProcessInstaller
Me.ServiceInstaller1 = New System.ServiceProcess.ServiceInstaller
TO
Me.ServiceProcessInstaller1 = New System.ServiceProcess.ServiceProcessInstaller
Me.ServiceInstaller1 = New Verifide.ServiceUtils.ServiceInstallerEx
With the Friend Declarations in the ProjectInstaller.Designer.vb
CHANGE
Friend WithEvents ServiceProcessInstaller1 As System.ServiceProcess.ServiceProcessInstaller
Friend WithEvents ServiceInstaller1 As System.ServiceProcess.ServiceInstaller
TO
Friend WithEvents ServiceProcessInstaller1 As System.ServiceProcess.ServiceProcessInstaller
Friend WithEvents ServiceInstaller1 As Verifide.ServiceUtils.ServiceInstallerEx
CHANGE
Me.Installers.AddRange(New System.Configuration.Install.Installer() {Me.ServiceProcessInstaller1, Me.ServiceInstaller1})
TO
Me.Installers.AddRange(New System.Configuration.Install.Installer() {Me.ServiceInstaller1, Me.ServiceProcessInstaller1})
Import The Namespace On ProjectInstaller.vb
In ProjectInstaller.vb in the Public Sub New Function After Initialize component function has been called
ADD
'Set Reset Time Count - This Is 4 Days Before Count Is Reset
ServiceInstaller1.FailCountResetTime = 60 * 60 * 24 * 4
'ServiceInstaller1.FailRebootMsg = "Houston! We have a problem"
'Add Failure Actions
ServiceInstaller1.FailureActions.Add(New FailureAction(RecoverAction.Restart, 60000))
ServiceInstaller1.FailureActions.Add(New FailureAction(RecoverAction.Restart, 60000))
ServiceInstaller1.FailureActions.Add(New FailureAction(RecoverAction.None, 3000))
ServiceInstaller1.StartOnInstall = True
Build installer and install. Voila
Wrap your service code in a runner which can catch any errors and restart your service.
The best way is to wrap Try / Catch blocks around the methods in the service you can afford to let throw exceptions.
However, there may be serious exceptions thrown that should result in the service being stopped immediately. Don't ignore these! In these cases, handle the exception, log it, email it and then rethrow it. That way you will be informed that the exception has occurred and will know what went wrong. You can then fix the problem and re-start the service manually.
Just ignoring it could cause a major failure in your system which you would not know about. It could also be very expensive on CPU/RAM if the service stops then restarts then stops ad infinitum.
As suggested by "John Saunders" and "theGecko", you can monitor the service and restart it when it fails. The builtin Windows Service Recovery functionality will get you a long way, but if you find that you need some more advanced features (for example, CPU hogging and hang detection) then please check out Service Protector. It is designed to keep your important Windows Services operating 24x7.
Good luck!