为什么CloseHandle对异常的处理在.NET4和3.5之间有所不同

本文关键字:之间 有所不同 NET4 CloseHandle 异常 处理 为什么 | 更新日期: 2023-09-27 18:29:09

我遇到这样一种情况:当在调试器下运行时,对CloseHandle的PInvoke调用会在.NET 4应用程序中抛出SEHException。与其他在从3.5迁移到4时遇到类似问题的人不同,我对这种行为并不特别在意,并且已经找到了问题(第三方库在同一句柄上调用CloseHandle两次)。然而,我很困惑,为什么这种行为不会发生在.NET3.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的文件,这是预期行为:

如果应用程序在调试器下运行,则函数将如果接收到的句柄值不是有效或伪句柄值。如果关闭手柄,可能会发生这种情况两次,或者如果对由FindFirstFile函数,而不是调用FindClose函数。

但是,当编译为.NET 3.5应用程序(或CLR 2.0)时,第二个CloseHandle调用不会引发异常,并且会打印消息"No exception"

根据这篇文章,为.NET 4发布的更新CLR具有一些不同的默认行为,包括可能破坏进程状态的低级异常。然而,据我从那篇文章中所了解,没有提到任何以前的CLR行为会导致异常被完全忽略。

为什么.NET 3.5(或CLR 2.0)应用程序没有表现出.NET 4中存在的CloseHandle的记录行为

为什么CloseHandle对异常的处理在.NET4和3.5之间有所不同

Windows只有在看到附加了调试器时才会生成SEH异常。本机调试器。.NET 4托管调试器发生了更改,现在Windows可以看到这样的调试器。对不起,我找不到任何像样的链接来记录这种新行为的确切细节。

编辑:我找到了一个不错的。本文底部的项目符号8:

在后台,我们构建在本机调试管道上在v2兼容模式下,ICD继续拥有到目标进程的管道(因为这是v2模型),但该管道不再是与目标进程共享的IPC对象的集合,而是本机调试器使用的同一管道。具体来说,我们通过调用kernel32来附加到一个进程!DebugActiveProcess,并使用kernel32获取我们的托管事件(导致调用ICorDebugManagedCallback的事件)!等待调试事件这也意味着kernel32!IsDebuggerPresent现在在执行仅托管调试时返回true这还有一个很好的副作用,即在启用内核调试器时避免了只进行托管调试的问题(操作系统假设在未附加调试器时发生的任何断点指令都会导致内核调试器中断)。

顺便说一句,这不仅仅是一个表面上的修复,尽管处理回收攻击是让微软员工夜不能寐的事情。.NET 4版本的CLR是使用CRT版本构建的,该版本用于检查缓冲区溢出。当检测到一个时,CRT立即终止程序。没有AppDomain.UnhandledException,这是对桌面的即时崩溃。但是,此代码执行与Windows相同的操作,检查本机调试器并在附加断点时生成断点。因此,托管调试器开始看起来像本机调试器是非常重要的,这是真正诊断崩溃的唯一方法。

对此没有真正的"好"答案。。3.5在SEH中有一个错误,在4.0中得到了修复。它正在吞噬这个例外。我遇到了类似的问题,我们打开了一张带有MSFT的机票——他们的回答是"错误修复"。