使用回调调用 MiniDumpWriteDump

发布于 2024-08-03 07:34:14 字数 2664 浏览 9 评论 0原文

我想使用 MiniDumpWriteDump 函数创建一些自定义转储文件(主要是,我想导出一个包含线程调用堆栈最少信息的转储文件),但我在定义需要传递的结构时遇到困难回调函数的参数

[StructLayout(LayoutKind.Explicit)]
internal struct MINIDUMP_CALLBACK_OUTPUT
{
    [FieldOffset(0)]
    public ulong ModuleWriteFlags;
    [FieldOffset(0)]
    public ulong ThreadWriteFlags;
}

public struct MINIDUMP_CALLBACK_INFORMATION
    {
        public IntPtr CallbackRoutine;
        public IntPtr CallbackParam;
    }

public delegate bool MINIDUMP_CALLBACK_ROUTINE(
        IntPtr CallBackParam, 
        MINIDUMP_CALLBACK_INPUT input, 
        MINIDUMP_CALLBACK_OUTPUT output);

[DllImport("dbghelp.dll")]
public static extern bool MiniDumpWriteDump(IntPtr hProcess, Int32 ProcessId, IntPtr hFile, int DumpType,
    IntPtr ExceptionParam, IntPtr UserStreamParam, IntPtr CallStackParam);

调用如下所示:

MINIDUMP_CALLBACK_INFORMATION mci;
MINIDUMP_CALLBACK_ROUTINE r = new MINIDUMP_CALLBACK_ROUTINE(MyCallback);
GC.KeepAlive(r);
mci.CallbackRoutine = Marshal.GetFunctionPointerForDelegate(r);
mci.CallbackParam = IntPtr.Zero;    
IntPtr structPointer = Marshal.AllocHGlobal(Marshal.SizeOf(mci));    
Marshal.StructureToPtr(mci, structPointer, true);    
MiniDumpWriteDump(process[0].Handle, process[0].Id,
                        fs.SafeFileHandle.DangerousGetHandle(),
                        (int)MINIDUMP_TYPE.MiniDumpNormal,
                        Marshal.GetExceptionPointers(),
                        IntPtr.Zero,
                        structPointer);

Marshal.FreeHGlobal(structPointer);

所以我的问题是如何定义 MINIDUMP_CALLBACK_INPUT:

C 中引起问题的结构的定义是:

typedef struct _MINIDUMP_CALLBACK_INPUT 
{
    ULONG                       ProcessId;
    HANDLE                      ProcessHandle;
    ULONG                       CallbackType;
    union 
    {
        MINIDUMP_THREAD_CALLBACK        Thread;
        MINIDUMP_THREAD_EX_CALLBACK     ThreadEx;
        MINIDUMP_MODULE_CALLBACK        Module;
        MINIDUMP_INCLUDE_THREAD_CALLBACK IncludeThread;
        MINIDUMP_INCLUDE_MODULE_CALLBACK IncludeModule;
    } u;
} MINIDUMP_CALLBACK_INPUT

typedef struct _MINIDUMP_MODULE_CALLBACK 
{
    PWCHAR                      FullPath;
    ULONGLONG                   BaseOfImage;
    ULONG                       SizeOfImage;
    ULONG                       CheckSum;
    ULONG                       TimeDateStamp;
    VS_FIXEDFILEINFO            VersionInfo;
    PVOID                       CvRecord;
    ULONG                       SizeOfCvRecord;
    PVOID                       MiscRecord;
    ULONG                       SizeOfMiscRecord;
} MINIDUMP_MODULE_CALLBACK

I want to use the MiniDumpWriteDump function to create some custom dump files (mainly, i want to export a dump file that contains the minimum amount of information for the thread callstacks), but i am having difficulties defining the structures that need to be passed as a parameter to the callback function

[StructLayout(LayoutKind.Explicit)]
internal struct MINIDUMP_CALLBACK_OUTPUT
{
    [FieldOffset(0)]
    public ulong ModuleWriteFlags;
    [FieldOffset(0)]
    public ulong ThreadWriteFlags;
}

public struct MINIDUMP_CALLBACK_INFORMATION
    {
        public IntPtr CallbackRoutine;
        public IntPtr CallbackParam;
    }

