Error handling in Windows Service

S

SetonSoftware

I'm using .NET 3.5 SP1 to create a Windows service. The service
accesses a web site and pulls down fax images, processes them, and
places them in a SQL Server database. Because it is a multi-step
process, I have try..catch blocks around the different areas of the
code so when a problem occurs I can log it and the service can send me
an email.

The problem is that it errors never seem to be entering the catch
block. If an error occurs I'll get an entry like this in the Event
Log:

EventType clr20r3, P1 faxmanagerservice.exe, P2 1.0.3380.18713, P3
49d62a02, P4 mscorlib, P5 2.0.0.0, P6 471ebc5b, P7 416b, P8 27, P9
2m52exv2c4qv0uevrpjp1p2vvubsmive, P10 NIL.

After that the service just stops - no log entry, no email.

What could I be missing here?

Thanks

Carl
 
J

Jeroen Mostert

SetonSoftware said:
I'm using .NET 3.5 SP1 to create a Windows service. The service
accesses a web site and pulls down fax images, processes them, and
places them in a SQL Server database. Because it is a multi-step
process, I have try..catch blocks around the different areas of the
code so when a problem occurs I can log it and the service can send me
an email.

The problem is that it errors never seem to be entering the catch
block. If an error occurs I'll get an entry like this in the Event
Log:

EventType clr20r3, P1 faxmanagerservice.exe, P2 1.0.3380.18713, P3
49d62a02, P4 mscorlib, P5 2.0.0.0, P6 471ebc5b, P7 416b, P8 27, P9
2m52exv2c4qv0uevrpjp1p2vvubsmive, P10 NIL.

After that the service just stops - no log entry, no email.

What could I be missing here?
For one thing, the actual code. Not being psychic, all I can tell you is
that you have an exception somewhere that has no handler. One common cause
of surprises is code that executes on a separate thread (either explicitly
or implicitly as a delegate called for asynchronous processing). Exceptions
do not propagate across threads -- if a thread has no top-level exception
handler, the exception goes unhandled.

There are two things you can do that will make your life a lot easier.

First, on service start, assign a handler to AppDomain.UnhandledException
event and have that log something more meaningful than the framework's
fall-back logging, which is nigh useless.

Second, modify your service so it can run as a regular console application,
so you can debug it. To do this, change the .Main() method to read something
like this:

if (Environment.UserInteractive) {
// Not a service
using (MyService s = new MyService()) {
s.OnStart();
Console.ReadLine();
s.OnStop();
}
} else {
// Service
ServiceBase.Run(new MyService());
}
 
M

Mr. Arnold

SetonSoftware said:
I'm using .NET 3.5 SP1 to create a Windows service. The service
accesses a web site and pulls down fax images, processes them, and
places them in a SQL Server database. Because it is a multi-step
process, I have try..catch blocks around the different areas of the
code so when a problem occurs I can log it and the service can send me
an email.

The problem is that it errors never seem to be entering the catch
block. If an error occurs I'll get an entry like this in the Event
Log:

EventType clr20r3, P1 faxmanagerservice.exe, P2 1.0.3380.18713, P3
49d62a02, P4 mscorlib, P5 2.0.0.0, P6 471ebc5b, P7 416b, P8 27, P9
2m52exv2c4qv0uevrpjp1p2vvubsmive, P10 NIL.

After that the service just stops - no log entry, no email.

What could I be missing here?

Why can't you use a Try/catch (Exception ex) and catch everything? You can
have multiple catches in a catch. You can place the (Exception ex) catch as
the last catch of a multiple catch, because if it makes it that far, all
proceeding catches were missed.

And if it just stops, then it aborted out of the service.
 
P

Paul

This is simple.

Never put code other than that for scheduling and calling BLL in a windows
service. If this means creating a SOAP BLL then so be it. If the code lies
outside your Service it will not make your service stop if an unhandled
error occurs from any called code unless you so wish and is more easily
tested.

I'm confused as to why any service would be using a Web App as a supprting
actor, so I suggest you are using a service as more than just an event for
firing a Syetem generated 'Use Case' / 'Workflow Action'.
 
M

Mr. Arnold

Paul said:
This is simple.

Never put code other than that for scheduling and calling BLL in a windows
service. If this means creating a SOAP BLL then so be it. If the code lies
outside your Service it will not make your service stop if an unhandled
error occurs from any called code unless you so wish and is more easily
tested.

I'm confused as to why any service would be using a Web App as a supprting
actor, so I suggest you are using a service as more than just an event for
firing a Syetem generated 'Use Case' / 'Workflow Action'.

Why can't you make a direct call to a Web service from a Console, Windows
Service or Windows form application, if that is what is called for? There is
no need to use a BLL in a solution unless the solution calls for it.

