Leaked source code of windows server 2003
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

/****************************** 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);
}