You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
791 lines
28 KiB
791 lines
28 KiB
/****************************** Module Header ******************************\
|
|
* Module Name: reason.c
|
|
*
|
|
* Copyright (c) 1985 - 2000, Microsoft Corporation
|
|
*
|
|
* This module contains the (private) APIs for the shutdown reason stuff.
|
|
*
|
|
* History:
|
|
* ??-??-???? HughLeat Wrote it as part of msgina.dll
|
|
* 11-15-2000 JasonSch Moved from msgina.dll to its new, temporary home in
|
|
* user32.dll. Ultimately this code should live in
|
|
* advapi32.dll, but that's contingent upon LoadString
|
|
* being moved to ntdll.dll.
|
|
\***************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
#include <regstr.h>
|
|
|
|
REASON_INITIALISER g_rgReasonInits[] = {
|
|
{ UCLEANUI | SHTDN_REASON_MAJOR_HARDWARE | SHTDN_REASON_MINOR_MAINTENANCE, IDS_REASON_UNPLANNED_HARDWARE_MAINTENANCE_TITLE, IDS_REASON_HARDWARE_MAINTENANCE_DESCRIPTION },
|
|
{ PCLEANUI | SHTDN_REASON_MAJOR_HARDWARE | SHTDN_REASON_MINOR_MAINTENANCE, IDS_REASON_PLANNED_HARDWARE_MAINTENANCE_TITLE, IDS_REASON_HARDWARE_MAINTENANCE_DESCRIPTION },
|
|
{ UCLEANUI | SHTDN_REASON_MAJOR_HARDWARE | SHTDN_REASON_MINOR_INSTALLATION, IDS_REASON_UNPLANNED_HARDWARE_INSTALLATION_TITLE, IDS_REASON_HARDWARE_INSTALLATION_DESCRIPTION },
|
|
{ PCLEANUI | SHTDN_REASON_MAJOR_HARDWARE | SHTDN_REASON_MINOR_INSTALLATION, IDS_REASON_PLANNED_HARDWARE_INSTALLATION_TITLE, IDS_REASON_HARDWARE_INSTALLATION_DESCRIPTION },
|
|
|
|
// { UCLEANUI | SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_UPGRADE, IDS_REASON_UNPLANNED_OPERATINGSYSTEM_UPGRADE_TITLE, IDS_REASON_OPERATINGSYSTEM_UPGRADE_DESCRIPTION },
|
|
// { PCLEANUI | SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_UPGRADE, IDS_REASON_PLANNED_OPERATINGSYSTEM_UPGRADE_TITLE, IDS_REASON_OPERATINGSYSTEM_UPGRADE_DESCRIPTION },
|
|
{ UCLEANUI | SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_RECONFIG, IDS_REASON_UNPLANNED_OPERATINGSYSTEM_RECONFIG_TITLE, IDS_REASON_OPERATINGSYSTEM_RECONFIG_DESCRIPTION },
|
|
{ PCLEANUI | SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_RECONFIG, IDS_REASON_PLANNED_OPERATINGSYSTEM_RECONFIG_TITLE, IDS_REASON_OPERATINGSYSTEM_RECONFIG_DESCRIPTION },
|
|
|
|
{ UCLEANUI | SHTDN_REASON_MAJOR_APPLICATION | SHTDN_REASON_MINOR_HUNG, IDS_REASON_APPLICATION_HUNG_TITLE, IDS_REASON_APPLICATION_HUNG_DESCRIPTION },
|
|
{ UCLEANUI | SHTDN_REASON_MAJOR_APPLICATION | SHTDN_REASON_MINOR_UNSTABLE, IDS_REASON_APPLICATION_UNSTABLE_TITLE, IDS_REASON_APPLICATION_UNSTABLE_DESCRIPTION },
|
|
{ PCLEANUI | SHTDN_REASON_MAJOR_APPLICATION | SHTDN_REASON_MINOR_INSTALLATION, IDS_REASON_PLANNED_APPLICATION_INSTALLATION_TITLE, IDS_REASON_APPLICATION_INSTALLATION_DESCRIPTION },
|
|
{ UCLEANUI | SHTDN_REASON_MAJOR_APPLICATION | SHTDN_REASON_MINOR_MAINTENANCE, IDS_REASON_APPLICATION_MAINTENANCE_TITLE, IDS_REASON_APPLICATION_MAINTENANCE_DESCRIPTION },
|
|
{ PCLEANUI | SHTDN_REASON_MAJOR_APPLICATION | SHTDN_REASON_MINOR_MAINTENANCE, IDS_REASON_APPLICATION_PM_TITLE, IDS_REASON_APPLICATION_PM_DESCRIPTION },
|
|
|
|
{ UCLEANUI | SHTDN_REASON_FLAG_COMMENT_REQUIRED | SHTDN_REASON_MAJOR_OTHER | SHTDN_REASON_MINOR_OTHER, IDS_REASON_UNPLANNED_OTHER_TITLE, IDS_REASON_OTHER_DESCRIPTION },
|
|
{ PCLEANUI | SHTDN_REASON_FLAG_COMMENT_REQUIRED | SHTDN_REASON_MAJOR_OTHER | SHTDN_REASON_MINOR_OTHER, IDS_REASON_PLANNED_OTHER_TITLE, IDS_REASON_OTHER_DESCRIPTION },
|
|
|
|
{ UDIRTYUI | SHTDN_REASON_MAJOR_SYSTEM | SHTDN_REASON_MINOR_BLUESCREEN, IDS_REASON_SYSTEMFAILURE_BLUESCREEN_TITLE, IDS_REASON_SYSTEMFAILURE_BLUESCREEN_DESCRIPTION },
|
|
{ UDIRTYUI | SHTDN_REASON_MAJOR_POWER | SHTDN_REASON_MINOR_CORDUNPLUGGED, IDS_REASON_POWERFAILURE_CORDUNPLUGGED_TITLE, IDS_REASON_POWERFAILURE_CORDUNPLUGGED_DESCRIPTION },
|
|
{ UDIRTYUI | SHTDN_REASON_MAJOR_POWER | SHTDN_REASON_MINOR_ENVIRONMENT, IDS_REASON_POWERFAILURE_ENVIRONMENT_TITLE, IDS_REASON_POWERFAILURE_ENVIRONMENT_DESCRIPTION },
|
|
{ UDIRTYUI | SHTDN_REASON_MAJOR_OTHER | SHTDN_REASON_MINOR_HUNG, IDS_REASON_OTHERFAILURE_HUNG_TITLE, IDS_REASON_OTHERFAILURE_HUNG_DESCRIPTION },
|
|
{ UDIRTYUI | SHTDN_REASON_FLAG_DIRTY_PROBLEM_ID_REQUIRED | SHTDN_REASON_MAJOR_OTHER | SHTDN_REASON_MINOR_OTHER, IDS_REASON_UNPLANNED_OTHER_TITLE, IDS_REASON_OTHER_DESCRIPTION },
|
|
{ SHTDN_REASON_FLAG_PLANNED | SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_UPGRADE, IDS_REASON_PLANNED_OPERATINGSYSTEM_UPGRADE_TITLE, IDS_REASON_OPERATINGSYSTEM_UPGRADE_DESCRIPTION },
|
|
{ SHTDN_REASON_FLAG_PLANNED | SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_SERVICEPACK, IDS_REASON_PLANNED_OPERATINGSYSTEM_SERVICEPACK_TITLE, IDS_REASON_OPERATINGSYSTEM_SERVICEPACK_DESCRIPTION },
|
|
{ SHTDN_REASON_FLAG_PLANNED | SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_HOTFIX, IDS_REASON_PLANNED_OPERATINGSYSTEM_HOTFIX_TITLE, IDS_REASON_OPERATINGSYSTEM_HOTFIX_DESCRIPTION },
|
|
{ SHTDN_REASON_FLAG_PLANNED | SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_SECURITYFIX, IDS_REASON_PLANNED_OPERATINGSYSTEM_SECURITYFIX_TITLE, IDS_REASON_OPERATINGSYSTEM_SECURITYFIX_DESCRIPTION },
|
|
{ SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_HOTFIX, IDS_REASON_UNPLANNED_OPERATINGSYSTEM_HOTFIX_TITLE, IDS_REASON_OPERATINGSYSTEM_HOTFIX_DESCRIPTION },
|
|
{ SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_SECURITYFIX, IDS_REASON_UNPLANNED_OPERATINGSYSTEM_SECURITYFIX_TITLE,IDS_REASON_OPERATINGSYSTEM_SECURITYFIX_DESCRIPTION },
|
|
{ SHTDN_REASON_LEGACY_API, IDS_REASON_LEGACY_API_TITLE, IDS_REASON_LEGACY_API_DESCRIPTION },
|
|
{ UCLEANUI | SHTDN_REASON_MAJOR_SYSTEM | SHTDN_REASON_MINOR_SECURITY, IDS_REASON_SECURITY_ISSUE_TITLE, IDS_REASON_SECURITY_ISSUE_DESCRIPTION },
|
|
{ PCLEANUI | SHTDN_REASON_MAJOR_SYSTEM | SHTDN_REASON_MINOR_SECURITY, IDS_REASON_SECURITY_ISSUE_TITLE, IDS_REASON_SECURITY_ISSUE_DESCRIPTION },
|
|
{ UDIRTYUI | SHTDN_REASON_MAJOR_SYSTEM | SHTDN_REASON_MINOR_SECURITY, IDS_REASON_SECURITY_ISSUE_TITLE, IDS_REASON_SECURITY_ISSUE_DESCRIPTION },
|
|
// { PDIRTYUI | SHTDN_REASON_MAJOR_SYSTEM | SHTDN_REASON_MINOR_SECURITY, IDS_REASON_SECURITY_ISSUE_TITLE, IDS_REASON_SECURITY_ISSUE_DESCRIPTION },
|
|
{ UCLEANUI | SHTDN_REASON_MAJOR_SYSTEM | SHTDN_REASON_MINOR_NETWORK_CONNECTIVITY, IDS_REASON_LOSS_OF_NETWORK_TITLE, IDS_REASON_LOSS_OF_NETWORK_DESCRIPTION }
|
|
};
|
|
|
|
BOOL ReasonCodeNeedsComment(DWORD dwCode)
|
|
{
|
|
return (dwCode & SHTDN_REASON_FLAG_COMMENT_REQUIRED) != 0;
|
|
}
|
|
|
|
BOOL ReasonCodeNeedsBugID(DWORD dwCode)
|
|
{
|
|
return (dwCode & SHTDN_REASON_FLAG_DIRTY_PROBLEM_ID_REQUIRED) != 0;
|
|
}
|
|
|
|
/*
|
|
* Here is the regular expression used to parse the user defined reason codes
|
|
* in the registry:
|
|
*
|
|
* S -> 's' | 'S' { Set For Clean UI }
|
|
* D -> 'd' | 'D' { Set For Dirty UI }
|
|
* P -> 'p' | 'P' { Set Planned }
|
|
* C -> 'c' | 'C' { Set Comment Required }
|
|
* B -> 'b' | 'B' { Set Problem Id Required in Dirty Mode }
|
|
*
|
|
*
|
|
* Delim -> ';'
|
|
* Flag -> S | D | P | C | B
|
|
* Flags -> ( Flag )*
|
|
*
|
|
* Digit -> '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | ''8' | '9'
|
|
* Num -> Digit*
|
|
* Maj -> Num { Set Major Code }
|
|
* Min -> Num { Set Minor Code }
|
|
*
|
|
* ValidSentence -> Flags . Delim . Maj . Delim . Min
|
|
*
|
|
* All initial states are false for each flag and 0 for minor and major reason
|
|
* codes.
|
|
* If neither S nor D are specified it is invaliad.
|
|
*/
|
|
|
|
BOOL
|
|
ParseReasonCode(
|
|
PWCHAR lpString,
|
|
LPDWORD lpdwCode)
|
|
{
|
|
WCHAR c;
|
|
UINT major = 0, minor = 0;
|
|
UINT cnt = 0;
|
|
|
|
if (!lpString || !lpdwCode)
|
|
return FALSE;
|
|
|
|
*lpdwCode = SHTDN_REASON_FLAG_USER_DEFINED;
|
|
|
|
/*
|
|
* Read the flags part.
|
|
*/
|
|
|
|
c = *lpString;
|
|
while ( c != 0 && c != L';') {
|
|
switch( c ) {
|
|
case L'P' :
|
|
case L'p' :
|
|
if (*lpdwCode & SHTDN_REASON_FLAG_PLANNED)
|
|
return FALSE;
|
|
*lpdwCode |= SHTDN_REASON_FLAG_PLANNED;
|
|
break;
|
|
case L'C' :
|
|
case L'c' :
|
|
if (*lpdwCode & SHTDN_REASON_FLAG_COMMENT_REQUIRED)
|
|
return FALSE;
|
|
*lpdwCode |= SHTDN_REASON_FLAG_COMMENT_REQUIRED;
|
|
break;
|
|
case L'S' :
|
|
case L's' :
|
|
if (*lpdwCode & SHTDN_REASON_FLAG_CLEAN_UI)
|
|
return FALSE;
|
|
*lpdwCode |= SHTDN_REASON_FLAG_CLEAN_UI;
|
|
break;
|
|
case L'D' :
|
|
case L'd' :
|
|
if (*lpdwCode & SHTDN_REASON_FLAG_DIRTY_UI)
|
|
return FALSE;
|
|
*lpdwCode |= SHTDN_REASON_FLAG_DIRTY_UI;
|
|
break;
|
|
case L'B' :
|
|
case L'b' :
|
|
if (*lpdwCode & SHTDN_REASON_FLAG_DIRTY_PROBLEM_ID_REQUIRED)
|
|
return FALSE;
|
|
*lpdwCode |= SHTDN_REASON_FLAG_DIRTY_PROBLEM_ID_REQUIRED;
|
|
break;
|
|
default :
|
|
return FALSE;
|
|
}
|
|
c = *++lpString;
|
|
}
|
|
|
|
if (!(*lpdwCode & SHTDN_REASON_FLAG_DIRTY_UI)
|
|
&& (*lpdwCode & SHTDN_REASON_FLAG_DIRTY_PROBLEM_ID_REQUIRED)) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (!(*lpdwCode & SHTDN_REASON_FLAG_CLEAN_UI)
|
|
&& (*lpdwCode & SHTDN_REASON_FLAG_COMMENT_REQUIRED)) {
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* You must have clean, or dirty or both flags set.
|
|
*/
|
|
|
|
if (!(*lpdwCode & ( SHTDN_REASON_FLAG_CLEAN_UI | SHTDN_REASON_FLAG_DIRTY_UI))) {
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* You must have reason code.
|
|
*/
|
|
|
|
if (c == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (c != L';') {
|
|
return FALSE;
|
|
}
|
|
|
|
c = *++lpString; /* Skip delimiter.*/
|
|
|
|
/*
|
|
* Parse major reason
|
|
*/
|
|
|
|
while (c != 0 && c != L';') {
|
|
if (c < L'0' || c > L'9') {
|
|
return FALSE;
|
|
}
|
|
cnt++;
|
|
if (cnt > 3) {
|
|
return FALSE;
|
|
}
|
|
major = major * 10 + c - L'0';
|
|
c = *++lpString;
|
|
}
|
|
|
|
/*
|
|
* major reason < 0x40 (64) is reserved for Microsoft.
|
|
*/
|
|
|
|
if (major > 0xff || major < 0x40) {
|
|
return FALSE;
|
|
}
|
|
*lpdwCode |= major << 16;
|
|
|
|
/*
|
|
* requires minor code.
|
|
*/
|
|
|
|
if (c == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Should have a delimiter
|
|
*/
|
|
|
|
if (c != L';') {
|
|
return FALSE;
|
|
}
|
|
|
|
c = *++lpString; /* Skip delimiter. */
|
|
|
|
/*
|
|
* Parse minor reason
|
|
*/
|
|
|
|
cnt = 0;
|
|
while (c != 0) {
|
|
if (c < L'0' || c > L'9') {
|
|
return FALSE;
|
|
}
|
|
cnt++;
|
|
if (cnt > 5) {
|
|
return FALSE;
|
|
}
|
|
minor = minor * 10 + c - L'0';
|
|
c = *++lpString;
|
|
}
|
|
|
|
if (minor > 0xffff) {
|
|
return FALSE;
|
|
}
|
|
*lpdwCode |= minor;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
int
|
|
__cdecl
|
|
CompareReasons(
|
|
CONST VOID *A,
|
|
CONST VOID *B)
|
|
{
|
|
REASON *a = *(REASON **)A;
|
|
REASON *b = *(REASON **)B;
|
|
|
|
/*
|
|
* Shift the planned bit out and put it back in the bottom.
|
|
* Ignore all ui bits.
|
|
*/
|
|
|
|
DWORD dwA = ((a->dwCode & SHTDN_REASON_VALID_BIT_MASK ) << 1) + !!(a->dwCode & SHTDN_REASON_FLAG_PLANNED);
|
|
DWORD dwB = ((b->dwCode & SHTDN_REASON_VALID_BIT_MASK ) << 1) + !!(b->dwCode & SHTDN_REASON_FLAG_PLANNED);
|
|
|
|
if (dwA < dwB) {
|
|
return -1;
|
|
} else if (dwA == dwB) {
|
|
return 0;
|
|
} else {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
SortReasonArray(
|
|
REASONDATA *pdata)
|
|
{
|
|
qsort(pdata->rgReasons, pdata->cReasons, sizeof(REASON *), CompareReasons);
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Append a reason to the reason array.
|
|
*
|
|
* Return -1 on error, 1 on success, 0 if dup.
|
|
*/
|
|
INT
|
|
AppendReason(
|
|
REASONDATA *pdata,
|
|
REASON *reason)
|
|
{
|
|
int i;
|
|
|
|
if (!pdata || !reason) {
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Remove dup here.
|
|
*/
|
|
|
|
if ( reason->dwCode & SHTDN_REASON_FLAG_USER_DEFINED) {
|
|
for (i = 0; i < pdata->cReasons; ++i) {
|
|
if ( (pdata->rgReasons[i]->dwCode & SHTDN_REASON_FLAG_PLANNED) == (reason->dwCode & SHTDN_REASON_FLAG_PLANNED)) {
|
|
if (((pdata->rgReasons[i]->dwCode & SHTDN_REASON_FLAG_CLEAN_UI) && (reason->dwCode & SHTDN_REASON_FLAG_CLEAN_UI))
|
|
|| ((pdata->rgReasons[i]->dwCode & SHTDN_REASON_FLAG_DIRTY_UI) && (reason->dwCode & SHTDN_REASON_FLAG_DIRTY_UI))) {
|
|
if (((pdata->rgReasons[i]->dwCode & 0x00ffffff) == (reason->dwCode & 0x00ffffff))
|
|
|| (_wcsicmp(pdata->rgReasons[i]->szName, reason->szName) == 0)) {
|
|
UserLocalFree(reason);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Insert the new reason into the list.
|
|
*/
|
|
|
|
if (pdata->cReasons < pdata->cReasonCapacity) {
|
|
pdata->rgReasons[pdata->cReasons++] = reason;
|
|
} else {
|
|
|
|
/*
|
|
* Need to expand the list.
|
|
*/
|
|
|
|
REASON **temp_list = (REASON **)UserLocalAlloc(0, sizeof(REASON*)*(pdata->cReasonCapacity*2+1));
|
|
if (temp_list == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < pdata->cReasons; ++i) {
|
|
temp_list[i] = pdata->rgReasons[i];
|
|
}
|
|
temp_list[pdata->cReasons++] = reason;
|
|
|
|
if (pdata->rgReasons ) {
|
|
UserLocalFree(pdata->rgReasons);
|
|
}
|
|
pdata->rgReasons = temp_list;
|
|
pdata->cReasonCapacity = pdata->cReasonCapacity*2+1;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
BOOL
|
|
LoadReasonStrings(
|
|
int idStringName,
|
|
int idStringDesc,
|
|
REASON *reason)
|
|
{
|
|
return (LoadStringW(hmodUser, idStringName, reason->szName, ARRAYSIZE(reason->szName)) != 0)
|
|
&& (LoadStringW(hmodUser, idStringDesc, reason->szDesc, ARRAYSIZE(reason->szDesc)) != 0);
|
|
}
|
|
|
|
BOOL
|
|
BuildPredefinedReasonArray(
|
|
REASONDATA *pdata,
|
|
BOOL forCleanUI,
|
|
BOOL forDirtyUI)
|
|
{
|
|
int i;
|
|
DWORD code;
|
|
|
|
/*
|
|
* If forCleanUI and forDirtyUI are both false, we
|
|
* actually will load all reasons (UI or non UI).
|
|
*/
|
|
|
|
for (i = 0; i < ARRAYSIZE(g_rgReasonInits); ++i) {
|
|
REASON *temp_reason = NULL;
|
|
|
|
code = g_rgReasonInits[ i ].dwCode;
|
|
if ((!forCleanUI && !forDirtyUI) ||
|
|
(forCleanUI && (code & SHTDN_REASON_FLAG_CLEAN_UI)) ||
|
|
(forDirtyUI && (code & SHTDN_REASON_FLAG_DIRTY_UI))) {
|
|
|
|
temp_reason = (REASON *)UserLocalAlloc(0, sizeof(REASON));
|
|
if (temp_reason == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
temp_reason->dwCode = g_rgReasonInits[i].dwCode;
|
|
if (!LoadReasonStrings(g_rgReasonInits[i].dwNameId, g_rgReasonInits[i].dwDescId, temp_reason)) {
|
|
UserLocalFree(temp_reason);
|
|
return FALSE;
|
|
}
|
|
|
|
if (AppendReason(pdata, temp_reason) == -1) {
|
|
UserLocalFree(temp_reason);
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
BuildUserDefinedReasonArray(
|
|
REASONDATA *pdata,
|
|
HKEY hReliabilityKey,
|
|
BOOL forCleanUI,
|
|
BOOL forDirtyUI
|
|
)
|
|
{
|
|
UINT i;
|
|
HKEY hKey = NULL;
|
|
DWORD num_values;
|
|
DWORD max_value_len;
|
|
DWORD rc;
|
|
WCHAR szUserDefined[] = L"UserDefined";
|
|
UINT uiReasons = 0;
|
|
|
|
//
|
|
// sizeof L"UserDefined" + sizeof(LangID)(Max Chars of ULONG)(11) + 1 ('\')
|
|
//
|
|
#define MAX_USER_LOCALE_KEY_SIZE (ARRAY_SIZE(szUserDefined) + 12)
|
|
WCHAR szUserLocale[MAX_USER_LOCALE_KEY_SIZE + 1];
|
|
|
|
_snwprintf (szUserLocale, MAX_USER_LOCALE_KEY_SIZE, L"%s\\%d", szUserDefined, GetSystemDefaultLangID());
|
|
szUserLocale[ MAX_USER_LOCALE_KEY_SIZE ] = 0;
|
|
|
|
#undef MAX_USER_LOCALE_KEY_SIZE
|
|
|
|
if (!pdata || !hReliabilityKey) {
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* First try to open the system locale key.
|
|
*/
|
|
|
|
rc = RegOpenKeyExW(hReliabilityKey,
|
|
szUserLocale,
|
|
0, KEY_READ, &hKey);
|
|
|
|
|
|
/*
|
|
* Fall back on userdefined key.
|
|
*/
|
|
|
|
if (rc != ERROR_SUCCESS){
|
|
rc = RegCreateKeyExW(hReliabilityKey,
|
|
szUserDefined,
|
|
0, NULL, REG_OPTION_NON_VOLATILE, KEY_READ,
|
|
NULL, &hKey, NULL);
|
|
}
|
|
|
|
if (rc != ERROR_SUCCESS) {
|
|
goto fail;
|
|
}
|
|
|
|
rc = RegQueryInfoKeyW(hKey, NULL, NULL, NULL, NULL, NULL, NULL, &num_values, NULL, &max_value_len, NULL, NULL);
|
|
if (rc != ERROR_SUCCESS) {
|
|
goto fail;
|
|
}
|
|
|
|
/*
|
|
* Read the user defined reasons.
|
|
*/
|
|
|
|
for (i = 0; i < num_values && uiReasons < MAX_NUM_REASONS; ++i) {
|
|
WCHAR name_buffer[ 16 ]; /* the value name for reason should be something like "ccccc;ddd;ddddd". */
|
|
DWORD name_buffer_len = ARRAY_SIZE(name_buffer); /* name buffer len is in WCHAR */
|
|
DWORD type;
|
|
WCHAR data[MAX_REASON_NAME_LEN + MAX_REASON_DESC_LEN + 1]; /* Space for name, desc and a null char.*/
|
|
DWORD data_len = (MAX_REASON_NAME_LEN + MAX_REASON_DESC_LEN + 1) * sizeof(WCHAR); /* Data len is in bytes */
|
|
DWORD code;
|
|
REASON *temp_reason = NULL;
|
|
LPWSTR szTemp = NULL;
|
|
|
|
rc = RegEnumValueW(hKey, i, name_buffer, &name_buffer_len, NULL, &type, (LPBYTE)data, &data_len);
|
|
if (rc != ERROR_SUCCESS) {
|
|
continue;
|
|
}
|
|
|
|
if (type != REG_MULTI_SZ) { /* Not a multi_string - ignore it.*/
|
|
continue;
|
|
}
|
|
|
|
// Tidy up the output from RegEnuValueW.
|
|
name_buffer[ARRAY_SIZE(name_buffer) - 1] = 0;
|
|
if (data_len/sizeof(WCHAR) + 2 <= ARRAY_SIZE(data)) {
|
|
// A reason might not have any description, an extra
|
|
// NULL is necessary under some sitations.
|
|
data[data_len/sizeof(WCHAR)] = 0;
|
|
data[data_len/sizeof(WCHAR) + 1] = 0;
|
|
}
|
|
|
|
if (!ParseReasonCode(name_buffer, &code)) { /* malformed reason code */
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Neither title nor desc can be longer than specified.
|
|
*/
|
|
|
|
if (lstrlenW (data) > MAX_REASON_NAME_LEN - 1
|
|
|| lstrlenW (data + lstrlenW(data) + 1) > MAX_REASON_DESC_LEN - 1) {
|
|
continue;
|
|
}
|
|
|
|
if ( (!forCleanUI && !forDirtyUI)
|
|
|| (forCleanUI && (code & SHTDN_REASON_FLAG_CLEAN_UI) != 0)
|
|
|| (forDirtyUI && (code & SHTDN_REASON_FLAG_DIRTY_UI) != 0)) {
|
|
|
|
temp_reason = (REASON *)UserLocalAlloc(0, sizeof(REASON));
|
|
if (temp_reason == NULL) {
|
|
goto fail;
|
|
}
|
|
|
|
temp_reason->dwCode = code;
|
|
// name and desc lenghts are checked already, should be ok here.
|
|
lstrcpyW(temp_reason->szName, data);
|
|
lstrcpyW(temp_reason->szDesc, data + lstrlenW(data) + 1);
|
|
|
|
/*
|
|
* Don't allow reasons without a title.
|
|
*/
|
|
|
|
if (lstrlenW(temp_reason->szName) == 0) {
|
|
UserLocalFree(temp_reason);
|
|
} else {
|
|
|
|
/*
|
|
* Dont allow reason title with only spaces.
|
|
*/
|
|
|
|
szTemp = temp_reason->szName;
|
|
while (*szTemp && iswspace (*szTemp)) {
|
|
szTemp++;
|
|
}
|
|
if (! *szTemp) {
|
|
UserLocalFree(temp_reason);
|
|
} else {
|
|
INT ret = AppendReason(pdata, temp_reason);
|
|
if (ret == -1) {
|
|
UserLocalFree(temp_reason);
|
|
goto fail;
|
|
} else if (ret == 1) {
|
|
uiReasons++;
|
|
} // else do nothing when dup.
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
RegCloseKey(hKey);
|
|
return TRUE;
|
|
|
|
fail :
|
|
if (hKey != NULL) {
|
|
RegCloseKey(hKey);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
BuildReasonArray(
|
|
REASONDATA *pdata,
|
|
BOOL forCleanUI,
|
|
BOOL forDirtyUI)
|
|
{
|
|
HKEY hReliabilityKey;
|
|
DWORD ignore_predefined_reasons = FALSE;
|
|
DWORD value_size = sizeof(DWORD);
|
|
DWORD rc;
|
|
DWORD dwPredefinedReasons;
|
|
|
|
if (!pdata) {
|
|
return FALSE;
|
|
}
|
|
|
|
pdata->rgReasons = (REASON **)UserLocalAlloc(0, sizeof(REASON *) * ARRAYSIZE(g_rgReasonInits));
|
|
if (pdata->rgReasons == NULL) {
|
|
return FALSE;
|
|
}
|
|
pdata->cReasonCapacity = ARRAYSIZE(g_rgReasonInits);
|
|
pdata->cReasons = 0;
|
|
|
|
if (!BuildPredefinedReasonArray(pdata, forCleanUI, forDirtyUI)) {
|
|
DestroyReasons(pdata);
|
|
return FALSE;
|
|
}
|
|
|
|
dwPredefinedReasons = pdata->cReasons;
|
|
|
|
/*
|
|
* Open the reliability key.
|
|
*/
|
|
|
|
rc = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
|
|
REGSTR_PATH_RELIABILITY,
|
|
0, NULL, REG_OPTION_NON_VOLATILE, KEY_QUERY_VALUE, NULL,
|
|
&hReliabilityKey, NULL);
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
rc = RegQueryValueEx(hReliabilityKey, REGSTR_VAL_SHUTDOWN_IGNORE_PREDEFINED, NULL, NULL, (UCHAR *)&ignore_predefined_reasons, &value_size);
|
|
if (rc != ERROR_SUCCESS) {
|
|
ignore_predefined_reasons = FALSE;
|
|
}
|
|
|
|
if (!BuildUserDefinedReasonArray(pdata, hReliabilityKey, forCleanUI, forDirtyUI) || pdata->cReasons == 0) {
|
|
ignore_predefined_reasons = FALSE;
|
|
}
|
|
|
|
RegCloseKey(hReliabilityKey);
|
|
}
|
|
|
|
// if ignore predefined reasons, we need to remove them from the list.
|
|
// But we do this only if there is some custom reasons.
|
|
if (ignore_predefined_reasons && pdata->cReasons > (int)dwPredefinedReasons) {
|
|
DWORD i;
|
|
for (i = 0; i < dwPredefinedReasons; i++) {
|
|
UserLocalFree(pdata->rgReasons[i]);
|
|
}
|
|
memmove(pdata->rgReasons, &pdata->rgReasons[dwPredefinedReasons], sizeof(REASON*)*(pdata->cReasons-dwPredefinedReasons));
|
|
pdata->cReasons -= dwPredefinedReasons;
|
|
}
|
|
|
|
return SortReasonArray(pdata);
|
|
}
|
|
|
|
VOID
|
|
DestroyReasons(
|
|
REASONDATA *pdata)
|
|
{
|
|
int i;
|
|
|
|
if (pdata->rgReasons != 0) {
|
|
for (i = 0; i < pdata->cReasons; ++i) {
|
|
UserLocalFree( pdata->rgReasons[i]);
|
|
}
|
|
UserLocalFree(pdata->rgReasons);
|
|
pdata->rgReasons = 0;
|
|
}
|
|
pdata->cReasons = 0;
|
|
}
|
|
|
|
/*
|
|
* Get the title from the reason code.
|
|
* Returns FALSE on error, TRUE otherwise.
|
|
*
|
|
* If the reason code cannot be found, then it fills the title with a default
|
|
* string.
|
|
*/
|
|
BOOL
|
|
GetReasonTitleFromReasonCode(
|
|
DWORD code,
|
|
WCHAR *lpTitle,
|
|
DWORD dwTitleLen)
|
|
{
|
|
REASONDATA data;
|
|
DWORD dwFlagBits = SHTDN_REASON_FLAG_CLEAN_UI | SHTDN_REASON_FLAG_DIRTY_UI;
|
|
int i;
|
|
|
|
if (lpTitle == NULL || dwTitleLen == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Load all of the reasons (UI or non UI).
|
|
*/
|
|
|
|
if (BuildReasonArray(&data, FALSE, FALSE) == FALSE) {
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Try to find the reason.
|
|
*/
|
|
|
|
for (i = 0; i < data.cReasons; ++i) {
|
|
if ((code & SHTDN_REASON_VALID_BIT_MASK) == (data.rgReasons[i]->dwCode & SHTDN_REASON_VALID_BIT_MASK)) { // Check valid bits.
|
|
if ((!(code & dwFlagBits) && !(data.rgReasons[i]->dwCode & dwFlagBits))
|
|
|| (code & SHTDN_REASON_FLAG_CLEAN_UI && data.rgReasons[i]->dwCode & SHTDN_REASON_FLAG_CLEAN_UI)
|
|
|| (code & SHTDN_REASON_FLAG_DIRTY_UI && data.rgReasons[i]->dwCode & SHTDN_REASON_FLAG_DIRTY_UI) ) { // check flag bits.
|
|
lstrcpynW(lpTitle, data.rgReasons[i]->szName, dwTitleLen);
|
|
lpTitle[dwTitleLen - 1] = '\0';
|
|
DestroyReasons(&data);
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Reason not found. Load the default string and return that.
|
|
*/
|
|
|
|
DestroyReasons(&data);
|
|
return (LoadStringW(hmodUser, IDS_REASON_DEFAULT_TITLE, lpTitle, dwTitleLen) != 0);
|
|
}
|
|
|
|
/*
|
|
* Check to see if SET is enabled.
|
|
* return TRUE if yes, FALSE if no.
|
|
* Currently SET is controled by policy, but we need to handle the case where
|
|
* the policy is not configured as in a clean setup case.
|
|
*/
|
|
BOOL
|
|
IsSETEnabled(
|
|
VOID)
|
|
{
|
|
HKEY hKey;
|
|
DWORD rc;
|
|
DWORD ShowReasonUI = 0;
|
|
OSVERSIONINFOEX osVersionInfoEx;
|
|
|
|
rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_RELIABILITY_POLICY, 0, KEY_QUERY_VALUE, &hKey);
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
DWORD ValueSize = sizeof (DWORD);
|
|
rc = RegQueryValueEx(hKey, REGSTR_PATH_RELIABILITY_POLICY_SHUTDOWNREASONUI, NULL, NULL, (UCHAR*)&ShowReasonUI, &ValueSize);
|
|
|
|
/*
|
|
* Now check the sku to decide whether we should show the dialog
|
|
*/
|
|
if (rc == ERROR_SUCCESS) {
|
|
if (ShowReasonUI != POLICY_SHOWREASONUI_NEVER && ShowReasonUI != POLICY_SHOWREASONUI_ALWAYS) {
|
|
osVersionInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
|
|
if (GetVersionEx((LPOSVERSIONINFOW)&osVersionInfoEx)) {
|
|
/*
|
|
* if ShowReasonUI is anything other than 2 or 3, we think it is 0.
|
|
*/
|
|
switch (osVersionInfoEx.wProductType) {
|
|
case VER_NT_WORKSTATION:
|
|
if (ShowReasonUI == POLICY_SHOWREASONUI_WORKSTATIONONLY) {
|
|
ShowReasonUI = 1;
|
|
} else {
|
|
ShowReasonUI = 0;
|
|
}
|
|
break;
|
|
default:
|
|
if (ShowReasonUI == POLICY_SHOWREASONUI_SERVERONLY) {
|
|
ShowReasonUI = 1;
|
|
} else {
|
|
ShowReasonUI = 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
ShowReasonUI = ShowReasonUI == POLICY_SHOWREASONUI_ALWAYS ? 1 : 0;
|
|
}
|
|
} else if (rc == ERROR_FILE_NOT_FOUND) {
|
|
/*
|
|
* Try to check the ShutdownReasonOn. if absent, it is not configured.
|
|
* if it is there, the value must be 0.
|
|
*/
|
|
DWORD dwSROn;
|
|
rc = RegQueryValueEx(hKey, L"ShutdownReasonOn", NULL, NULL, (UCHAR*)&dwSROn, &ValueSize);
|
|
if (rc == ERROR_FILE_NOT_FOUND) {
|
|
osVersionInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
|
|
if (GetVersionEx((LPOSVERSIONINFOW)&osVersionInfoEx)) {
|
|
switch (osVersionInfoEx.wProductType) {
|
|
case VER_NT_WORKSTATION:
|
|
ShowReasonUI = 0;
|
|
break;
|
|
default:
|
|
ShowReasonUI = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
RegCloseKey (hKey);
|
|
} else if (rc == ERROR_FILE_NOT_FOUND) { // Policy not configured.
|
|
osVersionInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
|
|
if (GetVersionEx((LPOSVERSIONINFOW)&osVersionInfoEx)) {
|
|
switch (osVersionInfoEx.wProductType) {
|
|
case VER_NT_WORKSTATION:
|
|
ShowReasonUI = 0;
|
|
break;
|
|
default:
|
|
ShowReasonUI = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return (ShowReasonUI != 0);
|
|
}
|