As an example, why can't you use an ASP.NET Data Service, ADO.NET Entity
Framework solution without that call being made from a BLL or use a WCF Web
Service using the ADO.NET EF or any other data access method? That's the
whole purpose of .NET is code reuse and the ability to use Internet or
Intranet Web centric solutions. It's called SOA where the service can be
called from any type of client.
 
C

Carl Ganz

For one thing, the actual code. Not being psychic, all I can tell you is
that you have an exception somewhere that has no handler. One common cause
of surprises is code that executes on a separate thread (either explicitly
or implicitly as a delegate called for asynchronous processing). Exceptions
do not propagate across threads -- if a thread has no top-level exception
handler, the exception goes unhandled.

There are two things you can do that will make your life a lot easier.

First, on service start, assign a handler to AppDomain.UnhandledException
event and have that log something more meaningful than the framework's
fall-back logging, which is nigh useless.

Second, modify your service so it can run as a regular console application,
so you can debug it. To do this, change the .Main() method to read something
like this:

   if (Environment.UserInteractive) {
     // Not a service
     using (MyService s = new MyService()) {
       s.OnStart();
       Console.ReadLine();
       s.OnStop();
     }
   } else {
     // Service
     ServiceBase.Run(new MyService());
   }

<<Exceptions do not propagate across threads -- if a thread has no
top-level exception
handler, the exception goes unhandled.
Damn, damn, damn! That was it! It's been a while since I spawned a
thread and I forgot this important fact.

Many thanks to everyone for their help!!!

Carl
 
C

Carl Ganz

For one thing, the actual code. Not being psychic, all I can tell you is
that you have an exception somewhere that has no handler. One common cause
of surprises is code that executes on a separate thread (either explicitly
or implicitly as a delegate called for asynchronous processing). Exceptions
do not propagate across threads -- if a thread has no top-level exception
handler, the exception goes unhandled.

There are two things you can do that will make your life a lot easier.

First, on service start, assign a handler to AppDomain.UnhandledException
event and have that log something more meaningful than the framework's
fall-back logging, which is nigh useless.

Second, modify your service so it can run as a regular console application,
so you can debug it. To do this, change the .Main() method to read something
like this:

   if (Environment.UserInteractive) {
     // Not a service
     using (MyService s = new MyService()) {
       s.OnStart();
       Console.ReadLine();
       s.OnStop();
     }
   } else {
     // Service
     ServiceBase.Run(new MyService());
   }

Fair enough - here's the code:

The OnStart method:

protected override void OnStart(string[] args)
{
string szMsg;

FaxManagerServiceMain oFaxManagerServiceMain;

oFaxManagerServiceMain = new FaxManagerServiceMain();

Thread oThread = new Thread(new ThreadStart
(oFaxManagerServiceMain.Run));

oThread.Start();

szMsg = "The Fax Manager Service was started at " +
DateTime.Now.ToString();

System.Diagnostics.EventLog.WriteEntry(this.ServiceName, szMsg);
}


invokes the main Run method here:


public class FaxManagerServiceMain
{
public void Run()
{
FaxManager.FaxManager oFaxManager = new FaxManager.FaxManager
();
short sFrequencyInMinutes;

sFrequencyInMinutes = short.Parse
(ConfigurationSettings.AppSettings["MinutesBetweenFaxChecks"]);

while (true)
{
oFaxManager.GetFax();

//Get the settings from the app.config file
sFrequencyInMinutes = short.Parse
(ConfigurationSettings.AppSettings["MinutesBetweenFaxChecks"]);

//Once the faxes have been checked,
//sit tight until its time to check again
System.Threading.Thread.Sleep(sFrequencyInMinutes * 60 *
1000);
}

}

}

The oFaxManager.GetFax() method is in a DLL which performs a multi-
step process of obtaining faxes from a server. I have try...catch
blocks around all the main areas of the code so if an error occurs I
can write the information about it to the log and an emil will go to
me informing me of the problem. Here is a sample of one of the
try...catch blocks:

try
{
aFiles = Directory.GetFiles(szInboundDirectory, szFilePattern);

oTiffUtil = new InnovatixCommon.TiffUtil(@"c:\temp");

foreach (string szFullFileName in aFiles)
{
szFileName = szFullFileName.Substring
(szFullFileName.LastIndexOf("\\") + 1);

oTiffUtil.splitTiffPages(szFullFileName, szInboundDirectory);

if (System.IO.File.Exists(szInboundHistoryDirectory +
szFileName))
System.IO.File.Delete(szInboundHistoryDirectory +
szFileName);

System.IO.File.Move(szFullFileName, szInboundHistoryDirectory
+ szFileName);

Common.WriteLog(szLogFile, "Tif main file moved to " +
szInboundHistoryDirectory + szFileName);
}
}
catch (Exception ex)
{
string szError = "Error splitting Tif into separate pages: " +
ex.Message + " " + ex.InnerException.Message;
Common.WriteLog(szLogFile, szError);
SendEmail(szError);
return;
}

