Подтвердить что ты не робот

Почему обработка исключений из CloseHandle отличается между .NET 4 и 3.5?

Я встречаюсь с ситуацией, когда вызов 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 на второй CloseHandle выдается SEHException. Согласно документации для 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?

4b9b3361

Ответ 1

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

Изменить: я нашел приличный. Bullet 8 внизу это сообщение в блоге:

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

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

Ответ 2

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