How to capture a process dump from PowerShell with no dependencies
Overview
The origin of this snippet is that I found myself wanting to write a script to collect some logs from bug filers, including process dumps of certain key processes. There are tools that can capture dumps but they require installing some sort of program onto the user's device, which adds friction. Examples include windbg or procmon.
This one took a surprisingly long time to figure out. The key to making this work is that PowerShell can embed C# code within a script, and C# has the ability to interop with Win32 APIs.
Add-Type @"
using System;
using System.IO;
using System.Runtime.InteropServices;
public static class DumpHelper
{
public static class MINIDUMPTYPE
{
public const int MiniDumpNormal = 0x00000000;
public const int MiniDumpWithDataSegs = 0x00000001;
public const int MiniDumpWithFullMemory = 0x00000002;
public const int MiniDumpWithHandleData = 0x00000004;
public const int MiniDumpFilterMemory = 0x00000008;
public const int MiniDumpScanMemory = 0x00000010;
public const int MiniDumpWithUnloadedModules = 0x00000020;
public const int MiniDumpWithIndirectlyReferencedMemory = 0x00000040;
public const int MiniDumpFilterModulePaths = 0x00000080;
public const int MiniDumpWithProcessThreadData = 0x00000100;
public const int MiniDumpWithPrivateReadWriteMemory = 0x00000200;
public const int MiniDumpWithoutOptionalData = 0x00000400;
public const int MiniDumpWithFullMemoryInfo = 0x00000800;
public const int MiniDumpWithThreadInfo = 0x00001000;
public const int MiniDumpWithCodeSegs = 0x00002000;
public const int MiniDumpWithoutAuxiliaryState = 0x00004000;
public const int MiniDumpWithFullAuxiliaryState = 0x00008000;
public const int MiniDumpWithPrivateWriteCopyMemory = 0x00010000;
public const int MiniDumpIgnoreInaccessibleMemory = 0x00020000;
public const int MiniDumpWithTokenInformation = 0x00040000;
public const int MiniDumpWithModuleHeaders = 0x00080000;
public const int MiniDumpFilterTriage = 0x00100000;
public const int MiniDumpWithAvxXStateContext = 0x00200000;
public const int MiniDumpWithIptTrace = 0x00400000;
public const int MiniDumpScanInaccessiblePartialPages = 0x00800000;
// public const int MiniDumpFilterWriteCombinedMemory;
public const int MiniDumpValidTypeFlags = 0x01ffffff;
}
[DllImport("dbghelp.dll")]
public static extern bool MiniDumpWriteDump(IntPtr hProcess,
Int32 ProcessId,
IntPtr hFile,
int DumpType,
IntPtr ExceptionParam,
IntPtr UserStreamParam,
IntPtr CallbackParam);
public static void CreateMiniDump(String dumpFilePath, IntPtr processHandle, Int32 processId)
{
var stream = new System.IO.FileStream(dumpFilePath, System.IO.FileMode.Create);
MiniDumpWriteDump(processHandle, processId, stream.SafeFileHandle.DangerousGetHandle(), MINIDUMPTYPE.MiniDumpNormal, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
stream.Close();
}
}
"@;
# Example Usage
$processInfo = Get-Process -Name "notepad"; # Assumes a single instance is running, will overwrite existing files
[string]$dumpName = "$($env:temp)\notepad.dmp";
[DumpHelper]::CreateMiniDump($dumpName, $processInfo.Handle, $processInfo.Id);