The problem is that when an error occurs it doesn't enter the catch
block. Though you say that errors don't propogate across thread, this
problem occurred within the existing thread. What gives?

I appreciate your help.

Thanks

Carl
 
J

Jeroen Mostert

Carl said:
SetonSoftware wrote: [snip]
There are two things you can do that will make your life a lot easier.

First, on service start, assign a handler to AppDomain.UnhandledException
event and have that log something more meaningful than the framework's
fall-back logging, which is nigh useless.

Second, modify your service so it can run as a regular console application,
so you can debug it. To do this, change the .Main() method to read something
like this:
[snip]
Did you do this? It helps. Now we have code but no exception. Without a
stack trace, you can pretty much forget about finding out the cause.
try
{
aFiles = Directory.GetFiles(szInboundDirectory, szFilePattern);

oTiffUtil = new InnovatixCommon.TiffUtil(@"c:\temp");

foreach (string szFullFileName in aFiles)
{
szFileName = szFullFileName.Substring
(szFullFileName.LastIndexOf("\\") + 1);

oTiffUtil.splitTiffPages(szFullFileName, szInboundDirectory);

if (System.IO.File.Exists(szInboundHistoryDirectory +
szFileName))
System.IO.File.Delete(szInboundHistoryDirectory +
szFileName);

System.IO.File.Move(szFullFileName, szInboundHistoryDirectory
+ szFileName);

Common.WriteLog(szLogFile, "Tif main file moved to " +
szInboundHistoryDirectory + szFileName);
}
}
catch (Exception ex)
{
string szError = "Error splitting Tif into separate pages: " +
ex.Message + " " + ex.InnerException.Message;
Common.WriteLog(szLogFile, szError);
SendEmail(szError);
return;
}

The problem is that when an error occurs it doesn't enter the catch
block. Though you say that errors don't propogate across thread, this
problem occurred within the existing thread. What gives?
First of all, either an exception is occurring on the current thread and
then it *must* enter the catch block, or the exception is actually occurring
on a different thread despite your assertion (maybe one that you cannot see
being spawned explicitly, for example from unmanaged code), or you've run
into an internal framework error (these bypass all managed error handling
mechanisms). The latter are easily recognized in the event viewer by the
words "fatal execution engine error", and they're hopefully not too common.
Distinguishing between the other two possibilities is easy, if you have a
stack trace.

By the way, is it really true that Common.WriteLog() and SendEmail() can
never fail? If it can fail, your exception handler is itself susceptible to
exceptions.

Oh, and also, *never catch Exception*. Use a real error handling strategy
instead. Many people have commented on this;
http://blogs.msdn.com/fxcop/archive/2006/06/14/631923.aspx is a particularly
good explanation.
 
C

Carl Ganz

Carl said:
SetonSoftware wrote: [snip]
There are two things you can do that will make your life a lot easier.
First, on service start, assign a handler to AppDomain.UnhandledException
event and have that log something more meaningful than the framework's
fall-back logging, which is nigh useless.
Second, modify your service so it can run as a regular console application,
so you can debug it. To do this, change the .Main() method to read something
like this:

[snip]
Did you do this? It helps. Now we have code but no exception. Without a
stack trace, you can pretty much forget about finding out the cause.




try
{
    aFiles = Directory.GetFiles(szInboundDirectory, szFilePattern);
    oTiffUtil = new InnovatixCommon.TiffUtil(@"c:\temp");
    foreach (string szFullFileName in aFiles)
    {
        szFileName = szFullFileName.Substring
(szFullFileName.LastIndexOf("\\") + 1);
        oTiffUtil.splitTiffPages(szFullFileName, szInboundDirectory);
        if (System.IO.File.Exists(szInboundHistoryDirectory +
szFileName))
            System.IO.File.Delete(szInboundHistoryDirectory+
szFileName);
        System.IO.File.Move(szFullFileName, szInboundHistoryDirectory
+ szFileName);
        Common.WriteLog(szLogFile, "Tif main file moved to " +
szInboundHistoryDirectory + szFileName);
    }
}
catch (Exception ex)
{
    string szError =  "Error splitting Tif into separate pages:" +
ex.Message + " " + ex.InnerException.Message;
    Common.WriteLog(szLogFile, szError);
    SendEmail(szError);
    return;
}
The problem is that when an error occurs it doesn't enter the catch
block. Though you say that errors don't propogate across thread, this
problem occurred within the existing thread. What gives?

First of all, either an exception is occurring on the current thread and
then it *must* enter the catch block, or the exception is actually occurring
on a different thread despite your assertion (maybe one that you cannot see
being spawned explicitly, for example from unmanaged code), or you've run
into an internal framework error (these bypass all managed error handling
mechanisms). The latter are easily recognized in the event viewer by the
words "fatal execution engine error", and they're hopefully not too common.
Distinguishing between the other two possibilities is easy, if you have a
stack trace.

