Hooking UnhandledException in .NET module invoked in classic ASP

  • Thread starter Thread starter Guest
  • Start date Start date
G

Guest

I posted this question in microsoft.public.dotnet.framework.interop but got
zero replies in 5 days so I'm reposting in the general framework group hoping
someone's got some insight:

We have a large IIS/SQL classic ASP site that we are converting to .NET by
abstracting functionality into .NET components/methods which are also
installed/registered as COM objects so they can by invoked by new ASPX pages
as well as by our current ASP pages (Set xxx = Server.CreateObject)

My question is how do I hook a handler into the UnhandledException chain for
my ASP page invoked components via (Set ThisObj =
Server.CreateObject("MyLib:MyObj")?

The section of my code where I hook the chain is:

If System.Web.HttpContext.Current Is Nothing Then
'Instantiated in the classic ASP environment
' ??? What goes here ???
Else
'Instantiated in the ASPX environment
Dim HttpApplication As System.Web.HttpApplication =
System.Web.HttpContext.Current.ApplicationInstance
AddHandler HttpApplication.Error, AddressOf HandleHttpExceptions
' This works fine to catch exceptions in the .NET assembly on
ASPX pages
End If

I've tried the standard console application hooking:

Dim CurrentDomain As AppDomain = System.AppDomain.CurrentDomain
AddHandler CurrentDomain.UnhandledException, AddressOf
HandleUnhandledExceptions

but that doesn't seem to work across the interop/COM boundaries.

Anybody got any light to shed on where the UnhandledException event chain
lives when the .NET assembly is registered as a COM object and invoked by
IIS's object structure?
 
John Snook said:
I posted this question in microsoft.public.dotnet.framework.interop but got
zero replies in 5 days so I'm reposting in the general framework group
hoping
someone's got some insight:

We have a large IIS/SQL classic ASP site that we are converting to .NET by
abstracting functionality into .NET components/methods which are also
installed/registered as COM objects so they can by invoked by new ASPX
pages
as well as by our current ASP pages (Set xxx = Server.CreateObject)

My question is how do I hook a handler into the UnhandledException chain
for
my ASP page invoked components via (Set ThisObj =
Server.CreateObject("MyLib:MyObj")?

The section of my code where I hook the chain is:

If System.Web.HttpContext.Current Is Nothing Then
'Instantiated in the classic ASP environment
' ??? What goes here ???
Else
'Instantiated in the ASPX environment
Dim HttpApplication As System.Web.HttpApplication =
System.Web.HttpContext.Current.ApplicationInstance
AddHandler HttpApplication.Error, AddressOf
HandleHttpExceptions
' This works fine to catch exceptions in the .NET assembly on
ASPX pages
End If

I've tried the standard console application hooking:

Dim CurrentDomain As AppDomain = System.AppDomain.CurrentDomain
AddHandler CurrentDomain.UnhandledException, AddressOf
HandleUnhandledExceptions

but that doesn't seem to work across the interop/COM boundaries.

Anybody got any light to shed on where the UnhandledException event chain
lives when the .NET assembly is registered as a COM object and invoked by
IIS's object structure?


I beleive that the managed exception is "handled" by the CCW. The exception
is caught and and a COM error is passed to COM. So the error propagates out
to the COM client and it is not unhandled. It's up to the COM client to do
something useful with the error then.

David
 
Hi David,

Many thanks for the response (I was getting a little lonely with no
replies!) and yes, you're right - that's exactly what happens because the CCW
is hooked into the propagation chain of the exception. What I want to do is
hook into that chain in my assembly ("nearer" to the exception) save some
information about the exception and then let it propagate on to the CCW and
thereafter COM and thereafter IIS (where we use a user friendly HTTP 500 ASP
page)

I can't do that with
System.Web.HttpContext.Current.ApplicationInstance.UnhandledException event
(as you would with an ASPX page) and
System.AppDomain.CurrentDomain.UnhandledException event (as you would with a
console app executable)

What "ApplicationDomain" is my assembly running under in the
IIS->ASP->COM->CCW->MyAssembly world (or at least how do I reference it) ??
But your use of the CCW keyword has triggered a new line of Googling ...

Rgds, JS
 
John Snook said:
Hi David,

Many thanks for the response (I was getting a little lonely with no
replies!) and yes, you're right - that's exactly what happens because the
CCW
is hooked into the propagation chain of the exception. What I want to do
is
hook into that chain in my assembly ("nearer" to the exception) save some
information about the exception and then let it propagate on to the CCW
and
thereafter COM and thereafter IIS (where we use a user friendly HTTP 500
ASP
page)

I can't do that with
System.Web.HttpContext.Current.ApplicationInstance.UnhandledException
event
(as you would with an ASPX page) and
System.AppDomain.CurrentDomain.UnhandledException event (as you would with
a
console app executable)

What "ApplicationDomain" is my assembly running under in the
IIS->ASP->COM->CCW->MyAssembly world (or at least how do I reference it)
??
But your use of the CCW keyword has triggered a new line of Googling ...

In the past I have structured every method in my COM visible class like:

void foo()
{
try
{
//method body
}
catch (Exception ex)
{
//log error and do whatever
throw;
}
}

David
 
Hi David,

Yes, thats an approach. But in this conversion project we'll be implementing
perhaps two hundred methods (although only, say half, of those will require
instantiation in IIS as COM objects) so a try/catch/finally on every method
and property gets pretty tedious (to create AND read).

We've got plenty of try/catch blocks where we "expect" (???) errors such as
foreign key constraints or bad data submitted but for the "acts of God"
errors (somebody killed the SQL cluster or chopped through a network link) we
just want to log some key info before the error disappears as a HTTP500 error
on an ASP page. Isn't that what structured error handling and
UnhandledException events were invented for?

Later ... JS
 
John Snook said:
Hi David,

Yes, thats an approach. But in this conversion project we'll be
implementing
perhaps two hundred methods (although only, say half, of those will
require
instantiation in IIS as COM objects) so a try/catch/finally on every
method
and property gets pretty tedious (to create AND read).

Your COM-visible classes are lame anyway. They can't have meaningful
constructors or have static methods, etc. Treat them as adapter classes in
your design and expect them to be full of tedius marshaling stuff. Put your
implementation in better-designed, non-COM visible objects.
We've got plenty of try/catch blocks where we "expect" (???) errors such
as
foreign key constraints or bad data submitted but for the "acts of God"
errors (somebody killed the SQL cluster or chopped through a network link)
we
just want to log some key info before the error disappears as a HTTP500
error
on an ASP page. Isn't that what structured error handling and
UnhandledException events were invented for?

Yes. But you are handing the error information to classic asp, which
doesn't support structured error handling.

So you should probably capture the error info before you let enter the black
hole of script that is old ASP.

On a more general level, you shold probably trap and log all errors as they
pass through major system boundries.

David
 
David Browne said:
Your COM-visible classes are lame anyway. They can't have meaningful
constructors or have static methods, etc. Treat them as adapter classes in
your design and expect them to be full of tedius marshaling stuff. Put your
implementation in better-designed, non-COM visible objects.

Yes. But you are handing the error information to classic asp, which
doesn't support structured error handling.

So you should probably capture the error info before you let enter the black
hole of script that is old ASP.

On a more general level, you shold probably trap and log all errors as they
pass through major system boundries.

David
Hi David

I don't disagree and would love to scrap everything and start with a clean
(non-COM visible) sheet of paper but with tens of thousands of lines of
linear, interpretative, procedural ASP code in a two tier system getting
several million hits a day our chosen path is progressive abstraction into a
three tier design until the ASP/COM side just "goes away". Alas I can't scrap
it and start again. And yes, there's an awful lot of marshalling but I let
..NET worry about that.

I don't mind the error propagating into the unstructured black hole of ASP
(where it's covered up by a user friendly page anyway) so long as I get to
"capture the error info before" and log it. Do you (or anybody) know how to
do that (short of a try/catch around each method) when the assembly is
running inside of a CCW inside of a dllhost.exe inside of inetinfo.exe ? If
nobody does know how to hook into that unhandled exception chain then I
probably will have to code try/catch around the methods.

Bear in mind we're not trying to handle the "standard" errors (we already do
that) but grab meaningful forensic info on the rarer errors. For example a
recent rebuild of one of the 10 web servers left the "recycle worker threads"
option in place on IIS so that about every 2 weeks one page hit failed. But
it failed semi-silently making it difficult to know that it had happened let
alone find out why.

Later ... JS
 
Back
Top