Почему обработка исключений из CloseHandle отличается в .NET 4 и 3.5? - PullRequest
18 голосов
/ 26 марта 2012

Я сталкиваюсь с ситуацией, когда вызов PInvoke для CloseHandle вызывает SEHException в приложении .NET 4 при запуске под отладчиком. В отличие от других, которые сталкивались с аналогичными проблемами при переходе с 3.5 на 4 , меня это не особо беспокоит, и я уже обнаружил проблему (сторонняя библиотека, вызывающая CloseHandle дважды для одного и того же дескриптора) , Тем не менее, я озадачен тем, почему такое поведение не происходит в приложении .NET 3.5.

Следующий небольшой, но полный пример демонстрирует поведение, с которым я сталкиваюсь (протестировано на XP SP3 и Win 7 x64, всегда скомпилировано как x86):

class Program
{
    static void Main(string[] args)
    {
        try
        {
            var hFileMapping = CreateFileMapping(new IntPtr(-1), IntPtr.Zero, 0x04 /* read write */, 0, 0x1000, null);
            CloseHandle(hFileMapping);
            CloseHandle(hFileMapping);
            Console.WriteLine("No exception");
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
        }

        Console.ReadKey();
    }

    [DllImport("kernel32", SetLastError = true)]
    static extern IntPtr CreateFileMapping(IntPtr hFile, IntPtr lpAttributes, int flProtect, int dwMaximumSizeHigh, int dwMaximumSizeLow, string lpName);

    [DllImport("kernel32", SetLastError = true)]
    static extern bool CloseHandle(IntPtr handle);
}

При запуске в качестве приложения .NET 4, SEHException генерируется во втором CloseHandle. Согласно документации для CloseHandle, это ожидаемое поведение:

Если приложение работает под отладчиком, функция бросить исключение, если оно получает либо значение дескриптора, которое не допустимое или псевдо-дескриптор значение. Это может случиться, если закрыть ручку дважды, или если вы вызываете CloseHandle для дескриптора, возвращенного Функция FindFirstFile вместо вызова функции FindClose.

Однако при компиляции в качестве приложения .NET 3.5 (или CLR 2.0) при втором вызове CloseHandle не возникает исключений, и выводится сообщение "No exception".

Согласно этой статье обновленный CLR, выпущенный для .NET 4, имеет несколько другое поведение по умолчанию с низкоуровневыми исключениями, которые могут повредить состояние процесса. Однако, насколько я понимаю из этой статьи, ничего не упоминалось о предыдущем поведении CLR, из-за которого исключение было бы полностью проигнорировано.

Почему приложение .NET 3.5 (или CLR 2.0) не демонстрирует документированное поведение CloseHandle, присутствующее в .NET 4?

Ответы [ 2 ]

9 голосов
/ 09 апреля 2012

Windows генерирует исключение SEH только тогда, когда видит, что отладчик подключен. Отладчик native . Произошло изменение в управляемом отладчике .NET 4, которое теперь заставляет Windows видеть такой отладчик. Я не могу найти приличных ссылок, которые документируют точные детали этого нового поведения, извините.

Редактировать: я нашел приличный. Пуля 8 внизу этого блога :

Под капотом мы построены на собственном конвейере отладки. В режиме v2-compat ICD продолжает владеть конвейером для целевого процесса (поскольку это была модель V2), но этот конвейер больше не является коллекцией общих объектов IPC с целевым процессом, а вместо этого является тем же конвейером отладчик использует. В частности, мы подключаемся к процессу, вызывая kernel32! DebugActiveProcess, и получаем наши управляемые события (вещи, которые приводят к вызовам ICorDebugManagedCallback), используя kernel32! WaitForDebugEvent. Это также означает, что kernel32! IsDebuggerPresent теперь возвращает true при выполнении только управляемой отладки. Это также имеет приятный побочный эффект - устранение проблемы с выполнением управляемой только отладки, когда включен отладчик ядра (ОС предполагает, что любые инструкции точки останова, которые происходят, когда отладчик не подключен, должны вызывать перерыв в отладчике ядра).

Между прочим, это не просто косметическое исправление, хотя работа с атаками повторного использования - это то, что не дает сотрудникам Microsoft бодрствовать ночью. Версия CLR .NET 4 построена с версией CRT, которая проверяет переполнение буфера. Когда один обнаружен, CRT немедленно завершает программу. Нет AppDomain.UnhandledException, это немедленный сбой на рабочем столе. Этот код, однако, делает то же самое, что и Windows, проверяет собственный отладчик и создает точку останова, когда она подключена. Поэтому было очень важно, чтобы управляемый отладчик начал выглядеть как собственный, единственный способ действительно диагностировать сбой.

1 голос
/ 09 апреля 2012

Нет действительно «хорошего» ответа на этот вопрос. 3.5 имел ошибку в SEH, которая была исправлена ​​в 4.0 .. Это было исключение. У меня была похожая проблема, и мы открыли тикет с MSFT - их ответ был «bugfix».

...