【发布时间】:2014-06-17 04:23:07
【问题描述】:
当发生未处理的异常时,我有一个进程会生成小型转储(同时使用AppDomain.CurrentDomain.UnhandledException 和TaskScheduler.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