|
|
/*++
Copyright (c) 1991-2000 Microsoft Corporation
Module Name:
savedump.c
Abstract:
This module contains the code to recover a dump from the system paging file.
Environment:
Kernel mode
Revision History:
--*/
#include <savedump.h>
//
// Read/write copy of dump header.
//
BOOL Test = FALSE;
/////////////////////////////////////////////////////////////////////////////
// API for Dr. Watson / other fault handlers to call
//
// FaultTypeToReport: the type of fault, Kernel, snapshot, or ?
// pwszDumpPath: a pointer to a string containing the dump or snapshot
//
// returns:
// S_OK on success
// some other system defined error code on failure
// ***************************************************************************
HRESULT PCHPFNotifyFault(EEventType FaultTypeToReport, LPWSTR pwszDumpPath, SEventInfoW *pEventInfo) { EFaultRepRetVal frrv; HMODULE hmodFaultRep; HRESULT hr = NOERROR; WCHAR wszDll[MAX_PATH];
GetWindowsDirectoryW(wszDll, sizeof(wszDll) / sizeof(WCHAR)); wcscat(wszDll, L"\\system32\\faultrep.dll");
frrv = frrvErrNoDW; hmodFaultRep = LoadLibraryExW(wszDll, NULL, 0); if (hmodFaultRep != NULL) { pfn_REPORTEREVENT pfn; pfn = (pfn_REPORTEREVENT)GetProcAddress(hmodFaultRep, "ReportEREvent");
if (pfn != NULL) { frrv = (*pfn)(FaultTypeToReport, pwszDumpPath, pEventInfo); }
FreeLibrary(hmodFaultRep); hmodFaultRep = NULL; }
hr = ((frrv != frrvErrNoDW) ? S_OK : HRESULT_FROM_WIN32(GetLastError()));
return hr;
}
BOOLEAN CreateMiniDump( IN PWSTR FullDumpName, IN PWSTR MiniDumpName ) { IDebugClient *DebugClient; PDEBUG_CONTROL DebugControl; HRESULT Hr; CHAR AnsiFullFileName[MAX_PATH]; CHAR AnsiMiniFileName[MAX_PATH];
if (WideCharToMultiByte(CP_ACP, 0, (PWSTR)FullDumpName, -1, AnsiFullFileName, sizeof(AnsiFullFileName), NULL, NULL) && WideCharToMultiByte(CP_ACP, 0, (PWSTR)MiniDumpName, -1, AnsiMiniFileName, sizeof(AnsiMiniFileName), NULL, NULL)) { if ((Hr = DebugCreate(__uuidof(IDebugClient), (void **)&DebugClient)) != S_OK) { KdPrint (("SAVEDUMP: cannot create DebugClientInterface\n")); } else { if (DebugClient->QueryInterface(__uuidof(IDebugControl), (void **)&DebugControl) == S_OK) { if (DebugClient->OpenDumpFile(AnsiFullFileName) == S_OK) { DebugControl->WaitForEvent(DEBUG_WAIT_DEFAULT, INFINITE);
if (DebugClient->WriteDumpFile(AnsiMiniFileName, DEBUG_DUMP_SMALL) == S_OK) { Hr = S_OK; } }
DebugControl->Release(); }
DebugClient->Release(); }
// Security descriptor issue.
//
//HANDLE MiniFileHandle;
//
//MiniFileHandle = CreateMyFile(miniFileName, FALSE, SecurityDescriptor);
//if (MiniFileHandle != INVALID_HANDLE_VALUE)
//{
// CloseHandle( fileHandle );
//}
}
return TRUE; }
NTSTATUS GetTargetFileNames( IN HKEY CrashControlKey, IN PWSTR MiniFilePath, IN PWSTR FullFilePath, IN ULONG DumpType ) { INT i; ULONG Status; ULONG Type; WCHAR FileName [ MAX_PATH + 1]; ULONG Length; BOOL AddWack; SYSTEMTIME Time; WCHAR DirName [ MAX_PATH ]; WCHAR ExpandedDirName [ MAX_PATH ];
//
// Minidump reads the directory from CrashControl\MiniDumpDir.
//
Length = sizeof (DirName); Status = RegQueryValueEx( CrashControlKey, L"MiniDumpDir", (LPDWORD) NULL, &Type, (LPBYTE) &DirName, &Length );
if (Status != ERROR_SUCCESS) {
//
// Set to default.
//
wcscpy (DirName, L"%SystemRoot%\\Minidump"); }
ExpandEnvironmentStrings ( DirName, ExpandedDirName, MAX_PATH);
//
// If directory does not exist, create it. Ignore errors here because
// they will be picked up later when we try to create the file.
//
CreateDirectory (ExpandedDirName, NULL);
//
// Format is: Mini-MM_DD_YY_HH_MM.dmp
//
GetLocalTime (&Time);
if ( ExpandedDirName [ wcslen ( ExpandedDirName ) - 1 ] != L'\\' ) { AddWack = TRUE; } else { AddWack = FALSE; }
for (i = 1; i < 100; i++) { swprintf (MiniFilePath, L"%s%sMini%2.2d%2.2d%2.2d-%2.2d.dmp", ExpandedDirName, AddWack ? L"\\" : L"", (int) Time.wMonth, (int) Time.wDay, (int) Time.wYear % 100, (int) i );
if (GetFileAttributes (MiniFilePath) == (DWORD) -1 && GetLastError () == ERROR_FILE_NOT_FOUND) {
break; }
}
//
// We failed to create a suitable file name; just fail.
//
if ( i == 100 ) { return STATUS_UNSUCCESSFUL; }
if (DumpType != DUMP_TYPE_TRIAGE) {
Length = sizeof (FileName); Status = RegQueryValueEx( CrashControlKey, L"DumpFile", (LPDWORD) NULL, &Type, (LPBYTE) &FileName, &Length );
if (Status != ERROR_SUCCESS) {
//
// Set to default.
//
wcscpy (FileName, L"%SystemRoot%\\MEMORY.DMP"); }
ExpandEnvironmentStrings (FileName, FullFilePath, MAX_PATH );
}
return STATUS_SUCCESS; }
VOID LogCrashDumpEvent( IN PUNICODE_STRING BugcheckString, IN PCWSTR SavedFileName, IN BOOL SuccessfullySavedDump ) { HANDLE LogHandle; LPWSTR StringArray[3]; WORD StringCount; DWORD EventId; BOOL Retry; DWORD Retries;
//
// Attempt to register the event source. Retry 20 times.
//
Retries = 0; do {
LogHandle = RegisterEventSource( (LPWSTR) NULL, L"Save Dump" );
//
// Retry on specific failures (server unavailable and interface
// unavailable).
//
if (LogHandle == NULL && Retries < 20 && ( GetLastError () == RPC_S_SERVER_UNAVAILABLE || GetLastError () == RPC_S_UNKNOWN_IF) ) {
Sleep ( 1500 ); Retry = TRUE; } else { Retry = FALSE; }
Retries++;
} while (LogHandle == NULL && Retry);
if (!LogHandle) { return ; } //
// Set up the parameters based on whether a full crash or summary
// was taken.
//
StringArray [ 0 ] = BugcheckString->Buffer; StringArray [ 1 ] = (PWSTR) SavedFileName;
//
// Report the appropriate event.
//
if (SuccessfullySavedDump) { EventId = EVENT_BUGCHECK_SAVED; StringCount = 2; } else { EventId = EVENT_BUGCHECK; StringCount = 2; } ReportEvent( LogHandle, EVENTLOG_INFORMATION_TYPE, 0, EventId, NULL, StringCount, 0, (LPCWSTR *)StringArray, NULL); }
VOID SendCrashDumpAlert( IN PUNICODE_STRING BugcheckString, IN PCWSTR SavedFileName, IN BOOL SuccessfullySavedDump ) { PADMIN_OTHER_INFO adminInfo; DWORD adminInfoSize; DWORD Length; DWORD i; ULONG winStatus; UCHAR VariableInfo [4096];
//
// Set up the administrator information variables for processing the
// buffer.
//
adminInfo = (PADMIN_OTHER_INFO) VariableInfo; adminInfoSize = sizeof( ADMIN_OTHER_INFO );
//
// Format the bugcheck information into the appropriate message format.
//
RtlCopyMemory( (LPWSTR) ((PCHAR) adminInfo + adminInfoSize), BugcheckString->Buffer, BugcheckString->Length ); adminInfoSize += BugcheckString->Length + sizeof( WCHAR );
//
// Set up the administrator alert information according to the type of
// dump that was taken.
//
if (SuccessfullySavedDump) { adminInfo->alrtad_errcode = ALERT_BugCheckSaved; adminInfo->alrtad_numstrings = 3; wcscpy( (LPWSTR) ((PCHAR) adminInfo + adminInfoSize), SavedFileName); adminInfoSize += ((wcslen( SavedFileName ) + 1) * sizeof( WCHAR )); } else { adminInfo->alrtad_errcode = ALERT_BugCheck; adminInfo->alrtad_numstrings = 2; }
//
// Get the name of the computer and insert it into the buffer.
//
Length = (sizeof( VariableInfo ) - adminInfoSize) / sizeof(WCHAR); winStatus = GetComputerName( (LPWSTR) ((PCHAR) adminInfo + adminInfoSize), &Length ); Length = ((Length + 1) * sizeof( WCHAR )); adminInfoSize += Length;
//
// Raise the alert.
//
i = 0;
do {
winStatus = NetAlertRaiseEx( ALERT_ADMIN_EVENT, adminInfo, adminInfoSize, L"SAVEDUMP" ); if (winStatus) { if (winStatus == ERROR_FILE_NOT_FOUND) { if (i++ > 20) { break; } if ((i & 3) == 0) { KdPrint (( "SAVEDUMP: Waiting for alerter...\n" )); }
Sleep( 15000 ); } } } while (winStatus == ERROR_FILE_NOT_FOUND); }
VOID GetSaveDumpInfo( IN ULONG BugCheckCode, IN ULONG_PTR BugCheckParameter1, IN ULONG_PTR BugCheckParameter2, IN ULONG_PTR BugCheckParameter3, IN ULONG_PTR BugCheckParameter4, IN ULONG MajorVersion, IN ULONG MinorVersion, OUT PUNICODE_STRING BugcheckString ) { ANSI_STRING ansiString1; ANSI_STRING ansiString2; CHAR buffer1[256]; CHAR buffer2[256];
sprintf( buffer1, "0x%08x (0x%08x, 0x%08x, 0x%08x, 0x%08x)", BugCheckCode, BugCheckParameter1, BugCheckParameter2, BugCheckParameter3, BugCheckParameter4 );
RtlInitAnsiString( &ansiString1, buffer1 ); RtlAnsiStringToUnicodeString( BugcheckString, &ansiString1, TRUE );
sprintf( buffer2, "Microsoft Windows [v%ld.%ld]", MajorVersion, MinorVersion ); }
BOOL ReadDumpHeader( IN PWSTR DumpFileName, IN PDUMP_HEADER Header ) { HANDLE File; ULONG Bytes; BOOL Succ;
File = CreateFile (DumpFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
if (File == INVALID_HANDLE_VALUE) { return FALSE; }
Succ = ReadFile (File, Header, sizeof (DUMP_HEADER), &Bytes, NULL);
CloseHandle (File);
if (Succ && Header->Signature == DUMP_SIGNATURE && Header->ValidDump == DUMP_VALID_DUMP) {
return TRUE; } else { return FALSE; } }
HRESULT DoDumpConv( PCHAR szInputDumpFile, // full or kernel dump
PCHAR szOutputDumpFile, // triage dump file
PCHAR szSymbolPath ) { HRESULT Hr = E_FAIL; IDebugClient *DebugClient; IDebugControl *DebugControl; IDebugSymbols *DebugSymbols;
if ((Hr = DebugCreate(__uuidof(IDebugClient), (void **)&DebugClient)) != S_OK) { return Hr; }
if ((DebugClient->QueryInterface(__uuidof(IDebugControl), (void **)&DebugControl) == S_OK) && (DebugClient->QueryInterface(__uuidof(IDebugSymbols), (void **)&DebugSymbols) == S_OK)) { if (DebugClient->OpenDumpFile(szInputDumpFile) == S_OK) { // Optional. Conversion does not require symbols
//if (DebugSymbols->SetSymbolPath("C:\\") == S_OK)
if (szSymbolPath) { DebugSymbols->SetSymbolPath(szSymbolPath); } DebugControl->WaitForEvent(DEBUG_WAIT_DEFAULT, INFINITE); if ((Hr = DebugClient->WriteDumpFile(szOutputDumpFile, DEBUG_DUMP_SMALL)) == S_OK) { Hr = S_OK; } }
DebugControl->Release(); DebugSymbols->Release(); }
DebugClient->Release();
return Hr; }
VOID SetSecurity(LPWSTR FileName) { PSID pLocalSystemSid; PSID pAdminSid;
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY ; SID_IDENTIFIER_AUTHORITY WorldAuthority = SECURITY_WORLD_SID_AUTHORITY ;
PSECURITY_DESCRIPTOR SecurityDescriptor; BYTE SDBuffer[SECURITY_DESCRIPTOR_MIN_LENGTH];
PACL pAcl; BYTE AclBuffer[1024];
HANDLE fileHandle;
HANDLE Token; PTOKEN_OWNER pto; ULONG bl = 5; ULONG retlen;
fileHandle = CreateFile(FileName, WRITE_DAC, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (fileHandle == INVALID_HANDLE_VALUE) { return; }
RtlAllocateAndInitializeSid( &NtAuthority, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &pLocalSystemSid );
RtlAllocateAndInitializeSid( &NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pAdminSid );
SecurityDescriptor = (PSECURITY_DESCRIPTOR) SDBuffer;
//
// You can be fancy and compute the exact size, but since the
// security descriptor capture code has to do that anyway, why
// do it twice?
//
pAcl = (PACL) AclBuffer;
RtlCreateSecurityDescriptor(SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION); RtlCreateAcl(pAcl, 1024, ACL_REVISION);
//
// current user, Administrator and system have full control
//
if (OpenThreadToken(GetCurrentThread (), MAXIMUM_ALLOWED, TRUE, &Token) || OpenProcessToken(GetCurrentProcess (), MAXIMUM_ALLOWED, &Token)) {
realloc: pto = (PTOKEN_OWNER)malloc(bl); if (pto) { if (GetTokenInformation(Token, TokenOwner, pto, bl, &retlen)) { RtlAddAccessAllowedAce(pAcl, ACL_REVISION, GENERIC_ALL | DELETE | WRITE_DAC | WRITE_OWNER, pto->Owner); } else if (bl < retlen) { bl = retlen; free (pto); goto realloc; }
free (pto); }
CloseHandle(Token); }
if (pAdminSid) { RtlAddAccessAllowedAce(pAcl, ACL_REVISION, GENERIC_ALL | DELETE | WRITE_DAC | WRITE_OWNER, pAdminSid); RtlFreeSid(pAdminSid); }
if (pLocalSystemSid) { RtlAddAccessAllowedAce(pAcl, ACL_REVISION, GENERIC_ALL | DELETE | WRITE_DAC | WRITE_OWNER, pLocalSystemSid); RtlFreeSid(pLocalSystemSid); }
RtlSetDaclSecurityDescriptor(SecurityDescriptor, TRUE, pAcl, FALSE); RtlSetOwnerSecurityDescriptor(SecurityDescriptor, pAdminSid, FALSE);
NtSetSecurityObject(fileHandle, DACL_SECURITY_INFORMATION, SecurityDescriptor);
CloseHandle(fileHandle); }
BOOL BugcheckEventHandler( IN BOOL NotifyPcHealth, OUT PBOOL SavedDump, OUT PULONG LogEvent, OUT PULONG SendAlert, OUT PUNICODE_STRING BugCheckString, OUT PWCHAR FileName )
/*++
Routine Description:
This is the boot time routine to handle pending bugcheck event.
Arguments:
NotifyPcHealth - TRUE if we should report event to PC Health, FALSE otherwise.
Return Value:
TRUE if bugcheck event found and reported to PC Health, FALSE otherwise.
--*/
{ NTSTATUS Status; DUMP_HEADER Header; ULONG WinStatus; HKEY Key; ULONG Length; ULONG Overwrite; ULONG Type; ULONG TempDestination = 0; WCHAR MiniDumpName[MAX_PATH]; WCHAR FullDumpName[MAX_PATH]; PWCHAR FinalFileName; WCHAR SourceDumpName[MAX_PATH]; BOOL Succ;
*SavedDump = FALSE; *LogEvent = 0; *SendAlert = 0;
Succ = FALSE; Overwrite = FALSE; FinalFileName = NULL;
WinStatus = RegOpenKey( HKEY_LOCAL_MACHINE, SUBKEY_CRASH_CONTROL L"\\MachineCrash", &Key );
if (WinStatus != ERROR_SUCCESS) { return FALSE; }
Length = sizeof (Length); WinStatus = RegQueryValueEx( Key, L"TempDestination", (LPDWORD) NULL, &Type, (LPBYTE) &TempDestination, &Length );
Length = sizeof (SourceDumpName); WinStatus = RegQueryValueEx( Key, L"DumpFile", (LPDWORD) NULL, &Type, (LPBYTE) &SourceDumpName, &Length );
RegCloseKey (Key);
if ((WinStatus != NO_ERROR) || !ReadDumpHeader (SourceDumpName, &Header)) { //
// Dump file is not present or invalid.
//
return FALSE; } //
// Open the base registry node for crash control information and get the
// actions for what needs to occur next.
//
WinStatus = RegOpenKey (HKEY_LOCAL_MACHINE, SUBKEY_CRASH_CONTROL, &Key);
if (WinStatus != ERROR_SUCCESS) { return FALSE; }
Length = 4;
WinStatus = RegQueryValueEx (Key, L"LogEvent", (LPDWORD) NULL, &Type, (LPBYTE) LogEvent, &Length);
WinStatus = RegQueryValueEx (Key, L"SendAlert", (LPDWORD) NULL, &Type, (LPBYTE) SendAlert, &Length);
//
// If the dump file needs to be copied, copy it now.
//
Status = GetTargetFileNames (Key, MiniDumpName, FullDumpName, Header.DumpType);
if (!NT_SUCCESS (Status)) { goto fileDone; }
if (!TempDestination) {
FinalFileName = SourceDumpName; *SavedDump = TRUE;
} else {
Overwrite = 0;
WinStatus = RegQueryValueEx (Key, L"Overwrite", (LPDWORD) NULL, &Type, (LPBYTE) &Overwrite, &Length);
if (!Test) {
//
// Set the priority class of this application down to the Lowest
// priority class to ensure that copying the file does not overload
// everything else that is going on during system initialization.
//
//
// We do not lower the priority in test mode because it just
// wastes time.
//
SetPriorityClass (GetCurrentProcess(), IDLE_PRIORITY_CLASS); SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_LOWEST); }
if (Header.DumpType == DUMP_TYPE_FULL || Header.DumpType == DUMP_TYPE_SUMMARY) {
FinalFileName = FullDumpName; *SavedDump = CopyFileEx (SourceDumpName, FullDumpName, NULL, NULL, NULL, Overwrite ? 0 : COPY_FILE_FAIL_IF_EXISTS);
} else if (Header.DumpType == DUMP_TYPE_TRIAGE) {
FinalFileName = MiniDumpName; *SavedDump = CopyFileEx (SourceDumpName, MiniDumpName, NULL, NULL, NULL, 0); }
//
// NOTE: MoveFileEx would do the Copy and Delete as one action,
// which may be desirable.
//
if (*SavedDump) { DeleteFile (SourceDumpName); } }
fileDone:
RegCloseKey (Key);
if (*SavedDump) { #if DBG
ASSERT (FinalFileName != NULL); #endif
//
// Set the security on the file
//
SetSecurity(FinalFileName); }
if (FinalFileName != NULL) { wcscpy(FileName, FinalFileName); }
GetSaveDumpInfo (Header.BugCheckCode, Header.BugCheckParameter1, Header.BugCheckParameter2, Header.BugCheckParameter3, Header.BugCheckParameter4, Header.MajorVersion, Header.MinorVersion, BugCheckString);
//
// Copy a minidump out, if necessary.
//
if (*SavedDump && (Header.DumpType == DUMP_TYPE_FULL || Header.DumpType == DUMP_TYPE_SUMMARY)) {
CreateMiniDump (FullDumpName, MiniDumpName); }
//
// Whenever we have had a blue creen event we are going to post an unexpected restart
// shutdown event screen on startup (assuming Server SKU or specially set Professional).
// In order to make it easier on the user we attempt to prefill the comment with the bug
// check data. If and only if we got to this point before he has first logged in.
//
//
// Open the Reliability key.
//
Status = RegOpenKey(HKEY_LOCAL_MACHINE, SUBKEY_RELIABILITY, &Key);
if (Status == ERROR_SUCCESS) {
//
// Is Dirty Shutdown set?
//
Length = MAX_PATH; Status = RegQueryValueEx(Key, L"DirtyShutDown", NULL, &Type, (LPBYTE)SourceDumpName, &Length);
//
// Preload the set comment.
//
if (Status == ERROR_SUCCESS) {
Status = RegSetValueEx(Key, L"BugCheckString", NULL, REG_SZ, (LPBYTE)(BugCheckString->Buffer), BugCheckString->Length); }
RegCloseKey(Key); }
//
// Report it to PC Health.
//
if (NotifyPcHealth) { PCHPFNotifyFault(eetKernelFault, MiniDumpName, NULL); return TRUE; }
return FALSE; }
void Usage() { fprintf(stderr,"savedump -c input_dump_file output_dump_file\n"); fprintf(stderr,"\tinput dump file is full or kernel crash dump.\n"); fprintf(stderr,"\toutput is triage crash dump.\n"); }
VOID __cdecl main( int argc, char *argv[] )
/*++
Routine Description:
This is the main driving routine for the dump recovery process.
Arguments:
None.
Return Value:
None.
--*/
{ BOOL DumpConv; PCHAR ConvDumpFrom; PCHAR ConvDumpTo; PCHAR SymbolPath; LONG arg; ULONG WinStatus; HKEY Key; BOOL SavedDump; ULONG LogEvent; ULONG SendAlert; UNICODE_STRING BugCheckString; WCHAR FileName[MAX_PATH]; PWCHAR FinalFileName;
DumpConv = FALSE; ConvDumpTo = NULL; ConvDumpFrom = NULL; SymbolPath = NULL; SavedDump = FALSE; LogEvent = 0; SendAlert = 0; *FileName = UNICODE_NULL; FinalFileName = NULL;
for (arg = 1; arg < argc; arg++) { if (argv[arg][0] == '-' || argv[arg][0] == '/') { switch (argv[arg][1]) { case 'c': case 'C': DumpConv = TRUE; if (arg+2<argc) { ConvDumpFrom = argv[++arg]; ConvDumpTo = argv[++arg]; } else { Usage(); exit (-1); } break; #if DBG
case 'y': case 'Y': if (++arg < argc) { SymbolPath = argv[arg]; } break; case 't': case 'T':
Test = TRUE; break; #endif
default: break; } }
} if (DumpConv) { if (!ConvDumpFrom || !ConvDumpTo) { Usage(); exit (-1); } if (DoDumpConv(ConvDumpFrom, ConvDumpTo, SymbolPath) != S_OK) { fprintf(stderr, "Dump conversion failed, please check in source dump is vaild\n" "by loading it in the debugger as 'kd.exe -z <dump> -y <symbols>\n");
}
ANSI_STRING ansiString; UNICODE_STRING unicodeString; WCHAR buffer[MAX_PATH+1];
unicodeString.Buffer = buffer; unicodeString.MaximumLength = MAX_PATH*sizeof(WCHAR); unicodeString.Length = 0;
RtlInitAnsiString( &ansiString, ConvDumpTo ); RtlAnsiStringToUnicodeString( &unicodeString, &ansiString, FALSE );
buffer[unicodeString.Length / sizeof(WCHAR)] = 0; SetSecurity(buffer);
return; }
//
// Handle dirty shutdown events.
//
BugcheckEventHandler(TRUE, &SavedDump, &LogEvent, &SendAlert, &BugCheckString, FileName);
WatchdogEventHandler(TRUE); DirtyShutdownEventHandler(TRUE);
//
// Knock down reliability ShutdownEventPending flag. We must always try to do this
// since somebody can set this flag and recover later on (e.g. watchdog's EventFlag
// cleared). With this flag set savedump will always run and we don't want that.
//
// Note: This flag is shared between multiple components. Only savedump is allowed
// to clear this flag, all others components are only allowed to set it to trigger
// savedump run at next logon.
//
WinStatus = RegOpenKey(HKEY_LOCAL_MACHINE, SUBKEY_RELIABILITY, &Key);
if (ERROR_SUCCESS == WinStatus) { RegDeleteValue(Key, L"ShutdownEventPending"); RegCloseKey(Key); }
//
// We delay time consuming opertaions till the end. We had the case where SendCrashDumpAlert
// delayed PC Health pop-ups few minutes.
//
if (SavedDump) { FinalFileName = FileName; }
if (LogEvent) { LogCrashDumpEvent (&BugCheckString, FinalFileName, SavedDump); }
if (SendAlert) { SendCrashDumpAlert (&BugCheckString, FinalFileName, SavedDump); }
//
// BUGBUG: We should call RtlFreeUnicodeString for BugCheckString (and somehow determine
// if it was allocated). I'm not doing this now since original code didn't do this
// either and I don't want to introduce additional complexity at this moment.
//
}
|