【问题标题】:Why does generating a mini dump inside a UnobservedTaskException raise an AccessViolationException为什么在 UnobservedTaskException 中生成迷你转储会引发 AccessViolationException
【发布时间】:2014-06-17 04:23:07
【问题描述】:

当发生未处理的异常时,我有一个进程会生成小型转储(同时使用AppDomain.CurrentDomain.UnhandledExceptionTaskScheduler.UnobservedTaskException)。转储在未处理的异常处理程序中工作正常,但在从未观察的任务异常处理程序调用时会产生AccessViolationException(请参见下面的示例代码)。

根据我在 MSDN 和 StackOverflow 上阅读的内容,这两个代码路径之间的主要区别在于后者发生在终结器线程中,我猜测线程状态或安全性正在阻止此操作成功。

是否有任何信息或链接可以解释为什么在未观察到的线程异常情况下小型转储操作失败?我很高兴接受我正在尝试的事情根本不可能,但我真的很想确认这一点并知道为什么......

下面的代码通过在任务中触发未处理的异常并强制垃圾收集来触发未观察到的线程异常来演示我的问题。然后MiniDumpWriteDump 调用会生成一个AccessViolationException

using System;
using System.IO;
using System.Runtime.ConstrainedExecution;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;
using System.Security;
using System.Threading;
using System.Threading.Tasks;

namespace ExceptionTest
{
    public class ClassThatThrows
    {
        public void Throw()
        {
            var t = new Task(() =>
            {
                Console.WriteLine("Throwing...");
                throw new NullReferenceException();
            });

            t.Start();
        }
    }

    public class Program
    {
        public static void Main(string[] args)
        {
            TaskScheduler.UnobservedTaskException += HandleUnobservedTaskException;

            var test = new ClassThatThrows();
            test.Throw();

            Thread.Sleep(1000);

            GC.Collect();
            GC.WaitForPendingFinalizers();

            Console.ReadLine();
        }

        [HandleProcessCorruptedStateExceptions]
        [ReliabilityContract(Consistency.MayCorruptInstance, Cer.MayFail)]
        [SecurityCritical]
        private static void HandleUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
        {
            WriteMiniDump();
        }

        private static void WriteMiniDump()
        {
            var miniDumpFilePath = GetMiniDumpFilePath();

            using (var fileStream = new FileStream(miniDumpFilePath, FileMode.Create, FileAccess.Write, FileShare.None))
            {
                MiniDumpExceptionInformation exceptionInfo;
                exceptionInfo.ThreadId = GetCurrentWin32ThreadId();
                exceptionInfo.ClientPointers = false;
                exceptionInfo.ExceptionPointers = Marshal.GetExceptionPointers();

                var currentProcess = GetCurrentProcess();
                var currentProcessId = GetCurrentProcessId();

                var safeFileHandle = fileStream.SafeFileHandle;
                MiniDumpWriteDump(currentProcess, currentProcessId, safeFileHandle.DangerousGetHandle(), 0x00000000, ref exceptionInfo, IntPtr.Zero, IntPtr.Zero);
            }
        }

        protected static string GetMiniDumpFilePath()
        {
            var timestamp = DateTime.Now.ToString("yyyyMMddTHHmmss");
            var fileName = string.Format("MiniDump.{0}.mdmp", timestamp);
            return Path.Combine(Path.GetTempPath(), fileName);
        }

        [DllImport("Kernel32", EntryPoint = "GetCurrentThreadId", ExactSpelling = true)]
        public static extern uint GetCurrentWin32ThreadId();

        [DllImport("dbghelp.dll", EntryPoint = "MiniDumpWriteDump", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
        public static extern bool MiniDumpWriteDump(IntPtr hProcess, uint processId, IntPtr hFile, uint dumpType, ref MiniDumpExceptionInformation expParam, IntPtr userStreamParam, IntPtr callbackParam);

        [DllImport("kernel32.dll", EntryPoint = "GetCurrentProcess", ExactSpelling = true)]
        public static extern IntPtr GetCurrentProcess();

        [DllImport("kernel32.dll", EntryPoint = "GetCurrentProcessId", ExactSpelling = true)]
        public static extern uint GetCurrentProcessId();

        [StructLayout(LayoutKind.Sequential, Pack = 4)]
        public struct MiniDumpExceptionInformation
        {
            public uint ThreadId;
            public IntPtr ExceptionPointers;
            [MarshalAs(UnmanagedType.Bool)]
            public bool ClientPointers;
        }
    }
}

谢谢

【问题讨论】:

  • 您是否尝试过固定正在传递的IntPtr
  • 没有重现,我没有看到明显的错误。问题中应记录 Windows、dbghelp.dll 和 .NET 版本。我所能推荐的只是将GC.KeepAlive(e.Exception); 添加到事件处理程序中,但是远距离拍摄。寻找环境问题。并启用非托管调试、Microsoft 符号服务器并发布您在爆炸时看到的堆栈跟踪。

标签: c# multithreading c#-4.0 exception-handling task-parallel-library


【解决方案1】:

我尝试在我的家用机器上执行 Yuval 的建议,但无法重现该问题,这立即让我考虑使用原始机器上的 dbghelp.dll 版本。

将最新 Windows 8.1 SDK 中的 dbghelp.dll 放入我的可执行文件夹后,问题就消失了,因此我可以将责任归咎于过时的 dbghelp.dll,而不是创建迷你转储时出现的一些晦涩的 C# 问题来自终结器线程。

作为参考,dbghelp.dll 6.1.7601.17514 演示了该问题,dbghelp.dll 6.3.9600.17029 已确认可以解决该问题。异常的调用堆栈(虽然没有透露太多)如下所示:

    ntdll.dll!NtWaitForSingleObject()
KernelBase.dll!WaitForSingleObjectEx()
msvcr110_clr0400.dll!__C_specific_handler()
ntdll.dll!RtlpExecuteHandlerForException()
ntdll.dll!RtlDispatchException()
ntdll.dll!KiUserExceptionDispatch()
dbghelp.dll!MiniDumpWriteDump()
[Managed to Native Transition]  
    ExceptionTest.exe!ExceptionTest.Program.WriteMiniDump() Line 66 C#
ExceptionTest.exe!ExceptionTest.Program.HandleUnobservedTaskException(object sender, System.Threading.Tasks.UnobservedTaskExceptionEventArgs e) Line 48 C#
mscorlib.dll!System.Threading.Tasks.TaskScheduler.PublishUnobservedTaskException(object sender, System.Threading.Tasks.UnobservedTaskExceptionEventArgs ueea)
mscorlib.dll!System.Threading.Tasks.TaskExceptionHolder.Finalize()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-09
    • 1970-01-01
    相关资源
    最近更新 更多