Why is it not possible to catch MissingMethodExcep

2019-02-06 14:23发布

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?

4条回答
三岁会撩人
2楼-- · 2019-02-06 14:59

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.

查看更多
倾城 Initia
3楼-- · 2019-02-06 15:05

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);
    }
查看更多
地球回转人心会变
4楼-- · 2019-02-06 15:16

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.

查看更多
贼婆χ
5楼-- · 2019-02-06 15:18

You could use reflection to see whether the method exists.

private void CheckDotNet2SP()
{
    return typeof(WaitHandle).GetMethod("WaitOne", new Type[] { typeof(int) }) 
       != null;
} 
查看更多
登录 后发表回答