使用回调调用MiniDumpWriteDump

时间:2009-09-01 10:54:21

标签: c# .net dllimport minidump

我想使用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

3 个答案:

答案 0 :(得分:2)

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

你知道你所需要的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方法,该方法在当前进程中为未处理的异常注册内部过滤器。如果进程因未处理的异常(可能是本机异常或托管异常)崩溃,则过滤器会捕获它并创建一个minidump(具有指定的文件名)。以下是此示例代码:

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);

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

答案 1 :(得分:1)

使用此代码定义一个32位的union结构:

[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位还是32位,偏移量应分别为{0,8,16,24}或{0,4,12,16}。


修改

我认为您可以使用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;
  }

答案 2 :(得分:0)

我认为正是工会给你带来了麻烦?

如果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总是一个值,你可以只编写一个结构,但我不知道是否是这种情况。

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