问题
I've got a System.Diagnostics.Process
object. My C# program is monitoring it for some condition. When the condition is hit, I want to take a full memory dump of the Process.
What is the best way to achieve this?
Is there a way that is provided by the CLR?
回答1:
You could use ProcDump from Sysinternals and make your C# program call it when needed.
Process.Start("procdump " + otherProgramPID.ToString());
回答2:
You can try calling the C++ method from your C# code. Here's an example of how you would declare it:
[DllImport("dbghelp.dll",
EntryPoint = "MiniDumpWriteDump",
CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Unicode,
ExactSpelling = true,
SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool MiniDumpWriteDump(
IntPtr hProcess,
uint processId,
SafeHandle hFile,
MINIDUMP_TYPE dumpType,
IntPtr expParam,
IntPtr userStreamParam,
IntPtr callbackParam);
You can check out the code of this project for an example: https://github.com/projectkudu/kudu/blob/master/Kudu.Core/Infrastructure/MiniDumpNativeMethods.cs https://github.com/projectkudu/kudu/blob/2db563be679bb60656050ec3f04945086f07b360/Kudu.Core/Infrastructure/ProcessExtensions.cs
回答3:
Microsofts MSDN lists a code example on how to achieve this using C# code within your own application:
using System;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
// !load \\ddelementary\autowatson\vsdbg\vsdbg.dll
namespace CreateMiniDump
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Loaded += (ol, el) =>
{
IntPtr hFile = IntPtr.Zero;
try
{
if (IntPtr.Size == 4)
{
this.Title = "CreateMiniDump Running as 32 bit, creating 32 bit dumps";
}
else
{
this.Title = "CreateMiniDump Running as 64 bit, creating 64 bit dumps";
}
this.Height = 800;
this.Width = 800;
var sp = new StackPanel() { Orientation = Orientation.Vertical };
this.Content = sp;
var dumpFileName = System.IO.Path.Combine(
System.IO.Path.GetTempPath(),
"testdump.dmp");
var txtDumpFile = new TextBox()
{
Text = dumpFileName
};
sp.Children.Add(txtDumpFile);
var txtProcName = new TextBox()
{
ToolTip = "Process name without extension",
Text = "devenv"
};
sp.Children.Add(txtProcName);
var lstDumpType = new ListBox()
{
// allow multi select
SelectionMode = SelectionMode.Extended
};
lstDumpType.ItemsSource = Enum.GetValues(typeof(NativeMethods._MINIDUMP_TYPE));
// set initial value
// for a dump with memory info we want these:
foreach (var val in new[] {
NativeMethods._MINIDUMP_TYPE.MiniDumpWithFullMemory,
NativeMethods._MINIDUMP_TYPE.MiniDumpWithFullMemoryInfo,
NativeMethods._MINIDUMP_TYPE.MiniDumpWithHandleData,
NativeMethods._MINIDUMP_TYPE.MiniDumpWithThreadInfo
})
{
var nval = (int)(Math.Log((int)val) / Math.Log(2)) + 1;
lstDumpType.SelectedItems.Add(
lstDumpType.Items[nval]
);
}
sp.Children.Add(lstDumpType);
var btnGo = new Button()
{
Content = "_Create Dump",
Width = 200
};
sp.Children.Add(btnGo);
var txtStatus = new TextBox()
{
IsUndoEnabled = false,
VerticalScrollBarVisibility = ScrollBarVisibility.Auto,
MaxHeight = 400
};
sp.Children.Add(txtStatus);
btnGo.Click += (ob, eb) =>
{
try
{
var sw = new Stopwatch();
sw.Start();
dumpFileName = txtDumpFile.Text.Trim();
if (System.IO.File.Exists(dumpFileName))
{
System.IO.File.Delete(dumpFileName);
}
hFile = NativeMethods.CreateFile(
dumpFileName,
NativeMethods.EFileAccess.GenericWrite,
NativeMethods.EFileShare.None,
lpSecurityAttributes: IntPtr.Zero,
dwCreationDisposition: NativeMethods.ECreationDisposition.CreateAlways,
dwFlagsAndAttributes: NativeMethods.EFileAttributes.Normal,
hTemplateFile: IntPtr.Zero
);
if (hFile == NativeMethods.INVALID_HANDLE_VALUE)
{
var hr = Marshal.GetHRForLastWin32Error();
var ex = Marshal.GetExceptionForHR(hr);
throw ex;
}
NativeMethods._MINIDUMP_TYPE dumpType = NativeMethods._MINIDUMP_TYPE.MiniDumpNormal; // 0
foreach (var item in lstDumpType.SelectedItems)
{
var dt = (NativeMethods._MINIDUMP_TYPE)item;
dumpType |= dt;
}
var exceptInfo = new NativeMethods.MINIDUMP_EXCEPTION_INFORMATION();
var proc = Process.GetProcessesByName(
txtProcName.Text.Trim()
).FirstOrDefault();
if (!proc.Is32BitProcess() && IntPtr.Size == 4)
{
throw new InvalidOperationException(
"Can't create 32 bit dump of 64 bit process"
);
}
var result = NativeMethods.MiniDumpWriteDump(
proc.Handle,
proc.Id,
hFile,
dumpType,
ref exceptInfo,
UserStreamParam: IntPtr.Zero,
CallbackParam: IntPtr.Zero
);
if (result == false)
{
var hr = Marshal.GetHRForLastWin32Error();
var ex = Marshal.GetExceptionForHR(hr);
throw ex;
}
txtStatus.Text = string.Format(
"Dump Created. Pid= {0} {1}\r File size = {2:n0}\rElapsed = {3:n3}\r",
proc.Id,
dumpType,
(new System.IO.FileInfo(dumpFileName)).Length,
sw.Elapsed.TotalSeconds
);
}
catch (Exception ex)
{
txtStatus.Text = ex.ToString();
}
finally
{
NativeMethods.CloseHandle(hFile);
}
};
}
catch (Exception ex)
{
this.Content = ex.ToString();
}
};
}
}
public static class ExtensionMethods
{
public static bool Is32BitProcess(this Process proc)
{
bool fIs32bit = false;
// if we're runing on 32bit, default to true
if (IntPtr.Size == 4)
{
fIs32bit = true;
}
bool fIsRunningUnderWow64 = false;
// if machine is 32 bit then all procs are 32 bit
if (NativeMethods.IsWow64Process(NativeMethods.GetCurrentProcess(), out fIsRunningUnderWow64)
&& fIsRunningUnderWow64)
{
// current OS is 64 bit
if (NativeMethods.IsWow64Process(proc.Handle, out fIsRunningUnderWow64)
&& fIsRunningUnderWow64)
{
fIs32bit = true;
}
else
{
fIs32bit = false;
}
}
return fIs32bit;
}
}
public static partial class NativeMethods
{
[DllImport("Dbghelp.dll")]
public static extern bool MiniDumpWriteDump(
IntPtr hProcess,
int ProcessId,
IntPtr hFile,
_MINIDUMP_TYPE DumpType,
ref MINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
IntPtr UserStreamParam,
IntPtr CallbackParam
);
//https://msdn.microsoft.com/en-us/library/windows/desktop/ms680519%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
[Flags]
public enum _MINIDUMP_TYPE
{
MiniDumpNormal = 0x00000000,
MiniDumpWithDataSegs = 0x00000001,
MiniDumpWithFullMemory = 0x00000002,
MiniDumpWithHandleData = 0x00000004,
MiniDumpFilterMemory = 0x00000008,
MiniDumpScanMemory = 0x00000010,
MiniDumpWithUnloadedModules = 0x00000020,
MiniDumpWithIndirectlyReferencedMemory = 0x00000040,
MiniDumpFilterModulePaths = 0x00000080,
MiniDumpWithProcessThreadData = 0x00000100,
MiniDumpWithPrivateReadWriteMemory = 0x00000200,
MiniDumpWithoutOptionalData = 0x00000400,
MiniDumpWithFullMemoryInfo = 0x00000800,
MiniDumpWithThreadInfo = 0x00001000,
MiniDumpWithCodeSegs = 0x00002000,
MiniDumpWithoutAuxiliaryState = 0x00004000,
MiniDumpWithFullAuxiliaryState = 0x00008000,
MiniDumpWithPrivateWriteCopyMemory = 0x00010000,
MiniDumpIgnoreInaccessibleMemory = 0x00020000,
MiniDumpWithTokenInformation = 0x00040000,
MiniDumpWithModuleHeaders = 0x00080000,
MiniDumpFilterTriage = 0x00100000,
MiniDumpValidTypeFlags = 0x001fffff,
};
/*
*
https://msdn.microsoft.com/en-us/library/windows/desktop/bb513622(v=vs.85).aspx
WER_DUMP_TYPE:
WerDumpTypeHeapDump
MiniDumpWithDataSegs
MiniDumpWithProcessThreadData
MiniDumpWithHandleData
MiniDumpWithPrivateReadWriteMemory
MiniDumpWithUnloadedModules
MiniDumpWithFullMemoryInfo
MiniDumpWithThreadInfo (Windows 7 and later)
MiniDumpWithTokenInformation (Windows 7 and later)
MiniDumpWithPrivateWriteCopyMemory
WerDumpTypeMicroDump
MiniDumpWithDataSegs
MiniDumpWithUnloadedModules
MiniDumpWithProcessThreadData
MiniDumpWithoutOptionalData
WerDumpTypeMiniDump
MiniDumpWithDataSegs
MiniDumpWithUnloadedModules
MiniDumpWithProcessThreadData
MiniDumpWithTokenInformation
*/
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct MINIDUMP_EXCEPTION_INFORMATION
{
public uint ThreadId;
public IntPtr ExceptionPointers;
public int ClientPointers;
}
[DllImport("kernel32.dll",
SetLastError = true,
CharSet = CharSet.Auto)]
public static extern IntPtr CreateFile(
string lpFileName,
EFileAccess dwDesiredAccess,
EFileShare dwShareMode,
IntPtr lpSecurityAttributes,
ECreationDisposition dwCreationDisposition,
EFileAttributes dwFlagsAndAttributes,
IntPtr hTemplateFile
);
[DllImport("kernel32.dll",
SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(IntPtr hObject);
public static IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
[Flags]
public enum EFileAccess : uint
{
//
// Standart Section
//
AccessSystemSecurity = 0x1000000, // AccessSystemAcl access type
MaximumAllowed = 0x2000000, // MaximumAllowed access type
Delete = 0x10000,
ReadControl = 0x20000,
WriteDAC = 0x40000,
WriteOwner = 0x80000,
Synchronize = 0x100000,
StandardRightsRequired = 0xF0000,
StandardRightsRead = ReadControl,
StandardRightsWrite = ReadControl,
StandardRightsExecute = ReadControl,
StandardRightsAll = 0x1F0000,
SpecificRightsAll = 0xFFFF,
FILE_READ_DATA = 0x0001, // file & pipe
FILE_LIST_DIRECTORY = 0x0001, // directory
FILE_WRITE_DATA = 0x0002, // file & pipe
FILE_ADD_FILE = 0x0002, // directory
FILE_APPEND_DATA = 0x0004, // file
FILE_ADD_SUBDIRECTORY = 0x0004, // directory
FILE_CREATE_PIPE_INSTANCE = 0x0004, // named pipe
FILE_READ_EA = 0x0008, // file & directory
FILE_WRITE_EA = 0x0010, // file & directory
FILE_EXECUTE = 0x0020, // file
FILE_TRAVERSE = 0x0020, // directory
FILE_DELETE_CHILD = 0x0040, // directory
FILE_READ_ATTRIBUTES = 0x0080, // all
FILE_WRITE_ATTRIBUTES = 0x0100, // all
//
// Generic Section
//
GenericRead = 0x80000000,
GenericWrite = 0x40000000,
GenericExecute = 0x20000000,
GenericAll = 0x10000000,
SPECIFIC_RIGHTS_ALL = 0x00FFFF,
FILE_ALL_ACCESS =
StandardRightsRequired |
Synchronize |
0x1FF,
FILE_GENERIC_READ =
StandardRightsRead |
FILE_READ_DATA |
FILE_READ_ATTRIBUTES |
FILE_READ_EA |
Synchronize,
FILE_GENERIC_WRITE =
StandardRightsWrite |
FILE_WRITE_DATA |
FILE_WRITE_ATTRIBUTES |
FILE_WRITE_EA |
FILE_APPEND_DATA |
Synchronize,
FILE_GENERIC_EXECUTE =
StandardRightsExecute |
FILE_READ_ATTRIBUTES |
FILE_EXECUTE |
Synchronize
}
[Flags]
public enum EFileShare : uint
{
/// <summary>
///
/// </summary>
None = 0x00000000,
/// <summary>
/// Enables subsequent open operations on an object to request read access.
/// Otherwise, other processes cannot open the object if they request read access.
/// If this flag is not specified, but the object has been opened for read access, the function fails.
/// </summary>
Read = 0x00000001,
/// <summary>
/// Enables subsequent open operations on an object to request write access.
/// Otherwise, other processes cannot open the object if they request write access.
/// If this flag is not specified, but the object has been opened for write access, the function fails.
/// </summary>
Write = 0x00000002,
/// <summary>
/// Enables subsequent open operations on an object to request delete access.
/// Otherwise, other processes cannot open the object if they request delete access.
/// If this flag is not specified, but the object has been opened for delete access, the function fails.
/// </summary>
Delete = 0x00000004
}
public enum ECreationDisposition : uint
{
/// <summary>
/// Creates a new file. The function fails if a specified file exists.
/// </summary>
New = 1,
/// <summary>
/// Creates a new file, always.
/// If a file exists, the function overwrites the file, clears the existing attributes, combines the specified file attributes,
/// and flags with FILE_ATTRIBUTE_ARCHIVE, but does not set the security descriptor that the SECURITY_ATTRIBUTES structure specifies.
/// </summary>
CreateAlways = 2,
/// <summary>
/// Opens a file. The function fails if the file does not exist.
/// </summary>
OpenExisting = 3,
/// <summary>
/// Opens a file, always.
/// If a file does not exist, the function creates a file as if dwCreationDisposition is CREATE_NEW.
/// </summary>
OpenAlways = 4,
/// <summary>
/// Opens a file and truncates it so that its size is 0 (zero) bytes. The function fails if the file does not exist.
/// The calling process must open the file with the GENERIC_WRITE access right.
/// </summary>
TruncateExisting = 5
}
[Flags]
public enum EFileAttributes : uint
{
Readonly = 0x00000001,
Hidden = 0x00000002,
System = 0x00000004,
Directory = 0x00000010,
Archive = 0x00000020,
Device = 0x00000040,
Normal = 0x00000080,
Temporary = 0x00000100,
SparseFile = 0x00000200,
ReparsePoint = 0x00000400,
Compressed = 0x00000800,
Offline = 0x00001000,
NotContentIndexed = 0x00002000,
Encrypted = 0x00004000,
Write_Through = 0x80000000,
Overlapped = 0x40000000,
NoBuffering = 0x20000000,
RandomAccess = 0x10000000,
SequentialScan = 0x08000000,
DeleteOnClose = 0x04000000,
BackupSemantics = 0x02000000,
PosixSemantics = 0x01000000,
OpenReparsePoint = 0x00200000,
OpenNoRecall = 0x00100000,
FirstPipeInstance = 0x00080000
}
[DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool IsWow64Process(
[In] IntPtr hProcess,
[Out, MarshalAs(UnmanagedType.Bool)] out bool wow64Process
);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetCurrentProcess();
}
}
来源:https://stackoverflow.com/questions/5880398/taking-memory-dump-using-c-sharp