Почему невозможно поймать MissingMethodException?

У меня есть зависимость от .NET 2.0 SP2 в моем развернутом приложении ClickOnce (метод ApplicationDeployment.CurrentDeployment.CheckForDetailedUpdate(false) — только SP2).

Я хотел бы проверить, присутствует ли SP2 во время запуска приложения. Я попытался обнаружить это, перехватив MissingMethodException после вызова метода только для SP2.

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

Код в catch никогда не выполняется, если он работает в .NET 2.0 без SP2. Исключение перехватывается только обработчиком событий AppDomain.CurrentDomain.UnhandledException.

Как это возможно, что MissingMethodException не перехватывается? Я могу представить, что это частный случай — CLR натыкается на метод, которого не существует, и каким-то образом невозможно передать это в блок catch. Я хотел бы понять принцип, лежащий в основе этого.

У кого-нибудь есть ресурсы по этому вопросу? Существуют ли какие-либо другие исключения, которые нельзя поймать в блоке catch?


person Marek    schedule 23.08.2010    source источник


Ответы (4)


Я подозреваю, что это происходит во время JIT, еще до того, как метод будет правильно введен, то есть до того, как ваш блок catch будет поражен. Возможно, если вы поймаете MissingMethodException в методе вызова, это уладит ситуацию... особенно если вы украсите CheckDotNet2SP MethodImpl[MethodImplOptions.NoInlining]. Хотя все равно кажется, что это было бы довольно рискованно.

Вы всегда можете проверить наличие метода с помощью отражения, а не пытаться его вызвать.

person Jon Skeet    schedule 23.08.2010

Есть несколько исключений, которые были определены как «неисправимые». Один из них — MissingMethodException, потому что отсутствие метода в классе — это серьезная ошибка и для восстановления требуется выгрузка класса и перезагрузка нового класса, что нельзя сделать тривиально (если вообще сделать).

Для восстановления нужно переустановить, проверить версии сборок, проверить валидность PE-образов и т.д.

Если все, что вам нужно знать, это установить ли SP2, по умолчанию используется загрузочное приложение, которое просто проверяет установленную версию. Если все в порядке, оно запускает приложение, если нет, оно показывает красивое сообщение.


Обновление, запрошенное OP:
Другие исключения, которые либо трудно перехватить, либо невозможно перехватить (может зависеть от вашей версии .NET, т. е. в .NET 4.0 добавлено больше неперехватываемых): OutOfMemoryException (может быть перехвачено когда он синхронный), StackOverflowException (никогда не может быть пойман), ThreadAbortException (может быть пойман, но особенный, потому что он будет автоматически повторно поднят в конце блока catch), BadImageFormatException и MissingManifestResourceException, если вы попытаетесь его поймать в сборке, вызывающей исключение (если вы загружаете его динамически, как и в случае с MissingMethodException, вы можете его перехватить). И вообще, любое исключение, которое не наследуется от Exception, сложно перехватить (но вы можете перехватить его с помощью общего блока try/catch).

Есть и другие, но с первыми тремя вы столкнетесь чаще всего на практике.

person Abel    schedule 23.08.2010
comment
не могли бы вы перечислить другие типы «неисправимых» исключений? - person Marek; 23.08.2010
comment
У вас есть ссылка на эти неисправимые исключения? Я вижу, что это упоминается в Application.DispatcherUnhandledException, но я не могу найти точную информацию. - person Oliver Bock; 21.07.2011

Исключение возникает на этапе 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);
    }
person desco    schedule 23.08.2010
comment
На самом деле, этот метод по-прежнему будет выгружать приложение в случае ошибки, поскольку она считается неисправимой. - person Abel; 23.08.2010
comment
Я проверил и подтвердил, что этот ответ правильно работает в .NET Core 3.1. - person Chris Gillum; 27.03.2021

Вы можете использовать отражение, чтобы увидеть, существует ли метод.

private void CheckDotNet2SP()
{
    return typeof(WaitHandle).GetMethod("WaitOne", new Type[] { typeof(int) }) 
       != null;
} 
person Dirk Vollmar    schedule 23.08.2010
comment
Обратите внимание, что это не сработает, если JIT-компилятор фактически генерирует исключение. Это будет работать только тогда, когда вызов метода сам по себе выполняется путем отражения. - person Abel; 09.12.2010