public delegate bool MINIDUMP_CALLBACK_ROUTINE(
        IntPtr CallBackParam, 
        MINIDUMP_CALLBACK_INPUT input, 
        MINIDUMP_CALLBACK_OUTPUT output);

[DllImport("dbghelp.dll")]
public static extern bool MiniDumpWriteDump(IntPtr hProcess, Int32 ProcessId, IntPtr hFile, int DumpType,
    IntPtr ExceptionParam, IntPtr UserStreamParam, IntPtr CallStackParam);

And the call looks like this:

MINIDUMP_CALLBACK_INFORMATION mci;
MINIDUMP_CALLBACK_ROUTINE r = new MINIDUMP_CALLBACK_ROUTINE(MyCallback);
GC.KeepAlive(r);
mci.CallbackRoutine = Marshal.GetFunctionPointerForDelegate(r);
mci.CallbackParam = IntPtr.Zero;    
IntPtr structPointer = Marshal.AllocHGlobal(Marshal.SizeOf(mci));    
Marshal.StructureToPtr(mci, structPointer, true);    
MiniDumpWriteDump(process[0].Handle, process[0].Id,
                        fs.SafeFileHandle.DangerousGetHandle(),
                        (int)MINIDUMP_TYPE.MiniDumpNormal,
                        Marshal.GetExceptionPointers(),
                        IntPtr.Zero,
                        structPointer);

Marshal.FreeHGlobal(structPointer);

So my question is how to define MINIDUMP_CALLBACK_INPUT:

The definition of the structures in C that pose problems are:

typedef struct _MINIDUMP_CALLBACK_INPUT 
{
    ULONG                       ProcessId;
    HANDLE                      ProcessHandle;
    ULONG                       CallbackType;
    union 
    {
        MINIDUMP_THREAD_CALLBACK        Thread;
        MINIDUMP_THREAD_EX_CALLBACK     ThreadEx;
        MINIDUMP_MODULE_CALLBACK        Module;
        MINIDUMP_INCLUDE_THREAD_CALLBACK IncludeThread;
        MINIDUMP_INCLUDE_MODULE_CALLBACK IncludeModule;
    } u;
} MINIDUMP_CALLBACK_INPUT

typedef struct _MINIDUMP_MODULE_CALLBACK 
{
    PWCHAR                      FullPath;
    ULONGLONG                   BaseOfImage;
    ULONG                       SizeOfImage;
    ULONG                       CheckSum;
    ULONG                       TimeDateStamp;
    VS_FIXEDFILEINFO            VersionInfo;
    PVOID                       CvRecord;
    ULONG                       SizeOfCvRecord;
    PVOID                       MiscRecord;
    ULONG                       SizeOfMiscRecord;
} MINIDUMP_MODULE_CALLBACK

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

左耳近心 2024-08-10 07:34:15

我想是工会给你添麻烦了吧?

如果 CallbackType==KernelMinidumpStatusCallback,则 CALLBACK_INPUT 结构定义为:

ULONG ProcessId;
HANDLE ProcessHandle;
ULONG CallbackType;
HRESULT Status;

如果 CallbackType==ThreadCallback,则为:

ULONG ProcessId;
HANDLE ProcessHandle;
ULONG CallbackType;
MINIDUMP_THREAD_CALLBACK Thread;

如果 CallbackType==ThreadExCallback,则为:

ULONG ProcessId;
HANDLE ProcessHandle;
ULONG CallbackType;
MINIDUMP_THREAD_EX_CALLBACK ThreadEx;

依此类推(此来自 MSDN) - 看起来第 4 个成员可以是 8 种不同类型之一,取决于 CallbackType 等于什么。在内部,Windows 将为所有这些结构使用相同的内存块(将较小的结构填充到最大的结构)。在 C++ 中,通过类型转换很容易获得所需的类型。

我不知道如何在 C# 中执行此操作。我在C++中使用过MiniDumpWriteDump,但从未使用过回调函数。如果您可以确定 CallbackType 始终是一个值,您可以只编写一个结构的代码,但我不知道情况是否如此。

抱歉,我无法提供更多信息,但这也许有助于描述问题。

I take it that it is the union that is giving you trouble?

