Почему невозможно обнаружить 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?

Ответ 1

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

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

Ответ 2

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

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

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


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

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

Ответ 3

Исключение выбрано на этапе компиляции 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);
    }

Ответ 4

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

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