Files
mirror-processhacker/2.x/trunk/ProcessHacker/mdump.c
T
wj32 442f83f50d use fallback minidump flags with old versions of dbghelp.dll
git-svn-id: svn://svn.code.sf.net/p/processhacker/code@4154 21ef857c-d57f-4fe0-8362-d861dc6d29cd
2011-03-24 03:37:32 +00:00

379 lines
10 KiB
C

/*
* Process Hacker -
* minidump writer
*
* Copyright (C) 2010-2011 wj32
*
* This file is part of Process Hacker.
*
* Process Hacker is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Process Hacker is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
*/
#include <phapp.h>
#include <symprv.h>
#define WM_PH_MINIDUMP_STATUS_UPDATE (WM_APP + 301)
#define PH_MINIDUMP_STATUS_UPDATE 1
#define PH_MINIDUMP_COMPLETED 2
#define PH_MINIDUMP_ERROR 3
typedef struct _PROCESS_MINIDUMP_CONTEXT
{
HANDLE ProcessId;
PWSTR FileName;
MINIDUMP_TYPE DumpType;
HANDLE ProcessHandle;
HANDLE FileHandle;
HWND WindowHandle;
HANDLE ThreadHandle;
BOOLEAN Stop;
BOOLEAN Succeeded;
ULONG LastTickCount;
} PROCESS_MINIDUMP_CONTEXT, *PPROCESS_MINIDUMP_CONTEXT;
BOOLEAN PhpCreateProcessMiniDumpWithProgress(
__in HWND hWnd,
__in HANDLE ProcessId,
__in PWSTR FileName,
__in MINIDUMP_TYPE DumpType
);
INT_PTR CALLBACK PhpProcessMiniDumpDlgProc(
__in HWND hwndDlg,
__in UINT uMsg,
__in WPARAM wParam,
__in LPARAM lParam
);
BOOLEAN PhUiCreateDumpFileProcess(
__in HWND hWnd,
__in PPH_PROCESS_ITEM Process
)
{
static PH_FILETYPE_FILTER filters[] =
{
{ L"Dump files (*.dmp)", L"*.dmp" },
{ L"All files (*.*)", L"*.*" }
};
PVOID fileDialog;
PPH_STRING fileName;
fileDialog = PhCreateSaveFileDialog();
PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER));
PhSetFileDialogFileName(fileDialog, PhaConcatStrings2(Process->ProcessName->Buffer, L".dmp")->Buffer);
if (!PhShowFileDialog(hWnd, fileDialog))
{
PhFreeFileDialog(fileDialog);
return FALSE;
}
fileName = PHA_DEREFERENCE(PhGetFileDialogFileName(fileDialog));
PhFreeFileDialog(fileDialog);
return PhpCreateProcessMiniDumpWithProgress(
hWnd,
Process->ProcessId,
fileName->Buffer,
// task manager uses these flags
MiniDumpWithFullMemory |
MiniDumpWithHandleData |
MiniDumpWithUnloadedModules |
MiniDumpWithFullMemoryInfo |
MiniDumpWithThreadInfo
);
}
BOOLEAN PhpCreateProcessMiniDumpWithProgress(
__in HWND hWnd,
__in HANDLE ProcessId,
__in PWSTR FileName,
__in MINIDUMP_TYPE DumpType
)
{
NTSTATUS status;
PROCESS_MINIDUMP_CONTEXT context;
context.ProcessId = ProcessId;
context.FileName = FileName;
context.DumpType = DumpType;
context.Stop = FALSE;
context.Succeeded = FALSE;
if (!NT_SUCCESS(status = PhOpenProcess(
&context.ProcessHandle,
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
ProcessId
)))
{
PhShowStatus(hWnd, L"Unable to open the process", status, 0);
return FALSE;
}
status = PhCreateFileWin32(
&context.FileHandle,
FileName,
FILE_GENERIC_WRITE,
0,
0,
FILE_OVERWRITE_IF,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
);
if (!NT_SUCCESS(status))
{
PhShowStatus(hWnd, L"Unable to access the dump file", status, 0);
NtClose(context.ProcessHandle);
return FALSE;
}
DialogBoxParam(
PhInstanceHandle,
MAKEINTRESOURCE(IDD_PROGRESS),
hWnd,
PhpProcessMiniDumpDlgProc,
(LPARAM)&context
);
NtClose(context.FileHandle);
NtClose(context.ProcessHandle);
return context.Succeeded;
}
static BOOL CALLBACK PhpProcessMiniDumpCallback(
__in PVOID CallbackParam,
__in const PMINIDUMP_CALLBACK_INPUT CallbackInput,
__inout PMINIDUMP_CALLBACK_OUTPUT CallbackOutput
)
{
PPROCESS_MINIDUMP_CONTEXT context = CallbackParam;
PPH_STRING message = NULL;
// Don't try to send status updates if we're creating a dump of the current process.
if (context->ProcessId == NtCurrentProcessId())
return TRUE;
// MiniDumpWriteDump seems to get bored of calling the callback
// after it begins dumping the process handles. The code is
// still here in case they fix this problem in the future.
switch (CallbackInput->CallbackType)
{
case CancelCallback:
{
if (context->Stop)
CallbackOutput->Cancel = TRUE;
}
break;
case ModuleCallback:
{
message = PhFormatString(L"Processing module %s...", CallbackInput->Module.FullPath);
}
break;
case ThreadCallback:
{
message = PhFormatString(L"Processing thread %u...", CallbackInput->Thread.ThreadId);
}
break;
}
if (message)
{
SendMessage(
context->WindowHandle,
WM_PH_MINIDUMP_STATUS_UPDATE,
PH_MINIDUMP_STATUS_UPDATE,
(LPARAM)message->Buffer
);
PhDereferenceObject(message);
}
return TRUE;
}
NTSTATUS PhpProcessMiniDumpThreadStart(
__in PVOID Parameter
)
{
PPROCESS_MINIDUMP_CONTEXT context = Parameter;
MINIDUMP_CALLBACK_INFORMATION callbackInfo;
callbackInfo.CallbackRoutine = PhpProcessMiniDumpCallback;
callbackInfo.CallbackParam = context;
if (PhWriteMiniDumpProcess(
context->ProcessHandle,
context->ProcessId,
context->FileHandle,
context->DumpType,
NULL,
NULL,
&callbackInfo
))
{
context->Succeeded = TRUE;
}
else
{
// We may have an old version of dbghelp - in that case, try
// using minimal dump flags.
if (GetLastError() == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER) && PhWriteMiniDumpProcess(
context->ProcessHandle,
context->ProcessId,
context->FileHandle,
MiniDumpWithFullMemory | MiniDumpWithHandleData,
NULL,
NULL,
&callbackInfo
))
{
context->Succeeded = TRUE;
}
else
{
SendMessage(
context->WindowHandle,
WM_PH_MINIDUMP_STATUS_UPDATE,
PH_MINIDUMP_ERROR,
(LPARAM)GetLastError()
);
}
}
SendMessage(
context->WindowHandle,
WM_PH_MINIDUMP_STATUS_UPDATE,
PH_MINIDUMP_COMPLETED,
0
);
return STATUS_SUCCESS;
}
INT_PTR CALLBACK PhpProcessMiniDumpDlgProc(
__in HWND hwndDlg,
__in UINT uMsg,
__in WPARAM wParam,
__in LPARAM lParam
)
{
switch (uMsg)
{
case WM_INITDIALOG:
{
PPROCESS_MINIDUMP_CONTEXT context = (PPROCESS_MINIDUMP_CONTEXT)lParam;
PhCenterWindow(hwndDlg, GetParent(hwndDlg));
SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context);
SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT, L"Creating the dump file...");
PhSetWindowStyle(GetDlgItem(hwndDlg, IDC_PROGRESS), PBS_MARQUEE, PBS_MARQUEE);
SendMessage(GetDlgItem(hwndDlg, IDC_PROGRESS), PBM_SETMARQUEE, TRUE, 75);
context->WindowHandle = hwndDlg;
context->ThreadHandle = PhCreateThread(0, PhpProcessMiniDumpThreadStart, context);
if (!context->ThreadHandle)
{
PhShowStatus(hwndDlg, L"Unable to create the minidump thread", 0, GetLastError());
EndDialog(hwndDlg, IDCANCEL);
}
SetTimer(hwndDlg, 1, 500, NULL);
}
break;
case WM_DESTROY:
{
PPROCESS_MINIDUMP_CONTEXT context;
context = (PPROCESS_MINIDUMP_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom());
NtClose(context->ThreadHandle);
RemoveProp(hwndDlg, PhMakeContextAtom());
}
break;
case WM_COMMAND:
{
switch (LOWORD(wParam))
{
case IDCANCEL:
{
PPROCESS_MINIDUMP_CONTEXT context =
(PPROCESS_MINIDUMP_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom());
EnableWindow(GetDlgItem(hwndDlg, IDCANCEL), FALSE);
context->Stop = TRUE;
}
break;
}
}
break;
case WM_TIMER:
{
if (wParam == 1)
{
PPROCESS_MINIDUMP_CONTEXT context =
(PPROCESS_MINIDUMP_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom());
ULONG currentTickCount;
currentTickCount = GetTickCount();
if (currentTickCount - context->LastTickCount >= 2000)
{
// No status message update for 2 seconds.
SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT,
(PWSTR)L"Creating the dump file...");
InvalidateRect(GetDlgItem(hwndDlg, IDC_PROGRESSTEXT), NULL, FALSE);
context->LastTickCount = currentTickCount;
}
}
}
break;
case WM_PH_MINIDUMP_STATUS_UPDATE:
{
PPROCESS_MINIDUMP_CONTEXT context;
context = (PPROCESS_MINIDUMP_CONTEXT)GetProp(hwndDlg, PhMakeContextAtom());
switch (wParam)
{
case PH_MINIDUMP_STATUS_UPDATE:
SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT, (PWSTR)lParam);
InvalidateRect(GetDlgItem(hwndDlg, IDC_PROGRESSTEXT), NULL, FALSE);
context->LastTickCount = GetTickCount();
break;
case PH_MINIDUMP_ERROR:
PhShowStatus(hwndDlg, L"Unable to create the minidump", 0, (ULONG)lParam);
break;
case PH_MINIDUMP_COMPLETED:
EndDialog(hwndDlg, IDOK);
break;
}
}
break;
}
return FALSE;
}