当调用进程外时,MiniDumpWriteDump中的访问冲突

MiniDumpWriteDump函数的文档声明

应尽可能从单独的进程中调用MiniDumpWriteDump ,而不是从被转储的目标进程中调用。

所以我写了一个小的MFC崩溃处理程序,就是这么做的。 我遵循Hans Passant的这个SO回答中的build议,也就是说,即使exception指针在崩溃处理程序的上下文中无效,我也会将崩溃程序中的exception指针的值传递给崩溃处理程序。 当我在一个debugging版本中运行testing的时候,这个效果很好,但是当我切换到一个版本的时候,崩溃处理器程序崩溃了,在MiniDumpWriteDump函数中发生访问冲突。

我很难过 为什么这应该在debugging版本中工作,而不是在发布版本中? 这是疯狂的,因为访问违规往往是访问无效指针的指标,而我在崩溃处理程序中接收的exception指针确实是无效的 – 但另一方面,我被告知这应该不重要, MiniDumpWriteDump正在解释指针崩溃过程的上下文(从指针来源处)。

任何想法我可能做错了什么?

在旁注中:Hans在他的回答中提出了一个解决scheme,在该解决scheme中,看门狗进程被预先启动,然后在碰撞过程触发时进入hibernate状态并醒来。 我的解决scheme略有不同:我只是在发生崩溃时启动崩溃处理程序,然后通过命令行参数将崩溃程序中的必要信息传递给崩溃处理程序。 我仔细检查了传递的信息是否正确,特别是exception指针。

Solutions Collecting From Web of "当调用进程外时,MiniDumpWriteDump中的访问冲突"

我在类似的问题上挣扎,现在终于注意到了什么是错的。

MINIDUMP_EXCEPTION_INFORMATION的MSDN文档指出,如果ExceptionPointers地址来自目标进程而不是本地进程,则ClientPointers字段必须为TRUE

正确设置这个字段之后,我可以简单地从崩溃的过程中传递ThreadIdExceptionPointers ,在转储写入过程MINIDUMP_EXCEPTION_INFORMATION它们填充到MINIDUMP_EXCEPTION_INFORMATION中,并且完美地工作。

我改变了方法,使最终的解决方案看起来像Hans Passant建议的那样:看门狗进程已经预先启动,然后进入休眠状态,并在崩溃进程触发时唤醒。 崩溃过程会对EXCEPTION_POINTERS结构进行深层复制,并将该信息传递给看门狗进程。

这里是深拷贝的代码。 正如在问题的评论中提到的,主要的“问题”是EXCEPTION_RECORD ,这是一个可能无限大小的链表。

 // The maximum number of nested exception that we can handle. The value we // use for this constant is an arbitrarily chosen number that is, hopefully, // sufficiently high to support all realistic and surrealistic scenarios. // // sizeof(CrashInfo) for a maximum of 1000 = ca. 80 KB const int MaximumNumberOfNestedExceptions = 1000; // Structure with information about the crash that we can pass to the // watchdog process struct CrashInfo { EXCEPTION_POINTERS exceptionPointers; int numberOfExceptionRecords; // Contiguous area of memory that can easily be processed by memcpy EXCEPTION_RECORD exceptionRecords[MaximumNumberOfNestedExceptions]; CONTEXT contextRecord; }; // The EXCEPTION_POINTERS parameter is the original exception pointer // that we are going to deep-copy. // The CrashInfo parameter receives the copy. void FillCrashInfoWithExceptionPointers(CrashInfo& crashInfo, EXCEPTION_POINTERS* exceptionPointers) { // De-referencing creates a copy crashInfo.exceptionPointers = *exceptionPointers; crashInfo.contextRecord = *(exceptionPointers->ContextRecord); int indexOfExceptionRecord = 0; crashInfo.numberOfExceptionRecords = 0; EXCEPTION_RECORD* exceptionRecord = exceptionPointers->ExceptionRecord; while (exceptionRecord != 0) { if (indexOfExceptionRecord >= MaximumNumberOfNestedExceptions) { // Yikes, maximum number of nested exceptions reached break; } // De-referencing creates a copy crashInfo.exceptionRecords[indexOfExceptionRecord] = *exceptionRecord; ++indexOfExceptionRecord; ++crashInfo.numberOfExceptionRecords; exceptionRecord = exceptionRecord->ExceptionRecord; } } 

当我们在看门狗进程中收到CrashInfo结构时,我们现在有一个问题: EXCEPTION_RECORD引用指向无效的内存地址,即只有在崩溃进程中才有效的内存地址。 以下函数(必须在看门狗进程中运行)修复这些引用。

 // The CrashInfo parameter is both in/out void FixExceptionPointersInCrashInfo(CrashInfo& crashInfo) { crashInfo.exceptionPointers.ContextRecord = &(crashInfo.contextRecord); for (int indexOfExceptionRecord = 0; indexOfExceptionRecord < crashInfo.numberOfExceptionRecords; ++indexOfExceptionRecord) { if (0 == indexOfExceptionRecord) crashInfo.exceptionPointers.ExceptionRecord = &(crashInfo.exceptionRecords[indexOfExceptionRecord]); else crashInfo.exceptionRecords[indexOfExceptionRecord - 1].ExceptionRecord = &(crashInfo.exceptionRecords[indexOfExceptionRecord]); } } 

我们现在准备将&(crashInfo.exceptionPointers)传递给MiniDumpWriteDump函数。

注意:显然这不是一个完整的解决方案。 您可能想要将更多的信息从崩溃过程传递给看门狗进程。 CrashInfo结构是保存这些信息的候选者。 这里也没有显示进程如何相互通信的方式。 在我的情况下,我用Hans Passant提出的解决方案,在问题开始时链接:使用一个事件进行同步(CreateEvent + SetEvent)和一个内存映射文件(CreateFileMapping + MapViewOfFile)来洗刷一个进程到下一个。 事件和内存映射文件的(唯一)名称由主进程确定,并通过命令行参数传递给看门狗进程。