views:

121

answers:

4

I have a dependency on .NET 2.0 SP2 in my ClickOnce deployed application (the ApplicationDeployment.CurrentDeployment.CheckForDetailedUpdate(false) method is SP2 only).

I would like to check whether SP2 is present during app startup. I have tried to detect this by catching MissingMethodException after calling a SP2-only method.

    /// <summary>
    /// The SP2 bootstrapper does not allow HomeSite installation
    /// http://msdn.microsoft.com/en-us/vstudio/bb898654.aspx
    /// So we only advice the user to download .NET 2.0 SP2 manually.
    /// </summary>
    private void CheckDotNet2SP()
    {
        WaitHandle wh = new AutoResetEvent(true);
        try
        {
            wh.WaitOne(1); //this method is .NET 2.0 SP2 only
        }
        //NOTE: this catch does not catch the MissingMethodException
        catch (Exception) //change to catch(MissingMethodException) does not help
        {
            //report that .NET 2.0 SP2 is missing
        }
        finally
        {
            wh.Close();
        }
    }

The code in catch never executes when this runs on .NET 2.0 without SP2. The exception is only caught by the AppDomain.CurrentDomain.UnhandledException event handler.

How is it possible that the MissingMethodException is not caught? I can imagine that this is a special case - the CLR hits a method that does not exist and somehow it is not possible to pass this to the catch block. I would like to understand the principle behind this.

Anyone has any resources on this issue? Are there any other exceptions that cannot be caught in a catch block?

+5  A: 

I suspect it's happening at JIT time, before the method is even properly entered - i.e. before your catch block is hit. It's possible that if you catch MissingMethodException in the calling method, that will sort it out... particularly if you decorate CheckDotNet2SP with MethodImpl[MethodImplOptions.NoInlining]. It still sounds like it would be pretty dicey though.

You could always check for the presence of the method with reflection rather than by trying to call it though.

Jon Skeet
+2  A: 

There are a few exceptions that have been defined as "unrecoverable". One of them is MissingMethodException, because if a method is missing in a class, this is a severe error and it requires unloading the class and reloading a new class to recover, which cannot be done trivially (if at all).

To recover, you need to reinstall, check the versions of the assemblies, check whether the PE images are valid etc.

If all you want to know is whether SP2 is installed, the default method is using a bootstrap application which simply checks the installed version. If all's fine, it runs the application, if not it shows a nice message.


Update requested by OP:
Other exceptions that are either hard to catch or uncatchable (may depend on your version of .NET, i.e., .NET 4.0 added more uncatchables): OutOfMemoryException (can be caught when it is synchronous), StackOverflowException (can never be caught), ThreadAbortException (can be caught, but is special because it will automatically be reraised at the end of the catch-block), BadImageFormatException and MissingManifestResourceException if you'd try to catch it in the assembly throwing the exception (if you load it dynamically, same as with MissingMethodException, you are capable of catching it). And in general, any exception that does not inherit from Exception is hard to catch (but you can catch them with a generic try/catch block).

There are others, but the first three above are the ones you'll encounter most often in practice.

Abel
could you please list other 'unrecoverable' exception types?
Marek
@Marek: done, I updated the answer.
Abel
+2  A: 

Exception is thrown on JIT compilation step, so you didn't step into method. Try this version:

    private bool CheckDotNet2SP()
    {
        try
        {
            CheckImpl();
            return true;
        }
        catch (MissingMethodException)
        {
            return false;
        }
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    private void CheckImpl()
    {
        using (var wh = new ManualResetEvent(true))
            wh.WaitOne(1);
    }
desco
Afaict, this method will still unload the application upon error as it is considered unrecoverable.
Abel
+3  A: 

You could use reflection to see whether the method exists.

private void CheckDotNet2SP()
{
    return typeof(WaitHandle).GetMethod("WaitOne", new Type[] { typeof(int) }) 
       != null;
} 
0xA3