使用 FileMapping 传递 _EXCEPTION_POINTERS * 时遇到问题

Posted

技术标签:

【中文标题】使用 FileMapping 传递 _EXCEPTION_POINTERS * 时遇到问题【英文标题】:Trouble passing _EXCEPTION_POINTERS * using FileMapping 【发布时间】:2013-04-15 09:25:48 【问题描述】:

我想做一个进程外异常处理程序,并且我创建了一个看门狗进程,它在子进程引发异常时进行专门的异常处理。我已经通过 events 成功调用了 watchdog 进程。我面临的问题是尝试将异常信息指针传递给其他进程。

我来到这里 Passing a pointer to process spawned with exec() 并开始知道在共享内存中传递指针有这个问题:

"如果你使用共享内存,你不能传递指针。指针将包含虚拟地址,这与一个进程不同。你必须交换偏移值,基于共享内存的开始地区。

如果您不使用共享内存,则无法交换任何类型的指针:其他进程将无法访问您进程的内存。”

现在我该如何克服呢?

流程 1:

    struct mytest
    
      _EXCEPTION_POINTERS * except ;
      DWORD ThreadId ;
      DWORD ProcessId ;
    

    OpenFileMapping ( ) ;

    void * pBuf = MapViewOfFile ( ) ;

    mytest passdata ;

    CopyMemory ( pBuf , &passdata , sizeof ( passdata ) ) ;

    UnMapView ( ) ;

    CloseHandle ( ) ;

(例如)流程2:

    cout << passdata->except->ExceptionRecord->ExceptionCode << endl ;

会崩溃。我理解这是因为虚拟地址是特定于进程的。但是在这种情况下如何将异常信息传递给不同的进程并编写一个小型转储?

P.S :我什至尝试单独传递 PEXCEPTION_RECORD 结构,但不起作用。

【问题讨论】:

【参考方案1】:

我将把它放在一起作为答案,虽然它确实应该是对Hans's answer(以及那里的 cmets)的评论,但似乎有必要进行一些解释:

问题中发布的代码正确地将struct mytest 结构的值传递到共享内存中。

第二个代码sn -p:

(例如)流程2:

cout << passdata->except->ExceptionRecord->ExceptionCode << endl ;

虽然显示了一个误解:虽然您可以读取指针 passdata.except 的值,但在进程 2 中这只是一个任意的 32/64 位值,它不是一个有效的指针。

可以将此传递给MiniDumpWriteDump,此函数将在目标进程(proc 1)的上下文中评估此指针值。但是您不能在流程 #2 中取消引用它。

Hans 的示例给出了解决方案,如果您在进程#2 中需要ExeptionCode 的值,那么您需要取消引用 proc#1 中的指针并将该值放入您写入共享内存的数据中。

【讨论】:

酷,谢谢@Martin Ba。将按照您的指导进行。 终于搞定了。如果将来有人遇到无法写入 MiniDump 的问题,请检查您的进程安全访问和权限。【参考方案2】:

对,你不能在另一个进程中取消引用指针,它只在崩溃的进程中有效。仅传递给 MiniDumpWriteDump()、MINIDUMP_EXCEPTION_INFORMATION.ExceptionPointers 字段就足够了。从技术上讲,您可以使用 ReadProcessMemory() 但对于崩溃的进程这样做是不必要的风险。简单的解决方案是在结构中添加一个额外的字段,用于存储异常代码并由异常过滤器编写。

mytest passdata ;
passdata.except = ExceptionInfo;
// Note: added field
passdata.ExceptionCode = ExceptionInfo->ExceptionRecord->ExceptionCode;
passdata.ThreadId = GetCurrentThreadId();
// etc..

还要避免调用OpenFileMapping、MapViewOfFile等winapi函数,风险太大。当程序由于进程堆的堆损坏而崩溃时,它们往往会死锁。崩溃和死锁的常见原因是堆锁仍然被持有。只需在程序初始化时执行此操作。您也无需费心清理,当您的看门狗进程在进行小型转储后终止崩溃的进程时,Windows 会处理它。

【讨论】:

比@Hans Passant !我只有一个澄清。您是否尝试过传递 MINIDUMP_EXCEPTION_INFORMATION.ExceptionPointers 字段。在子进程中是否可读?? 是的,这就是该字段存在的原因。不,它在“客户端进程”中不可读。或者我解释的看门狗进程。它由 MiniDumpWriteDump 使用并记录在 minidump 文件中。因此,当您在调试器中打开 minidump 时,它可以显示异常发生的位置。调试器使用指针没有问题,因为它正在调试拥有指针的进程。 那么如何以及在看门狗中调用 MiniDumpWriteDump 需要哪些步骤? 这是一个完全不同的问题。如果现有答案没有帮助,请务必单击该按钮。使用谷歌或本页右上角的搜索框。 再次感谢赐教!

以上是关于使用 FileMapping 传递 _EXCEPTION_POINTERS * 时遇到问题的主要内容,如果未能解决你的问题,请参考以下文章

FileMapping写的内容什么时候会flush到磁盘?

Minifilter 拦截FileMapping IO事件

Minifilter 拦截FileMapping IO事件

python exception的传递

测试 __try, __finally, __except(被__finally捕获的异常, 还会被上一级的__except捕获。反之不行)

无法在动态链接库 msvcrt.dll 中找到过程入口点 _except_handler4_common