By the way, is it really true that Common.WriteLog() and SendEmail() can
never fail? If it can fail, your exception handler is itself susceptible to
exceptions.

Oh, and also, *never catch Exception*. Use a real error handling strategy
instead. Many people have commented on this;http://blogs.msdn.com/fxcop/archive/2006/06/14/631923.aspxis a particularly
good explanation.

Many thanks fior the feedback:

Sorry, I showed you the original code, not the one with the error
handler you suggested. Here it is:

protected override void OnStart(string[] args)
{
string szMsg;

AppDomain.CurrentDomain.UnhandledException += new
UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

FaxManagerServiceMain oFaxManagerServiceMain;

oFaxManagerServiceMain = new FaxManagerServiceMain();

Thread oThread = new Thread(new ThreadStart
(oFaxManagerServiceMain.Run));

oThread.Start();
}

private void CurrentDomain_UnhandledException(object sender,
UnhandledExceptionEventArgs e)
{
string szMessage = ((Exception)e.ExceptionObject).Message;

EventLog.CreateEventSource(this.ServiceName, szMessage);
}


Here is the Run method of the Fax class

public class FaxManagerServiceMain
{
public void Run()
{
FaxManager.FaxManager oFaxManager = new FaxManager.FaxManager
();
short sFrequencyInMinutes;

sFrequencyInMinutes = short.Parse
(ConfigurationSettings.AppSettings["MinutesBetweenFaxChecks"]);

while (true)
{
oFaxManager.GetFax();

//Get the settings from the app.config file
sFrequencyInMinutes = short.Parse
(ConfigurationSettings.AppSettings["MinutesBetweenFaxChecks"]);

//Once the faxes have been checked,
//sit tight until its time to check again
System.Threading.Thread.Sleep(sFrequencyInMinutes * 60 *
1000);
}

}

}

I modified oFaxManager.GetFax() to throw a DivideByZeroException in
the opening line. This error never triggers the
CurrentDomain_UnhandledException event. What could be wrong here?

I appreciate what you are saying about not capturing generic Exception
objects. I'm not debugging the web service to find a specific problem
as the Windows service works quite well. I'm simply adding error
handling for unanticipated problems. I went with Exception as I simply
need to record the stack trace.

You're right anout WriteLog and SendEmail. I'll handle the expcetions
for these once I can get the main error handling working.

Thanks

Carl
 
J

Jeroen Mostert

Carl Ganz wrote:
[snip]
protected override void OnStart(string[] args)
{
string szMsg;

AppDomain.CurrentDomain.UnhandledException += new
UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

FaxManagerServiceMain oFaxManagerServiceMain;

oFaxManagerServiceMain = new FaxManagerServiceMain();

Thread oThread = new Thread(new ThreadStart
(oFaxManagerServiceMain.Run));

oThread.Start();
}

private void CurrentDomain_UnhandledException(object sender,
UnhandledExceptionEventArgs e)
{
string szMessage = ((Exception)e.ExceptionObject).Message;

EventLog.CreateEventSource(this.ServiceName, szMessage);
}


Here is the Run method of the Fax class

public class FaxManagerServiceMain
{
public void Run()
{
FaxManager.FaxManager oFaxManager = new FaxManager.FaxManager
();
short sFrequencyInMinutes;

sFrequencyInMinutes = short.Parse
(ConfigurationSettings.AppSettings["MinutesBetweenFaxChecks"]);

while (true)
{
oFaxManager.GetFax();

//Get the settings from the app.config file
sFrequencyInMinutes = short.Parse
(ConfigurationSettings.AppSettings["MinutesBetweenFaxChecks"]);

//Once the faxes have been checked,
//sit tight until its time to check again
System.Threading.Thread.Sleep(sFrequencyInMinutes * 60 *
1000);
}

}

}

I modified oFaxManager.GetFax() to throw a DivideByZeroException in
the opening line. This error never triggers the
CurrentDomain_UnhandledException event. What could be wrong here?
Do you know the handler is never triggered, or do you infer this from no
message appearing in the event log? The latter can, of course, fail.

Assuming the method is indeed never called, this would mean the exception is
triggered from the FaxManager constructor, and if it doesn't trip the
AppDomain handler, this would in turn mean it's an exception occurring in an
unmanaged thread -- but this isn't consistent with the events you get in the
event log, so my fallback theory is that your service fails before the event
handler can even be attached. Does it run properly as a console application?

The only way to properly diagnose unmanaged exceptions (if it does turn out
to have something to do with them) is to attach a debugger that can do
unmanaged debugging (either Visual Studio with unmanaged debugging enabled
or something like WinDbg).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top