views:

200

answers:

3

Hi all, I have a service written in Delphi 2007 in which I'm trying capture any unknown exceptions. Assigning a method to the on exception doesn't seem to work ('Forms.Application.OnException:=UnknownApplicationException'). The 'UnknownApplicationException' doesn't appear to get called - I attribute this to the fact that there is no form in the application so the method never actually gets assigned. Aside from this, I've also tried creating an exception on a timer (after commenting out 'Forms.Application.OnException:=UnknownApplicationException' so that it does not interfere). The timer triggers 60 seconds after the service has started up:

procedure TProcessScheduler.Timer1Timer(Sender: TObject);    
begin    
  try    
    Raise Exception.Create('THIS GIG SUCKS');    
  except     
    LogEvent(Name,rsUNKNOWN_EXCEPTION,EVENTLOG_AUDIT_FAILURE,0);    
    ExitCode:=-1;    
    Halt;    
  end;     
end;

The exception never seems to get captured - the service starts up and after 60 seconds when this timer triggers, I hear a windows error sound but don't see any error dialog - perhaps this could be due to the fact that the application is a service? The 'Halt' never gets called and the application keeps running (i assume its waiting for someone to click ok on the invisible error dialog that it created). Any ideas why the code under the 'except' doesn't get called? Thanks in advance! KP

+2  A: 

A couple of notes:

  • As you are raising an exception within a try-except block, it should not trigger any Application.OnException handler, simply because the exception isn't unhandled.

  • How have you determined that the Halt doesn't get called? Does the exception get logged through your LogEvent?

  • In a Service application ExitCode and Halt don't function the way you would expect them to in a normal windows application. A service isn't stopped by calling halt, it should be stopped by going through the Windows' Service Control Manager.

  • If the except part of your try-except block is indeed not reached, it means that Windows has cut in because something has happened that it isn't happy with. That could be something in the LogEvent method you are calling. If that shows a dialog or if that raises an exception as well, the ExitCode and Halt won't be reached.

  • A service doesn't normally have a desktop associated with it, so showing dialogs isn't going to work.

  • If you need the service to show dialogs (bad idea by the way, services are intended to run without user interaction), you need to make it interactive and have it run under another user account than the normal "system" account that services run under. You do this through the services manager.

Marjan Venema
off: " A service doesn't normally have a desktop associated with it, so showing dialogs isn't going to work. "- isn't going to work, and you'll probably need to restart the system because the service gets stucked trying to show that window. very bad idea. Instead of using logevent function, why do you not write in a text file or send an email to see if the exception is catch? Best regards, Radu
Radu Barbu
+6  A: 

Reassigning Forms.Application.OnException is a bad idea, as TServiceApplication.Run() does this itself. You either do it before, then your assignment won't have an effect, or you do it afterwards, in which case you remove the exception handling mechanism that has been put into place.

If you leave the handling in place, then all exceptions will be logged to the Windows Event Logger, which seems a reasonable thing to do from a service.

mghie
Thanks for that, I removed the 'Forms.Application.OnException:=UnknownApplicationException' - and without this I can see that the exceptions are now automatically being logged into the Windows Event Viewer - I'd still like to be able to programmatically terminate the service - any idea how this can be done? Halt doesnt seem to work and as pointed out by someone else it's not really the best way to do it, thanks
Kunal
@Kunal: You can stop a service by requesting a new status, but to get a detailed explanation you should better ask a new question on Stack Overflow. Others can then benefit from the more focused Q and A.
mghie
both TServiceApplication and TService do internal exception handling that log uncaught exceptions to the Windows Event Log. So it is very rare for an exception to even reach the OnException event to begin with. But if you want to react to an uncaught exception, then try driving a new class from TServiceApplication and override its DoHandleException() method.
Remy Lebeau - TeamB
@Remy: I don't see how this could work, since `TServiceApplication.Create(nil)` is executed during `initialization` of *SvcMgr.pas*. Hooking `TServiceApplication.DoHandleException()` looks like the only workable solution, but that's an implementation detail and may change in a later VCL versions.
mghie
You can free the default TServiceApplication object and replace it with your own, ie: Application.Free; Application := TMyServiceApplication.Create(nil);
Remy Lebeau - TeamB
+1  A: 

Why are you setting Forms.Application? AFAIK a service uses the Application variable declared in SvcMgr, which is declared as:

var
  Application: TServiceApplication = nil;

Moreover a service should not display any dialog, it may not have access to the user desktop, and your dialog will hang the service. There are ways to display a dialog anyway, but services could also run when no human user is watching the screen. Log events to the event log (or if you don't like it to a file, but the event log has several useful features, including remote access).

ldsandon