diff --git a/PInvoke/DbgHelp/MiniDump.cs b/PInvoke/DbgHelp/MiniDump.cs
index c50c4069..c3aa89a4 100644
--- a/PInvoke/DbgHelp/MiniDump.cs
+++ b/PInvoke/DbgHelp/MiniDump.cs
@@ -732,6 +732,77 @@ namespace Vanara.PInvoke
[In, Optional] in MINIDUMP_EXCEPTION_INFORMATION ExceptionParam, [In, Optional] in MINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
[In, Optional] in MINIDUMP_CALLBACK_INFORMATION CallbackParam);
+ /// Writes user-mode minidump information to the specified file.
+ ///
+ /// A handle to the process for which the information is to be generated.
+ ///
+ /// This handle must have PROCESS_QUERY_INFORMATION and PROCESS_VM_READ access to the process. If handle information
+ /// is to be collected then PROCESS_DUP_HANDLE access is also required. For more information, see Process Security and Access
+ /// Rights. The caller must also be able to get THREAD_ALL_ACCESS access to the threads in the process. For more information,
+ /// see Thread Security and Access Rights.
+ ///
+ ///
+ /// The identifier of the process for which the information is to be generated.
+ /// A handle to the file in which the information is to be written.
+ ///
+ /// The type of information to be generated. This parameter can be one or more of the values from the MINIDUMP_TYPE enumeration.
+ ///
+ ///
+ /// A pointer to a MINIDUMP_EXCEPTION_INFORMATION structure describing the client exception that caused the minidump to be
+ /// generated. If the value of this parameter is NULL, no exception information is included in the minidump file.
+ ///
+ ///
+ /// A pointer to a MINIDUMP_USER_STREAM_INFORMATION structure. If the value of this parameter is NULL, no user-defined
+ /// information is included in the minidump file.
+ ///
+ ///
+ /// A pointer to a MINIDUMP_CALLBACK_INFORMATION structure that specifies a callback routine which is to receive extended minidump
+ /// information. If the value of this parameter is NULL, no callbacks are performed.
+ ///
+ ///
+ ///
+ /// If the function succeeds, the return value is TRUE; otherwise, the return value is FALSE. To retrieve extended
+ /// error information, call GetLastError. Note that the last error will be an HRESULT value.
+ ///
+ /// If the operation is canceled, the last error code is
+ /// HRESULT_FROM_WIN32(ERROR_CANCELLED)
+ /// .
+ ///
+ ///
+ ///
+ ///
+ /// The MiniDumpCallback function receives extended minidump information from MiniDumpWriteDump. It also provides a way for
+ /// the caller to determine the granularity of information written to the minidump file, as the callback function can filter the
+ /// default information.
+ ///
+ ///
+ /// MiniDumpWriteDump should be called from a separate process if at all possible, rather than from within the target process
+ /// being dumped. This is especially true when the target process is already not stable. For example, if it just crashed. A loader
+ /// deadlock is one of many potential side effects of calling MiniDumpWriteDump from within the target process.
+ ///
+ ///
+ /// MiniDumpWriteDump may not produce a valid stack trace for the calling thread. To work around this problem, you must
+ /// capture the state of the calling thread before calling MiniDumpWriteDump and use it as the ExceptionParam parameter. One
+ /// way to do this is to force an exception inside a __try/ __except block and use the EXCEPTION_POINTERS information
+ /// provided by GetExceptionInformation. Alternatively, you can call the function from a new worker thread and filter this worker
+ /// thread from the dump.
+ ///
+ ///
+ /// All DbgHelp functions, such as this one, are single threaded. Therefore, calls from more than one thread to this function will
+ /// likely result in unexpected behavior or memory corruption. To avoid this, you must synchronize all concurrent calls from more
+ /// than one thread to this function.
+ ///
+ ///
+ // https://docs.microsoft.com/en-us/windows/win32/api/minidumpapiset/nf-minidumpapiset-minidumpwritedump BOOL MiniDumpWriteDump(
+ // HANDLE hProcess, DWORD ProcessId, HANDLE hFile, MINIDUMP_TYPE DumpType, PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
+ // PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, PMINIDUMP_CALLBACK_INFORMATION CallbackParam );
+ [DllImport(Lib_DbgHelp, SetLastError = true, ExactSpelling = true)]
+ [PInvokeData("minidumpapiset.h", MSDNShortId = "NF:minidumpapiset.MiniDumpWriteDump")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern unsafe bool MiniDumpWriteDump(HPROCESS hProcess, uint ProcessId, HFILE hFile, MINIDUMP_TYPE DumpType,
+ [In, Optional] in MINIDUMP_EXCEPTION_INFORMATION ExceptionParam, [In, Optional] IntPtr UserStreamParam,
+ [In, Optional] IntPtr CallbackParam);
+
/// Writes user-mode minidump information to the specified file.
///
/// A handle to the process for which the information is to be generated.
diff --git a/UnitTests/PInvoke/DbgHelp/DbgHelpTests.cs b/UnitTests/PInvoke/DbgHelp/DbgHelpTests.cs
index 3f02023a..0ec1cb27 100644
--- a/UnitTests/PInvoke/DbgHelp/DbgHelpTests.cs
+++ b/UnitTests/PInvoke/DbgHelp/DbgHelpTests.cs
@@ -192,53 +192,62 @@ namespace Vanara.PInvoke.Tests
[Test]
public void MiniDumpCallbackOrderTest()
{
- var memCallbackCalled = false;
- using var hFile = CreateFile("CallbackOrder.dmp", Kernel32.FileAccess.GENERIC_READ | Kernel32.FileAccess.GENERIC_WRITE, 0, default, FileMode.Create, FileFlagsAndAttributes.FILE_ATTRIBUTE_NORMAL);
- if (!hFile.IsInvalid)
+ try
{
- var mdei = new MINIDUMP_EXCEPTION_INFORMATION
- {
- ThreadId = GetCurrentThreadId(),
- ExceptionPointers = Marshal.GetExceptionPointers()
- };
-
- var mci = new MINIDUMP_CALLBACK_INFORMATION { CallbackRoutine = MyMiniDumpCallback };
-
- Assert.That(MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MINIDUMP_TYPE.MiniDumpNormal, mdei, default, mci), ResultIs.Successful);
+ // Load an exception into the system tables
+ throw new InvalidOperationException();
}
-
- bool MyMiniDumpCallback([In, Out] IntPtr CallbackParam, in MINIDUMP_CALLBACK_INPUT CallbackInput, ref MINIDUMP_CALLBACK_OUTPUT CallbackOutput)
+ catch
{
- TestContext.Write($"{CallbackInput.CallbackType} ");
- switch (CallbackInput.CallbackType)
+ // Test for debug exception info
+ var memCallbackCalled = false;
+ using var hFile = CreateFile("CallbackOrder.dmp", Kernel32.FileAccess.GENERIC_READ | Kernel32.FileAccess.GENERIC_WRITE, 0, default, FileMode.Create, FileFlagsAndAttributes.FILE_ATTRIBUTE_NORMAL);
+ if (!hFile.IsInvalid)
{
- case MINIDUMP_CALLBACK_TYPE.ModuleCallback:
- TestContext.WriteLine($"(module: {CallbackInput.Module.FullPath})");
- return true;
- case MINIDUMP_CALLBACK_TYPE.ThreadCallback:
- TestContext.WriteLine($"(thread: {CallbackInput.Thread.ThreadId:X})");
- return true;
- case MINIDUMP_CALLBACK_TYPE.ThreadExCallback:
- TestContext.WriteLine($"(thread: {CallbackInput.ThreadEx.ThreadId:X})");
- return true;
- case MINIDUMP_CALLBACK_TYPE.IncludeThreadCallback:
- TestContext.WriteLine($"(thread: {CallbackInput.IncludeThread.ThreadId:X})");
- return true;
- case MINIDUMP_CALLBACK_TYPE.IncludeModuleCallback:
- TestContext.WriteLine($"(module: {CallbackInput.IncludeModule.BaseOfImage:X})");
- return true;
- case MINIDUMP_CALLBACK_TYPE.MemoryCallback:
- memCallbackCalled = true;
- TestContext.WriteLine("");
- return false;
- case MINIDUMP_CALLBACK_TYPE.CancelCallback:
- CallbackOutput.Cancel = false;
- CallbackOutput.CheckCancel = !memCallbackCalled;
- TestContext.WriteLine("");
- return true;
- default:
- TestContext.WriteLine("");
- return false;
+ var mdei = new MINIDUMP_EXCEPTION_INFORMATION
+ {
+ ThreadId = GetCurrentThreadId(),
+ ExceptionPointers = Marshal.GetExceptionPointers()
+ };
+
+ var mci = new MINIDUMP_CALLBACK_INFORMATION { CallbackRoutine = MyMiniDumpCallback };
+
+ Assert.That(MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MINIDUMP_TYPE.MiniDumpNormal, mdei, default, mci), ResultIs.Successful);
+ }
+
+ bool MyMiniDumpCallback([In, Out] IntPtr CallbackParam, in MINIDUMP_CALLBACK_INPUT CallbackInput, ref MINIDUMP_CALLBACK_OUTPUT CallbackOutput)
+ {
+ TestContext.Write($"{CallbackInput.CallbackType} ");
+ switch (CallbackInput.CallbackType)
+ {
+ case MINIDUMP_CALLBACK_TYPE.ModuleCallback:
+ TestContext.WriteLine($"(module: {CallbackInput.Module.FullPath})");
+ return true;
+ case MINIDUMP_CALLBACK_TYPE.ThreadCallback:
+ TestContext.WriteLine($"(thread: {CallbackInput.Thread.ThreadId:X})");
+ return true;
+ case MINIDUMP_CALLBACK_TYPE.ThreadExCallback:
+ TestContext.WriteLine($"(thread: {CallbackInput.ThreadEx.ThreadId:X})");
+ return true;
+ case MINIDUMP_CALLBACK_TYPE.IncludeThreadCallback:
+ TestContext.WriteLine($"(thread: {CallbackInput.IncludeThread.ThreadId:X})");
+ return true;
+ case MINIDUMP_CALLBACK_TYPE.IncludeModuleCallback:
+ TestContext.WriteLine($"(module: {CallbackInput.IncludeModule.BaseOfImage:X})");
+ return true;
+ case MINIDUMP_CALLBACK_TYPE.MemoryCallback:
+ memCallbackCalled = true;
+ TestContext.WriteLine("");
+ return false;
+ case MINIDUMP_CALLBACK_TYPE.CancelCallback:
+ CallbackOutput.Cancel = false;
+ CallbackOutput.CheckCancel = !memCallbackCalled;
+ TestContext.WriteLine("");
+ return true;
+ default:
+ TestContext.WriteLine("");
+ return false;
+ }
}
}
}