为什么无法捕获MissingMethodException?

11

我在我的ClickOnce部署应用程序中依赖于.NET 2.0 SP2 (方法ApplicationDeployment.CurrentDeployment.CheckForDetailedUpdate(false)仅限SP2)。

我想在应用程序启动时检查是否存在SP2。我尝试通过捕获调用仅适用于SP2的方法后的MissingMethodException来检测这一点。

    /// <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();
        }
    }

如果在没有安装.NET 2.0 SP2的情况下运行此代码,则catch中的代码不会执行。异常只能通过AppDomain.CurrentDomain.UnhandledException事件处理程序来捕获。

为什么MissingMethodException无法被catch捕获?我可以想象这是一个特殊情况 - CLR遇到了不存在的方法,并且以某种方式无法将其传递到catch块。我想了解背后的原理。

有人对这个问题有任何资源吗?是否还有其他无法在catch块中捕获的异常?

4个回答

15

我怀疑这是在JIT时间发生的,甚至在方法正式进入之前 - 也就是在捕获块被命中之前。如果你在调用方法中捕获MissingMethodException,那么这可能会解决问题...特别是如果你用MethodImpl[MethodImplOptions.NoInlining]来修饰CheckDotNet2SP 。尽管如此,它仍然听起来很冒险。

你可以通过反射检查方法的存在性,而不需要尝试调用它。


13
有一些异常被定义为“无法恢复”。其中之一是 MissingMethodException,因为如果一个类中缺少了方法,这是一个严重的错误,需要卸载该类并重新加载一个新类来恢复,这不可能是轻而易举的(如果有可能的话)。
要进行恢复,你需要重新安装,检查程序集的版本,检查PE图像是否有效等。
如果你只想知道是否安装了SP2,则默认方法是使用引导应用程序来检查已安装的版本。如果一切正常,它将运行应用程序,否则会显示一个不错的消息。
OP请求的更新:
其他难以捕获或无法捕获的异常(可能取决于你的.NET版本,即.NET 4.0添加了更多无法捕获的异常):OutOfMemoryException(在同步时可以捕获),StackOverflowException(永远无法捕获),ThreadAbortException(可以捕获,但是很特殊,因为它会自动重新引发在catch块的末尾),BadImageFormatExceptionMissingManifestResourceException(如果你尝试在抛出异常的程序集中捕获它们,与MissingMethodException一样,你就能够捕获它们)。而且,任何未继承自Exception的异常通常都很难捕获(但你可以使用通用try/catch块来捕获它们)。
还有其他异常,但前面提到的三个是你在实践中最常遇到的。

请列出其他“不可恢复”的异常类型,好吗? - Marek
1
你有这些“不可恢复”的异常的参考资料吗?我看到在Application.DispatcherUnhandledException中提到了它,但我找不到明确的信息。 - Oliver Bock

4

异常是在JIT编译步骤中抛出的,因此您没有进入方法。 请尝试使用此版本:

    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);
    }

据我所知,由于被视为无法恢复,因此该方法仍将在错误发生时卸载应用程序。 - Abel
我测试并验证了这个答案在 .NET Core 3.1 中正确工作。 - Chris Gillum

3
您可以使用反射来查看方法是否存在。
private void CheckDotNet2SP()
{
    return typeof(WaitHandle).GetMethod("WaitOne", new Type[] { typeof(int) }) 
       != null;
} 

请注意,如果异常实际上是由JIT编译器抛出的,则此方法将无法正常工作。只有在通过反射调用方法时才能使用它。 - Abel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接