If CallbackType==KernelMinidumpStatusCallback, then the CALLBACK_INPUT structure is defined as:

ULONG ProcessId;
HANDLE ProcessHandle;
ULONG CallbackType;
HRESULT Status;

If CallbackType==ThreadCallback, then it is:

ULONG ProcessId;
HANDLE ProcessHandle;
ULONG CallbackType;
MINIDUMP_THREAD_CALLBACK Thread;

If CallbackType==ThreadExCallback, then it is:

ULONG ProcessId;
HANDLE ProcessHandle;
ULONG CallbackType;
MINIDUMP_THREAD_EX_CALLBACK ThreadEx;

And so on (this is from MSDN) -- it looks like that 4th member can be one of 8 different types, depending on what CallbackType is equal to. Internally, Windows will use the same chunk of memory for all of these structures (padding the smaller ones to the largest one's size). In C++, it's an easy typecast to get the type you need.

I'm not sure how to do this in C#. I have used MiniDumpWriteDump in C++, but never used the callback function. If you could be sure that CallbackType was always one value, you could just code that one structure, but I don't know if this is the case.

Sorry I can't provide more information, but maybe this helps describe the problem.

蓝礼 2024-08-10 07:34:14

这不是您问题的直接答案,而是一种解决方法...

您知道 ClrDump lib哪个可以满足您的需求?几年前我在一个项目中使用过它,它对我来说效果很好。


对作者评论的回答

在网站上阅读:

ClrDump 可以生成小型小型转储,其中包含足够的信息来恢复应用程序中所有线程的调用堆栈。

我将 API 包装在以下类中:

internal class ClrDump
{
  [return: MarshalAs(UnmanagedType.Bool)]
  [DllImport("clrdump.dll", CharSet=CharSet.Unicode, SetLastError=true)]
  public static extern bool CreateDump(uint ProcessId, string FileName, MINIDUMP_TYPE DumpType, uint ExcThreadId, IntPtr ExtPtrs);

  [return: MarshalAs(UnmanagedType.Bool)]
  [DllImport("clrdump.dll", CharSet=CharSet.Unicode, SetLastError=true)]
  public static extern bool RegisterFilter(string FileName, MINIDUMP_TYPE DumpType);

  [DllImport("clrdump.dll")]
  public static extern FILTER_OPTIONS SetFilterOptions(FILTER_OPTIONS Options);

  [return: MarshalAs(UnmanagedType.Bool)]
  [DllImport("clrdump.dll", SetLastError=true)]
  public static extern bool UnregisterFilter();


  [Flags]
  public enum FILTER_OPTIONS
  {
    CLRDMP_OPT_CALLDEFAULTHANDLER = 1
  }

  [Flags]
  public enum MINIDUMP_TYPE
  {
    MiniDumpFilterMemory = 8,
    MiniDumpFilterModulePaths = 0x80,
    MiniDumpNormal = 0,
    MiniDumpScanMemory = 0x10,
    MiniDumpWithCodeSegs = 0x2000,
    MiniDumpWithDataSegs = 1,
    MiniDumpWithFullMemory = 2,
    MiniDumpWithFullMemoryInfo = 0x800,
    MiniDumpWithHandleData = 4,
    MiniDumpWithIndirectlyReferencedMemory = 0x40,
    MiniDumpWithoutManagedState = 0x4000,
    MiniDumpWithoutOptionalData = 0x400,
    MiniDumpWithPrivateReadWriteMemory = 0x200,
    MiniDumpWithProcessThreadData = 0x100,
    MiniDumpWithThreadInfo = 0x1000,
    MiniDumpWithUnloadedModules = 0x20
  }
}

然后,在初始化代码中的某个位置,我调用了 RegisterFilter 方法,该方法为当前进程中未处理的异常注册一个内部过滤器。如果进程因未处理的异常(可以是本机异常或托管异常)而崩溃,过滤器将捕获它并创建一个小型转储(具有指定的文件名)。以下是示例代码:

StringBuilder sb = new StringBuilder();
sb.Append(Path.GetFileNameWithoutExtension(Application.ExecutablePath));
sb.Append("_");
sb.Append(DateTime.Now.ToString("yyyyMMddHHmmssFF"));
sb.Append(".dmp");
string dmpFilePath = Path.Combine(Path.GetTempPath(), sb.ToString());
ClrDump.RegisterFilter(_dmpFilePath, ClrDump.MINIDUMP_TYPE.MiniDumpNormal);

您可以阅读本文来了解不同的 MINIDUMP_TYPE选项,但我认为基本的(MiniDumpNormal)可以满足您的需求。

It's not a direct answer of your question but a workaround...

Do you know the ClrDump lib which does what you need ? I've used it for a project several years ago and it works fine for me.


Answer to author comment:

Read on the site:

ClrDump can produce small minidumps that contain enough information to recover the call stacks of all threads in the application.

I wrapped the API in the following class:

internal class ClrDump
{
  [return: MarshalAs(UnmanagedType.Bool)]
  [DllImport("clrdump.dll", CharSet=CharSet.Unicode, SetLastError=true)]
  public static extern bool CreateDump(uint ProcessId, string FileName, MINIDUMP_TYPE DumpType, uint ExcThreadId, IntPtr ExtPtrs);

  [return: MarshalAs(UnmanagedType.Bool)]
  [DllImport("clrdump.dll", CharSet=CharSet.Unicode, SetLastError=true)]
  public static extern bool RegisterFilter(string FileName, MINIDUMP_TYPE DumpType);

  [DllImport("clrdump.dll")]
  public static extern FILTER_OPTIONS SetFilterOptions(FILTER_OPTIONS Options);

  [return: MarshalAs(UnmanagedType.Bool)]
  [DllImport("clrdump.dll", SetLastError=true)]
  public static extern bool UnregisterFilter();


  [Flags]
  public enum FILTER_OPTIONS
  {
    CLRDMP_OPT_CALLDEFAULTHANDLER = 1
  }

  [Flags]
  public enum MINIDUMP_TYPE
  {
    MiniDumpFilterMemory = 8,
    MiniDumpFilterModulePaths = 0x80,
    MiniDumpNormal = 0,
    MiniDumpScanMemory = 0x10,
    MiniDumpWithCodeSegs = 0x2000,
    MiniDumpWithDataSegs = 1,
    MiniDumpWithFullMemory = 2,
    MiniDumpWithFullMemoryInfo = 0x800,
    MiniDumpWithHandleData = 4,
    MiniDumpWithIndirectlyReferencedMemory = 0x40,
    MiniDumpWithoutManagedState = 0x4000,
    MiniDumpWithoutOptionalData = 0x400,
    MiniDumpWithPrivateReadWriteMemory = 0x200,
    MiniDumpWithProcessThreadData = 0x100,
    MiniDumpWithThreadInfo = 0x1000,
    MiniDumpWithUnloadedModules = 0x20
  }
}

and then, somewhere in my initialization code, I called the method RegisterFilter which registers an internal filter for unhandled exceptions in the current process. If the process crashes with unhandled exception (it can be native or managed exception), the filter catches it and creates a minidump (with the specified file name). Here is a sample code for this:

StringBuilder sb = new StringBuilder();
sb.Append(Path.GetFileNameWithoutExtension(Application.ExecutablePath));
sb.Append("_");
sb.Append(DateTime.Now.ToString("yyyyMMddHHmmssFF"));
sb.Append(".dmp");
string dmpFilePath = Path.Combine(Path.GetTempPath(), sb.ToString());
ClrDump.RegisterFilter(_dmpFilePath, ClrDump.MINIDUMP_TYPE.MiniDumpNormal);

You can read this article to understand the different MINIDUMP_TYPE options, but I think the basic one (MiniDumpNormal) could fit your needs.

浅笑依然 2024-08-10 07:34:14

使用这段代码定义一个32位联合结构:

[StructLayout(LayoutKind.Explicit)]
internal struct MINIDUMP_CALLBACK_INPUT 
{
  [FieldOffset(0)]
  UInt32 ProcessId;

  [FieldOffset(4)]
  IntPtr ProcessHandle;

  [FieldOffset(8)]
  UInt32 CallbackType;

  [FieldOffset(12)]
  MINIDUMP_THREAD_CALLBACK Thread;
  [FieldOffset(12)]
  MINIDUMP_THREAD_EX_CALLBACK ThreadEx;
  [FieldOffset(12)]
  MINIDUMP_MODULE_CALLBACK Module;
  [FieldOffset(12)]
  MINIDUMP_INCLUDE_THREAD_CALLBACK IncludeThread;
  [FieldOffset(12)]
  MINIDUMP_INCLUDE_MODULE_CALLBACK IncludeModule;
};

我认为在64位平台上,无论ULONG是64,偏移量应该分别是{0,8,16,24}或{0,4,12,16}或 32 位。


编辑

我认为您可以使用 MarshalAsAttribute 来专门转换字段:

  [StructLayout(LayoutKind.Sequential)]
  struct VS_FIXEDFILEINFO
  {
    UInt32 dwSignature;
    UInt32 dwStrucVersion;
    UInt32 dwFileVersionMS;
    UInt32 dwFileVersionLS;
    UInt32 dwProductVersionMS;
    UInt32 dwProductVersionLS;
    UInt32 dwFileFlagsMask;
    UInt32 dwFileFlags;
    UInt32 dwFileOS;
    UInt32 dwFileType;
    UInt32 dwFileSubtype;
    UInt32 dwFileDateMS;
    UInt32 dwFileDateLS;
  }

  [StructLayout (LayoutKind.Sequential)]
  struct MINIDUMP_MODULE_CALLBACK
  {
    [MarshalAs(UnmanagedType.LPWStr)]
    String                      FullPath;
    UInt64                      BaseOfImage;
    UInt32                      SizeOfImage;
    UInt32                      CheckSum;
    UInt32                      TimeDateStamp;
    VS_FIXEDFILEINFO            VersionInfo;
    IntPtr                      CvRecord;
    UInt32                      SizeOfCvRecord;
    IntPtr                      MiscRecord;
    UInt32                      SizeOfMiscRecord;
  }

Use this code to define a structure with union in 32 bits:

[StructLayout(LayoutKind.Explicit)]
internal struct MINIDUMP_CALLBACK_INPUT 
{
  [FieldOffset(0)]
  UInt32 ProcessId;

  [FieldOffset(4)]
  IntPtr ProcessHandle;

  [FieldOffset(8)]
  UInt32 CallbackType;

  [FieldOffset(12)]
  MINIDUMP_THREAD_CALLBACK Thread;
  [FieldOffset(12)]
  MINIDUMP_THREAD_EX_CALLBACK ThreadEx;
  [FieldOffset(12)]
  MINIDUMP_MODULE_CALLBACK Module;
  [FieldOffset(12)]
  MINIDUMP_INCLUDE_THREAD_CALLBACK IncludeThread;
  [FieldOffset(12)]
  MINIDUMP_INCLUDE_MODULE_CALLBACK IncludeModule;
};

I think that on a 64 bits platform, the offsets should be respectively { 0, 8, 16, 24 } or { 0, 4, 12, 16 } whether ULONG is 64 or 32 bits.


Edit

I think you can use the MarshalAsAttribute to specifically convert fields:

  [StructLayout(LayoutKind.Sequential)]
  struct VS_FIXEDFILEINFO
  {
    UInt32 dwSignature;
    UInt32 dwStrucVersion;
    UInt32 dwFileVersionMS;
    UInt32 dwFileVersionLS;
    UInt32 dwProductVersionMS;
    UInt32 dwProductVersionLS;
    UInt32 dwFileFlagsMask;
    UInt32 dwFileFlags;
    UInt32 dwFileOS;
    UInt32 dwFileType;
    UInt32 dwFileSubtype;
    UInt32 dwFileDateMS;
    UInt32 dwFileDateLS;
  }

  [StructLayout (LayoutKind.Sequential)]
  struct MINIDUMP_MODULE_CALLBACK
  {
    [MarshalAs(UnmanagedType.LPWStr)]
    String                      FullPath;
    UInt64                      BaseOfImage;
    UInt32                      SizeOfImage;
    UInt32                      CheckSum;
    UInt32                      TimeDateStamp;
    VS_FIXEDFILEINFO            VersionInfo;
    IntPtr                      CvRecord;
    UInt32                      SizeOfCvRecord;
    IntPtr                      MiscRecord;
    UInt32                      SizeOfMiscRecord;
  }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文