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.
2929 lines
77 KiB
2929 lines
77 KiB
#ifndef SECURITY_WIN32
|
|
#define SECURITY_WIN32
|
|
#endif
|
|
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <windows.h>
|
|
#include <initguid.h>
|
|
#include <windowsx.h>
|
|
#include <winuserp.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <mdcommsg.h>
|
|
#include <lm.h>
|
|
|
|
#include <shlobj.h>
|
|
#include <Cmnquery.h>
|
|
#include <dsclient.h>
|
|
#include <Dsquery.h>
|
|
|
|
#include <htmlhelp.h>
|
|
|
|
#include <reason.h>
|
|
#include <regstr.h>
|
|
#include "resource.h"
|
|
|
|
#ifndef WARNING_DIRTY_REBOOT
|
|
#define WARNING_DIRTY_REBOOT 0x80000434L
|
|
#endif
|
|
|
|
//#define SNAPSHOT_TEST
|
|
#ifdef SNAPSHOT_TEST
|
|
#define TESTMSG(x) \
|
|
WriteToConsole((x))
|
|
#else
|
|
#define TESTMSG(x)
|
|
#endif //SNAPSHOT_TEST
|
|
|
|
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
|
|
|
|
#define ERROR_WITH_SZ(id, sz, code) \
|
|
{ \
|
|
LPWSTR szBuf = LoadWString(id);\
|
|
if (szBuf)\
|
|
{\
|
|
if (sz && wcslen(sz) > 0) \
|
|
{\
|
|
LPWSTR szBuf1 = (LPWSTR)LocalAlloc(LMEM_FIXED, (wcslen(szBuf) + wcslen(sz) + 20) * sizeof(WCHAR));\
|
|
if (szBuf1)\
|
|
{\
|
|
if (code != 0) \
|
|
wsprintf(szBuf1, L"%s: %s(%d)\n", sz, szBuf, code);\
|
|
else \
|
|
wsprintf(szBuf1, L"%s: %s\n", sz, szBuf);\
|
|
WriteToError(szBuf1);\
|
|
LocalFree(szBuf1);\
|
|
}\
|
|
}\
|
|
else\
|
|
{\
|
|
LPWSTR szBuf1 = (LPWSTR)LocalAlloc(LMEM_FIXED, (wcslen(szBuf) + 20) * sizeof(WCHAR));\
|
|
if (szBuf1)\
|
|
{\
|
|
if (code != 0) \
|
|
wsprintf(szBuf1, L"%s(%d)\n", szBuf, code);\
|
|
else \
|
|
wsprintf(szBuf1, L"%s\n", szBuf);\
|
|
WriteToError(szBuf1);\
|
|
LocalFree(szBuf1);\
|
|
}\
|
|
}\
|
|
LocalFree(szBuf);\
|
|
}\
|
|
}
|
|
//
|
|
// Default warning state for warning user check button
|
|
//
|
|
#define DEFAULTWARNINGSTATE BST_CHECKED
|
|
|
|
#define TITLEWARNINGLEN 32
|
|
#define MINCOMMENTLEN 0
|
|
|
|
#define MAX_TIMEOUT 60*10 // 10 min.
|
|
#define DEFAULT_TIMEOUT 30
|
|
|
|
// Name of the executable
|
|
LPWSTR g_lpszProgramName = NULL;
|
|
|
|
// Help dir.
|
|
LPWSTR g_lpszHelpdir = NULL;
|
|
LPWSTR g_lpszHelpdirHlp = NULL;
|
|
LPWSTR g_lpszHelpdirChm = NULL;
|
|
LPWSTR g_lpszHelpdirWindows = NULL;
|
|
// Name of help file.
|
|
LPWSTR HELP_FILE = L"rrc.hlp";
|
|
LPWSTR CHM_FILE = L"rrc.chm";
|
|
LPWSTR WINDOWS_HELP = L"Windows.hlp";
|
|
LPWSTR CHM_MAIN = L"::/rrcHowToShutdownRemotely.htm";
|
|
LPWSTR g_lpszDefaultTimeout = L"30";
|
|
LPWSTR g_lpszMaxTimeout = L"600";
|
|
|
|
// original edit control win proc.
|
|
WNDPROC wpOrigEditProc;
|
|
|
|
//
|
|
// Help ids
|
|
//
|
|
DWORD ShutdownDialogHelpIds[] =
|
|
{
|
|
IDOK, 28443,
|
|
IDCANCEL, 28444,
|
|
IDHELP, 28445 ,
|
|
IDC_COMBOACTION, IDH_SHUTDOWN_COMBOACTION,
|
|
IDC_COMBOOPTION, IDH_SHUTDOWN_COMBOOPTION,
|
|
IDC_LISTSELECTEDCOMPUTERS, IDH_SHUTDOWN_SELECTEDCOMPUTERS,
|
|
IDC_BUTTONREMOVE, IDH_SHUTDOWN_BUTTONREMOVE,
|
|
IDC_BUTTONBROWSE, IDH_SHUTDOWN_BUTTONBROWSE,
|
|
IDC_CHECKWARNING, IDH_SHUTDOWN_CHECKWARNING,
|
|
IDC_EDITTIMEOUT, IDH_SHUTDOWN_EDITTIMEOUT,
|
|
IDC_EDITCOMMENT, IDH_SHUTDOWN_EDITCOMMENT,
|
|
IDC_BUTTONADDNEW, IDH_SHUTDOWN_BUTTONADDNEW,
|
|
IDC_CHECK_PLANNED, IDH_SHUTDOWN_CHECK_PLANNED,
|
|
|
|
0, 0
|
|
};
|
|
|
|
|
|
DWORD AddNewDialogHelpIds[] =
|
|
{
|
|
IDOK, 28443,
|
|
IDCANCEL, 28444,
|
|
IDC_EDIT_ADDCOMPUTERS_COMPUTERS, IDH_ADDNEW_COMPUTERS,
|
|
|
|
0, 0
|
|
};
|
|
|
|
//
|
|
// Enum for all of the actions.
|
|
//
|
|
enum
|
|
{
|
|
ACTION_SHUTDOWN = 0,
|
|
ACTION_RESTART = 1,
|
|
ACTION_ANNOTATE,
|
|
ACTION_LOGOFF,
|
|
ACTION_STANDBY,
|
|
ACTION_DISCONNECT,
|
|
ACTION_ABORT
|
|
};
|
|
|
|
//
|
|
// Resource IDs for actions.
|
|
//
|
|
DWORD g_dwActions[] =
|
|
{
|
|
IDS_ACTION_SHUTDOWN,
|
|
IDS_ACTION_RESTART,
|
|
// IDS_ACTION_LOGOFF,
|
|
IDS_ACTION_ANNOTATE
|
|
//IDS_ACTION_STANDBY,
|
|
//IDS_ACTION_DISCONNECT,
|
|
//IDS_ACTION_ABORT
|
|
};
|
|
|
|
enum
|
|
{
|
|
OPTION_ABORT = 0,
|
|
OPTION_ANNOTATE,
|
|
OPTION_HIBERNATE,
|
|
OPTION_LOGOFF,
|
|
OPTION_POWEROFF,
|
|
OPTION_RESTART,
|
|
OPTION_SHUTDOWN,
|
|
FLAG_COMMENT = 0,
|
|
FLAG_REASON,
|
|
FLAG_MACHINE,
|
|
FLAG_FORCE
|
|
};
|
|
|
|
//
|
|
// Number of actions and the action strings loaded from resource.
|
|
//
|
|
const int g_nActions = sizeof(g_dwActions) / sizeof(DWORD);
|
|
WCHAR g_lppszActions[g_nActions][MAX_PATH];
|
|
|
|
LPWSTR g_lpszNewComputers = NULL;
|
|
WCHAR g_lpszDefaultDomain[MAX_PATH] = L"";
|
|
WCHAR g_lpszLocalComputerName[MAX_PATH] = L"";
|
|
WCHAR g_lpszTitleWarning[TITLEWARNINGLEN];
|
|
BOOL g_bAssumeShutdown = FALSE;
|
|
BOOL g_bDirty = FALSE;
|
|
|
|
struct _PROVIDER{
|
|
LPWSTR szName;
|
|
DWORD dwLen;
|
|
};
|
|
|
|
typedef struct _SHUTDOWNREASON
|
|
{
|
|
DWORD dwCode;
|
|
WCHAR lpName[MAX_REASON_NAME_LEN];
|
|
WCHAR lpDesc[MAX_REASON_DESC_LEN];
|
|
} SHUTDOWNREASON, *PSHUTDOWNREASON;
|
|
|
|
DWORD g_dwReasonSelect;
|
|
DWORD g_dwActionSelect;
|
|
|
|
typedef struct _SHUTDOWNCACHEDHWNDS
|
|
{
|
|
HWND hwndShutdownDialog;
|
|
HWND hwndListSelectComputers;
|
|
HWND hwndEditComment;
|
|
HWND hwndStaticDesc;
|
|
HWND hwndEditTimeout;
|
|
HWND hwndButtonWarning;
|
|
HWND hwndComboAction;
|
|
HWND hwndComboOption;
|
|
HWND hwndBtnAdd;
|
|
HWND hwndBtnRemove;
|
|
HWND hwndBtnBrowse;
|
|
HWND hwndChkPlanned;
|
|
HWND hwndButtonOK;
|
|
HWND hwndStaticComment;
|
|
} SHUTDOWNCACHEDHWNDS, *PSHUTDOWNCACHEDHWNDS;
|
|
|
|
SHUTDOWNCACHEDHWNDS g_wins;
|
|
|
|
enum
|
|
{
|
|
POWER_OPTION_HIBERNATE,
|
|
POWER_OPTION_POWEROFF
|
|
};
|
|
|
|
HMODULE g_hDllInstance = NULL;
|
|
typedef BOOL (*REASONBUILDPROC)(REASONDATA *, BOOL, BOOL);
|
|
typedef VOID (*REASONDESTROYPROC)(REASONDATA *);
|
|
|
|
BOOL GetComputerNameFromPath(LPWSTR szPath, LPWSTR szName);
|
|
VOID AdjustWindowState();
|
|
INT_PTR
|
|
CALLBACK Shutdown_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam,
|
|
LPARAM lParam);
|
|
INT_PTR
|
|
CALLBACK AddNew_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam,
|
|
LPARAM lParam);
|
|
INT_PTR
|
|
CALLBACK Browse_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam,
|
|
LPARAM lParam);
|
|
BOOL Shutdown_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify);
|
|
BOOL AddNew_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify);
|
|
BOOL Browse_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify);
|
|
BOOL Shutdown_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam);
|
|
BOOL Browse_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam);
|
|
BOOL PowerOptionEnabled(UINT option);
|
|
BOOL Annotate(LPCWSTR lpMachine, LPDWORD lpdwReason, LPCWSTR lpComment, LPDWORD lpdwErr);
|
|
VOID report_error(DWORD error_code, LPCWSTR pwszComputer);
|
|
BOOL GetTokenHandle(PHANDLE pTokenHandle);
|
|
BOOL GetUserSid(PTOKEN_USER *ppTokenUser);
|
|
BOOL IsStaticControl (HWND hwnd);
|
|
WCHAR* LoadWString(int resid);
|
|
|
|
LRESULT APIENTRY EditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
|
|
typedef void (*PSetThreadUILanguage)(DWORD);
|
|
|
|
class ShutdownHelp
|
|
{
|
|
DWORD dwCookie;
|
|
public:
|
|
ShutdownHelp():dwCookie(NULL)
|
|
{
|
|
HtmlHelp(NULL, NULL, HH_INITIALIZE, (ULONG_PTR)(&dwCookie));
|
|
}
|
|
|
|
~ShutdownHelp()
|
|
{
|
|
// HtmlHelp(NULL, NULL, HH_CLOSE_ALL, 0);
|
|
HtmlHelp(NULL, NULL, HH_UNINITIALIZE, dwCookie);
|
|
}
|
|
};
|
|
|
|
class Reasons
|
|
{
|
|
public:
|
|
PSHUTDOWNREASON m_lpReasons;
|
|
int m_cReasons;
|
|
int m_dwReasonSelect;
|
|
WCHAR m_lpszDefaultTitle[MAX_REASON_NAME_LEN];
|
|
|
|
Reasons():m_lpReasons(NULL),m_cReasons(0),m_dwReasonSelect(-1)
|
|
{
|
|
HMODULE hUser32;
|
|
REASONBUILDPROC buildProc;
|
|
REASONDESTROYPROC DestroyProc;
|
|
WCHAR lpReasonName[MAX_REASON_NAME_LEN];
|
|
REASONDATA Reasons;
|
|
|
|
m_lpszDefaultTitle[0] = L'\0';
|
|
hUser32 = LoadLibraryW(L"user32.dll");
|
|
if(hUser32 != NULL)
|
|
{
|
|
//
|
|
// We are using the user32.dll to get and destroy the reasons.
|
|
// The reasons are added to the option combo and also cached for later use.
|
|
//
|
|
LoadStringW(hUser32, IDS_REASON_DEFAULT_TITLE, m_lpszDefaultTitle, MAX_REASON_NAME_LEN);
|
|
m_lpszDefaultTitle[MAX_REASON_NAME_LEN-1] = L'\0';
|
|
buildProc = (REASONBUILDPROC)GetProcAddress(hUser32, "BuildReasonArray");
|
|
DestroyProc = (REASONDESTROYPROC)GetProcAddress(hUser32, "DestroyReasons");
|
|
if(!buildProc || !DestroyProc)
|
|
{
|
|
FreeLibrary(hUser32);
|
|
hUser32 = NULL;
|
|
return;
|
|
}
|
|
if(!(*buildProc)(&Reasons, FALSE, FALSE))
|
|
{
|
|
report_error( GetLastError( ), NULL);
|
|
FreeLibrary(hUser32);
|
|
return;
|
|
}
|
|
|
|
if (Reasons.cReasons == 0)
|
|
{
|
|
|
|
(*DestroyProc)(&Reasons);
|
|
|
|
//
|
|
// BUG 592702: shutdown.exe .NET - no reasons listed when running on XP.
|
|
// ON XP if both Clean and Dirty flags are FALSE, user32 won't build
|
|
// any reason and return success, so we will retry it with Clean
|
|
// and Dirty both are set to TRUE.
|
|
//
|
|
if(!(*buildProc)(&Reasons, TRUE, TRUE))
|
|
{
|
|
report_error( GetLastError( ), NULL);
|
|
FreeLibrary(hUser32);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
report_error( GetLastError( ), NULL);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Alloc space for reasons.
|
|
//
|
|
m_lpReasons = (PSHUTDOWNREASON)LocalAlloc(LMEM_FIXED, Reasons.cReasons * sizeof(SHUTDOWNREASON));
|
|
if(!m_lpReasons)
|
|
{
|
|
(*DestroyProc)(&Reasons);
|
|
report_error( GetLastError( ), NULL);
|
|
FreeLibrary(hUser32);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Now populate the combo according the current check state and action.
|
|
//
|
|
for (int iOption = 0; iOption < (int)Reasons.cReasons; iOption++)
|
|
{
|
|
wcscpy(m_lpReasons[iOption].lpName, Reasons.rgReasons[iOption]->szName);
|
|
wcscpy(m_lpReasons[iOption].lpDesc, Reasons.rgReasons[iOption]->szDesc);
|
|
m_lpReasons[iOption].dwCode = Reasons.rgReasons[iOption]->dwCode;
|
|
}
|
|
|
|
m_cReasons = (int)Reasons.cReasons;
|
|
(*DestroyProc)(&Reasons);
|
|
FreeLibrary(hUser32);
|
|
}
|
|
|
|
~Reasons()
|
|
{
|
|
if(m_lpReasons)
|
|
LocalFree(m_lpReasons);
|
|
}
|
|
|
|
BOOL RequireComment(DWORD dwReason, BOOL isDirty = FALSE)
|
|
{
|
|
DWORD dwFlag = isDirty ? SHTDN_REASON_FLAG_DIRTY_PROBLEM_ID_REQUIRED : SHTDN_REASON_FLAG_COMMENT_REQUIRED;
|
|
DWORD dwDirtyOrClean = isDirty ? SHTDN_REASON_FLAG_DIRTY_UI : SHTDN_REASON_FLAG_CLEAN_UI;
|
|
DWORD dwAll = SHTDN_REASON_FLAG_DIRTY_PROBLEM_ID_REQUIRED | SHTDN_REASON_FLAG_COMMENT_REQUIRED
|
|
| SHTDN_REASON_FLAG_DIRTY_UI | SHTDN_REASON_FLAG_CLEAN_UI;
|
|
|
|
if (!m_lpReasons)
|
|
return FALSE;
|
|
|
|
if (dwReason & dwFlag)
|
|
return TRUE;
|
|
|
|
for(int i = 0; i < m_cReasons; i++)
|
|
{
|
|
if(m_lpReasons[i].dwCode & dwFlag)
|
|
{
|
|
if ( ((m_lpReasons[i].dwCode & ~dwAll) == (dwReason & ~dwAll))
|
|
&& (m_lpReasons[i].dwCode & dwDirtyOrClean) )
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL RequireComment(DWORD dwMajor, DWORD dwMinor, BOOL isDirty, BOOL isPlanned, BOOL isUserDefined)
|
|
{
|
|
DWORD dwReason = dwMinor;
|
|
dwReason <<= 20;
|
|
dwReason &= dwMajor;
|
|
|
|
if(isPlanned)
|
|
dwReason &= SHTDN_REASON_FLAG_PLANNED;
|
|
|
|
if(isUserDefined)
|
|
dwReason &= SHTDN_REASON_FLAG_USER_DEFINED;
|
|
|
|
return RequireComment (dwReason, isDirty);
|
|
}
|
|
|
|
VOID FillCombo(HWND hwnd, BOOL isDirty, BOOL isPlanned, HWND hwndStatic)
|
|
{
|
|
int iOption;
|
|
int iFirst = -1;
|
|
DWORD dwPlanned = isPlanned ? SHTDN_REASON_FLAG_PLANNED : 0;
|
|
DWORD dwDirty = isDirty ? SHTDN_REASON_FLAG_DIRTY_UI : SHTDN_REASON_FLAG_CLEAN_UI;
|
|
|
|
if(! hwnd)
|
|
return;
|
|
|
|
//
|
|
// Remove all items from combo
|
|
//
|
|
while (ComboBox_GetCount(hwnd))
|
|
ComboBox_DeleteString(hwnd, 0);
|
|
|
|
//
|
|
// Now populate the combo according the current check state.
|
|
//
|
|
for (iOption = 0; iOption < (int)m_cReasons; iOption++)
|
|
{
|
|
if(((m_lpReasons[iOption].dwCode & SHTDN_REASON_FLAG_PLANNED) == dwPlanned)
|
|
&& ((m_lpReasons[iOption].dwCode & dwDirty) == dwDirty))
|
|
{
|
|
ComboBox_AddString(hwnd, m_lpReasons[iOption].lpName);
|
|
if (iFirst == -1)
|
|
iFirst = iOption;
|
|
}
|
|
}
|
|
|
|
if(iFirst != -1)
|
|
{
|
|
ComboBox_SelectString(hwnd, -1, m_lpReasons[iFirst].lpName);
|
|
if (hwndStatic)
|
|
SetWindowTextW(hwndStatic, m_lpReasons[iFirst].lpDesc);
|
|
}
|
|
m_dwReasonSelect = iFirst;
|
|
}
|
|
|
|
VOID SetDesc(HWND hCombo, HWND hDesc)
|
|
{
|
|
WCHAR szName[MAX_REASON_NAME_LEN];
|
|
|
|
if(!hCombo || !hDesc)
|
|
return;
|
|
|
|
GetWindowText(hCombo, (LPWSTR)szName, MAX_REASON_NAME_LEN);
|
|
szName[MAX_REASON_NAME_LEN-1] = '\0';
|
|
|
|
for(DWORD dwIndex = 0; dwIndex < (DWORD)m_cReasons; dwIndex++)
|
|
{
|
|
if(lstrcmp(szName, m_lpReasons[dwIndex].lpName) == 0)
|
|
{
|
|
SetWindowTextW(hDesc, m_lpReasons[dwIndex].lpDesc);
|
|
m_dwReasonSelect = dwIndex;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID GetReasonTitle(DWORD dwReason, LPWSTR szBuf, DWORD dwSize)
|
|
{
|
|
DWORD dwFlagBits = SHTDN_REASON_FLAG_CLEAN_UI | SHTDN_REASON_FLAG_DIRTY_UI;
|
|
if(!szBuf || dwSize == 0)
|
|
return;
|
|
for(int i = 0; i < m_cReasons; i++)
|
|
{
|
|
if ((dwReason & SHTDN_REASON_VALID_BIT_MASK) == (m_lpReasons[i].dwCode & SHTDN_REASON_VALID_BIT_MASK))
|
|
{
|
|
if ((!(dwReason & dwFlagBits) && !(m_lpReasons[i].dwCode & dwFlagBits))
|
|
|| (dwReason & SHTDN_REASON_FLAG_CLEAN_UI && m_lpReasons[i].dwCode & SHTDN_REASON_FLAG_CLEAN_UI)
|
|
|| (dwReason & SHTDN_REASON_FLAG_DIRTY_UI && m_lpReasons[i].dwCode & SHTDN_REASON_FLAG_DIRTY_UI) ) { // check flag bits.
|
|
lstrcpynW(szBuf, m_lpReasons[i].lpName, dwSize - 1);
|
|
szBuf[dwSize - 1] = '\0';
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
wcsncpy(szBuf, m_lpszDefaultTitle, dwSize - 1);
|
|
szBuf[dwSize - 1] = '\0';
|
|
}
|
|
|
|
} g_reasons;
|
|
|
|
//
|
|
// Check whether a string is all white spaces.
|
|
//
|
|
BOOL
|
|
IsEmpty(LPCWSTR lpCWSTR)
|
|
{
|
|
if(!lpCWSTR)
|
|
return TRUE;
|
|
while(*lpCWSTR && (*lpCWSTR == '\n' || *lpCWSTR == '\t' || *lpCWSTR == '\r' || *lpCWSTR == ' '))
|
|
lpCWSTR++;
|
|
if(*lpCWSTR)
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
// Write the string to console
|
|
VOID
|
|
WriteOutput(
|
|
LPWSTR pszMsg,
|
|
DWORD nStdHandle
|
|
)
|
|
{
|
|
HANDLE hConsole = GetStdHandle( nStdHandle );
|
|
|
|
if ( !pszMsg || !*pszMsg )
|
|
return;
|
|
|
|
DWORD dwStrLen = lstrlenW( pszMsg );
|
|
LPSTR pszAMsg = NULL;
|
|
DWORD dwBytesWritten = 0;
|
|
DWORD dwMode = 0;
|
|
|
|
if ( (GetFileType ( hConsole ) & FILE_TYPE_CHAR ) &&
|
|
GetConsoleMode( hConsole, &dwMode ) )
|
|
{
|
|
WriteConsoleW( hConsole, pszMsg, dwStrLen, &dwBytesWritten, 0 );
|
|
return;
|
|
}
|
|
|
|
// console redirect to a file.
|
|
if ( !(pszAMsg = (LPSTR)LocalAlloc(LMEM_FIXED, (dwStrLen + 1) * sizeof(WCHAR) ) ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (WideCharToMultiByte(GetConsoleOutputCP(),
|
|
0,
|
|
pszMsg,
|
|
-1,
|
|
pszAMsg,
|
|
dwStrLen * sizeof(WCHAR),
|
|
NULL,
|
|
NULL) != 0
|
|
&& hConsole)
|
|
{
|
|
WriteFile( hConsole,
|
|
pszAMsg,
|
|
lstrlenA(pszAMsg),
|
|
&dwBytesWritten,
|
|
NULL );
|
|
|
|
}
|
|
|
|
LocalFree( pszAMsg );
|
|
}
|
|
|
|
// Write the string to stdout
|
|
VOID
|
|
WriteToConsole(
|
|
LPWSTR pszMsg
|
|
)
|
|
{
|
|
WriteOutput(pszMsg, STD_OUTPUT_HANDLE);
|
|
}
|
|
|
|
// Write the string to stderr
|
|
VOID
|
|
WriteToError(
|
|
LPWSTR pszMsg
|
|
)
|
|
{
|
|
WriteOutput(pszMsg, STD_ERROR_HANDLE);
|
|
}
|
|
|
|
// Report error.
|
|
VOID
|
|
report_error(
|
|
DWORD error_code,
|
|
LPCWSTR szComputer
|
|
)
|
|
{
|
|
LPVOID msgBuf = 0;
|
|
LPWSTR szBuf = NULL;
|
|
int len = 0;
|
|
|
|
if (error_code == 997 || error_code == 0)
|
|
return;
|
|
|
|
if (error_code == ERROR_NOT_READY)
|
|
{
|
|
ERROR_WITH_SZ(IDS_ERROR_NOT_READY, szComputer, error_code);
|
|
return;
|
|
}
|
|
else if (error_code == ERROR_BAD_NETPATH || error_code == WSAHOST_NOT_FOUND)
|
|
{
|
|
ERROR_WITH_SZ(IDS_ERROR_NOT_AVAILABLE, szComputer, error_code);
|
|
return;
|
|
}
|
|
else
|
|
FormatMessageW(
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_FROM_SYSTEM |
|
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
NULL,
|
|
error_code,
|
|
MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language
|
|
reinterpret_cast< wchar_t* >( &msgBuf ),
|
|
0,
|
|
NULL);
|
|
|
|
if (msgBuf)
|
|
{
|
|
// remove end newline.
|
|
len = wcslen(reinterpret_cast< wchar_t* >( msgBuf ));
|
|
reinterpret_cast< wchar_t* >( msgBuf )[len - 2] = L'\0';
|
|
if (szComputer && wcslen(szComputer) > 0)
|
|
{
|
|
LPWSTR szBuf1 = (LPWSTR)LocalAlloc(LMEM_FIXED,
|
|
(len + wcslen(szComputer) + 20) * sizeof(WCHAR));
|
|
if (szBuf1)
|
|
{
|
|
wsprintf(szBuf1, L"%s: %s(%d)\n", szComputer, reinterpret_cast< wchar_t* >( msgBuf ), error_code);
|
|
WriteToError(szBuf1);
|
|
LocalFree(szBuf1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LPWSTR szBuf1 = (LPWSTR)LocalAlloc(LMEM_FIXED,
|
|
(len + 20) * sizeof(WCHAR));
|
|
if (szBuf1)
|
|
{
|
|
wsprintf(szBuf1, L"%s(%d)\n", reinterpret_cast< wchar_t* >( msgBuf ), error_code);
|
|
WriteToError(szBuf1);
|
|
LocalFree(szBuf1);
|
|
}
|
|
}
|
|
|
|
LocalFree( msgBuf );
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
parse_reason_code(
|
|
LPCWSTR arg,
|
|
LPDWORD lpdwReason
|
|
)
|
|
{
|
|
// Code consists of flags;major;minor
|
|
// Qingboz: Now we make major and minor code mandatory
|
|
//
|
|
|
|
BOOL fMajor = FALSE;
|
|
BOOL fMinor = FALSE;
|
|
BOOL fUserDefined = FALSE;
|
|
int major = 0;
|
|
int minor = 0;
|
|
|
|
const int state_start = 0;
|
|
const int state_flags = 0;
|
|
const int state_major = 1;
|
|
const int state_minor = 2;
|
|
const int state_null = 3;
|
|
const int state_done = 4;
|
|
|
|
for( int i = 0, state = state_start; state != state_done; ++i )
|
|
{
|
|
switch( state )
|
|
{
|
|
case state_flags :
|
|
// Expecting flags
|
|
switch( arg[ i ] ) {
|
|
case L'U' : case L'u' :
|
|
if(fUserDefined)
|
|
{
|
|
//
|
|
// Already set.
|
|
//
|
|
return FALSE;
|
|
}
|
|
fUserDefined = TRUE;
|
|
break;
|
|
case L'P' : case L'p' :
|
|
if(*lpdwReason & 0x80000000)
|
|
{
|
|
//
|
|
// Already set.
|
|
//
|
|
return FALSE;
|
|
}
|
|
*lpdwReason |= 0x80000000; // SHTDN_REASON_FLAG_PLANNED
|
|
break;
|
|
case L':' :
|
|
if(i == 0) // ':' cannot be the first one now.
|
|
return FALSE;
|
|
state = state_major;
|
|
break;
|
|
case L'0':
|
|
case L'1':
|
|
case L'2':
|
|
case L'3':
|
|
case L'4':
|
|
case L'5':
|
|
case L'6':
|
|
case L'7':
|
|
case L'8':
|
|
case L'9':
|
|
if(i != 0) // A number here must be the first one.
|
|
return FALSE;
|
|
state = state_major;
|
|
i--; // Go back one.
|
|
break;
|
|
default :
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case state_major :
|
|
// Expecting major
|
|
if( arg[ i ] >= L'0' && arg[ i ] <= L'9' ) {
|
|
fMajor = TRUE;
|
|
major = major * 10 + arg[ i ] - L'0';
|
|
}
|
|
else {
|
|
if(!fMajor)
|
|
return FALSE;
|
|
// Make sure we only have 8 bits
|
|
if( major > 0xff ) return FALSE;
|
|
if (major >= 64)
|
|
*lpdwReason |= SHTDN_REASON_FLAG_USER_DEFINED;
|
|
|
|
*lpdwReason |= major << 16;
|
|
if( arg[ i ] != L':') {
|
|
// missing minor reason.
|
|
return FALSE;
|
|
}
|
|
|
|
state = state_minor;
|
|
}
|
|
break;
|
|
case state_minor :
|
|
// Expecting minor reason
|
|
if( arg[ i ] >= L'0' && arg[ i ] <= L'9' ) {
|
|
fMinor = TRUE;
|
|
minor = minor * 10 + arg[ i ] - L'0';
|
|
}
|
|
else {
|
|
if(!fMinor)
|
|
return FALSE;
|
|
// Make sure we only have 16 bits
|
|
if( minor > 0xffff ) return FALSE;
|
|
*lpdwReason = ( *lpdwReason & 0xffff0000 ) | minor;
|
|
if( arg[ i ] != 0 )
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
break;
|
|
default :
|
|
return FALSE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
// Parses an integer if it is in decimal notation.
|
|
// Returns FALSE if it is malformed.
|
|
BOOL
|
|
parse_int(
|
|
const wchar_t* arg,
|
|
LPDWORD lpdwInt
|
|
)
|
|
{
|
|
*lpdwInt = 0;
|
|
while( *arg ) {
|
|
if( *arg >= L'0' && *arg <= L'9' ) {
|
|
*lpdwInt = *lpdwInt * 10 + int( *arg++ - L'0' );
|
|
}
|
|
else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
// Parse options.
|
|
// Returns FALSE if the option strings are malformed. This causes the usage to be printed.
|
|
BOOL
|
|
parse_options(
|
|
int argc,
|
|
wchar_t *argv[],
|
|
LPBOOL lpfLogoff,
|
|
LPBOOL lpfForce,
|
|
LPBOOL lpfReboot,
|
|
LPBOOL lpfHibernate,
|
|
LPBOOL lpfAbort,
|
|
LPBOOL lpfPoweroff,
|
|
LPBOOL lpfAnnotate,
|
|
LPWSTR *ppServerName,
|
|
LPWSTR *ppMessage,
|
|
LPDWORD lpdwTimeout,
|
|
LPDWORD lpdwReason
|
|
)
|
|
{
|
|
BOOL fShutdown = FALSE;
|
|
BOOL fTimeout = FALSE;
|
|
BOOL fComment = FALSE;
|
|
BOOL fMachine = FALSE;
|
|
BOOL fReason = FALSE;
|
|
DWORD dwOption = 0;
|
|
DWORD dwFlag = 0;
|
|
|
|
*lpfLogoff = FALSE;
|
|
*lpfForce = FALSE;
|
|
*lpfReboot = FALSE;
|
|
*lpfHibernate = FALSE;
|
|
*lpfAbort = FALSE;
|
|
*lpfPoweroff = FALSE;
|
|
*lpfAnnotate = FALSE;
|
|
*ppServerName = NULL;
|
|
*ppMessage = NULL;
|
|
*lpdwTimeout = DEFAULT_TIMEOUT;
|
|
*lpdwReason = 0xff;
|
|
|
|
//
|
|
// Set default reason to be planned
|
|
//
|
|
*lpdwReason |= SHTDN_REASON_FLAG_PLANNED;
|
|
|
|
for( int i = 1; i < argc; ++i )
|
|
{
|
|
wchar_t* arg = argv[ i ];
|
|
|
|
switch( arg[ 0 ] )
|
|
{
|
|
case L'/' : case L'-' :
|
|
|
|
switch( arg[ 1 ] )
|
|
{
|
|
case L'L' : case L'l' :
|
|
|
|
if (*lpfLogoff)
|
|
return FALSE;
|
|
*lpfLogoff = TRUE;
|
|
if (arg[2] != 0) return FALSE;
|
|
dwOption++;
|
|
break;
|
|
|
|
case L'S' : case L's' :
|
|
|
|
//
|
|
// Use server name if supplied (i.e. do nothing here)
|
|
//
|
|
|
|
if(fShutdown)
|
|
return FALSE;
|
|
|
|
fShutdown = TRUE;
|
|
if( arg[ 2 ] != 0 ) return FALSE;
|
|
dwOption++;
|
|
break;
|
|
|
|
case L'F' : case L'f' :
|
|
if (*lpfForce)
|
|
return FALSE;
|
|
|
|
*lpfForce = TRUE;
|
|
if( arg[ 2 ] != 0 ) return FALSE;
|
|
break;
|
|
|
|
case L'H' : case L'h' :
|
|
if (*lpfHibernate)
|
|
return FALSE;
|
|
|
|
*lpfHibernate = TRUE;
|
|
if( arg[ 2 ] != 0 ) return FALSE;
|
|
dwOption++;
|
|
break;
|
|
|
|
case L'R' : case L'r' :
|
|
if (*lpfReboot)
|
|
return FALSE;
|
|
|
|
*lpfReboot = TRUE;
|
|
if( arg[ 2 ] != 0 ) return FALSE;
|
|
dwOption++;
|
|
break;
|
|
|
|
case L'A' : case L'a' :
|
|
if (*lpfAbort)
|
|
return FALSE;
|
|
*lpfAbort = TRUE;
|
|
if( arg[ 2 ] != 0 ) return FALSE;
|
|
dwOption++;
|
|
break;
|
|
|
|
case L'P' : case L'p' :
|
|
if (*lpfPoweroff)
|
|
return FALSE;
|
|
*lpfPoweroff = TRUE;
|
|
if( arg[ 2 ] != 0 ) return FALSE;
|
|
dwOption++;
|
|
break;
|
|
|
|
case L'E' : case L'e' :
|
|
if (*lpfAnnotate)
|
|
return FALSE;
|
|
*lpfAnnotate = TRUE;
|
|
if( arg[ 2 ] != 0 ) return FALSE;
|
|
dwOption++;
|
|
break;
|
|
|
|
case L'T' : case L't' :
|
|
|
|
//
|
|
// Next arg should be number of seconds
|
|
//
|
|
|
|
if(fTimeout) // Already did.
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (++i == argc)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
arg = argv[i];
|
|
|
|
if( arg[ 0 ] < L'0' || arg[ 0 ] > L'9' ) return FALSE;
|
|
if( !parse_int( arg, lpdwTimeout )) return FALSE;
|
|
if(*lpdwTimeout > MAX_TIMEOUT)
|
|
return FALSE;
|
|
fTimeout = TRUE;
|
|
break;
|
|
|
|
case L'Y' : case L'y' :
|
|
|
|
// Ignore this option.
|
|
break;
|
|
|
|
case L'D' : case L'd' :
|
|
|
|
//
|
|
// Next arg should be reason code
|
|
//
|
|
|
|
if (fReason)
|
|
return FALSE;
|
|
|
|
if (++i == argc)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
arg = argv[i];
|
|
|
|
//
|
|
//If reason code is given, we clear the planned bit.
|
|
//
|
|
*lpdwReason = (DWORD)0xFF;
|
|
|
|
if( !parse_reason_code( arg, lpdwReason ))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
fReason = TRUE;
|
|
|
|
break;
|
|
|
|
case L'C' : case L'c' :
|
|
|
|
//
|
|
// Next arg should be shutdown message. Make
|
|
// sure only one is specified.
|
|
//
|
|
if (fComment)
|
|
return FALSE;
|
|
|
|
if (++i == argc || *ppMessage)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
arg = argv[i];
|
|
|
|
if(wcslen(arg) > MAX_REASON_COMMENT_LEN - 1 || wcslen(arg) <= MINCOMMENTLEN)
|
|
return FALSE;
|
|
|
|
*ppMessage = arg;
|
|
fComment = TRUE;
|
|
|
|
break;
|
|
|
|
case L'M' : case L'm' :
|
|
|
|
//
|
|
// Next arg should be machine name. Make
|
|
// sure only one is specified.
|
|
//
|
|
|
|
if (fMachine)
|
|
return FALSE;
|
|
|
|
if (++i == argc || *ppServerName)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
arg = argv[i];
|
|
|
|
if (arg[0] == L'\\' && arg[1] == L'\\')
|
|
{
|
|
*ppServerName = arg + 2;
|
|
}
|
|
else
|
|
{
|
|
*ppServerName = arg;
|
|
}
|
|
|
|
fMachine = TRUE;
|
|
|
|
break;
|
|
|
|
case L'?' : default :
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
break;
|
|
|
|
default :
|
|
|
|
//
|
|
// Junk
|
|
//
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Check for mutually exclusive options
|
|
//
|
|
|
|
if (dwOption > 1)
|
|
return FALSE;
|
|
|
|
//
|
|
// Default is to logoff
|
|
//
|
|
|
|
if (dwOption == 0)
|
|
{
|
|
*lpfLogoff = TRUE;
|
|
}
|
|
|
|
//
|
|
// Only -f can go with -l
|
|
//
|
|
|
|
if (*lpfLogoff && (fTimeout || fReason || fComment || fMachine))
|
|
return FALSE;
|
|
|
|
//
|
|
// -a can only take -m
|
|
//
|
|
|
|
if (*lpfAbort && (fTimeout || fReason || *lpfForce || fComment))
|
|
return FALSE;
|
|
|
|
//
|
|
// -h only with -f
|
|
//
|
|
|
|
if (*lpfHibernate && (fTimeout || fReason || fComment || fMachine))
|
|
return FALSE;
|
|
|
|
//
|
|
// -p can only take -d
|
|
//
|
|
|
|
if (*lpfPoweroff && (fTimeout || *lpfForce || fComment || fMachine))
|
|
return FALSE;
|
|
|
|
//
|
|
// -e must have -d.
|
|
//
|
|
|
|
if (*lpfAnnotate)
|
|
{
|
|
if (*lpfForce || fTimeout)
|
|
return FALSE;
|
|
if (! fReason)
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Shutdown and reboot the same, comment must require a reason.
|
|
// Removed upon request.
|
|
//
|
|
#if 0
|
|
if (fShutdown || *lpfReboot)
|
|
{
|
|
if (fComment && !fReason)
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Add the clean or dirty flag.
|
|
//
|
|
|
|
if (*lpfAnnotate)
|
|
*lpdwReason |= SHTDN_REASON_FLAG_DIRTY_UI;
|
|
else
|
|
*lpdwReason |= SHTDN_REASON_FLAG_CLEAN_UI;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
// Print out usage help string.
|
|
VOID
|
|
usage(
|
|
VOID
|
|
)
|
|
{
|
|
HMODULE hModule = GetModuleHandle( NULL );
|
|
HMODULE hUser32;
|
|
REASONBUILDPROC buildProc;
|
|
REASONDESTROYPROC DestroyProc;
|
|
WCHAR lpReasonName[MAX_PATH];
|
|
REASONDATA Reasons;
|
|
|
|
if( hModule == NULL )
|
|
{
|
|
report_error( GetLastError(), NULL);
|
|
return;
|
|
}
|
|
|
|
for (DWORD i = IDS_USAGE0; i < IDS_USAGE_END; i++)
|
|
{
|
|
WCHAR *szBuf = LoadWString(i);
|
|
if(!szBuf)
|
|
continue;
|
|
if (i == IDS_USAGE0)
|
|
{
|
|
LPWSTR msg = (LPWSTR) LocalAlloc(LMEM_FIXED, (lstrlenW(szBuf) + lstrlenW( g_lpszProgramName ) + 2) * sizeof(WCHAR));
|
|
if(!msg)
|
|
{
|
|
LocalFree(szBuf);
|
|
report_error( GetLastError(), NULL);
|
|
return;
|
|
}
|
|
swprintf(msg, szBuf, g_lpszProgramName );
|
|
WriteToConsole( msg );
|
|
LocalFree(msg);
|
|
}
|
|
else
|
|
{
|
|
WriteToConsole( szBuf );
|
|
}
|
|
LocalFree(szBuf);
|
|
}
|
|
|
|
//
|
|
// Now print out the reasons.
|
|
//
|
|
WCHAR szCode[MAX_PATH];
|
|
WCHAR *szTitle = LoadWString(IDS_REASONLISTTITLE);
|
|
|
|
if(szTitle)
|
|
{
|
|
WriteToConsole(szTitle);
|
|
LocalFree(szTitle);
|
|
}
|
|
|
|
for (int iOption = 0; iOption < (int)g_reasons.m_cReasons; iOption++)
|
|
{
|
|
WriteToConsole(L"\n");
|
|
|
|
if(g_reasons.m_lpReasons[iOption].dwCode & SHTDN_REASON_FLAG_CLEAN_UI)
|
|
WriteToConsole(L"E");
|
|
else
|
|
WriteToConsole(L" ");
|
|
|
|
if(g_reasons.m_lpReasons[iOption].dwCode & SHTDN_REASON_FLAG_DIRTY_UI)
|
|
WriteToConsole(L"U");
|
|
else
|
|
WriteToConsole(L" ");
|
|
|
|
if(g_reasons.m_lpReasons[iOption].dwCode & SHTDN_REASON_FLAG_PLANNED)
|
|
WriteToConsole(L"P");
|
|
else
|
|
WriteToConsole(L" ");
|
|
|
|
if(g_reasons.m_lpReasons[iOption].dwCode & SHTDN_REASON_FLAG_USER_DEFINED)
|
|
WriteToConsole(L"C");
|
|
else
|
|
WriteToConsole(L" ");
|
|
|
|
swprintf(szCode, L"\t%d\t%d\t", (g_reasons.m_lpReasons[iOption].dwCode & 0x00ff0000)>>16,
|
|
g_reasons.m_lpReasons[iOption].dwCode & 0x0000ffff);
|
|
WriteToConsole(szCode);
|
|
WriteToConsole(g_reasons.m_lpReasons[iOption].lpName);
|
|
}
|
|
if(iOption)
|
|
WriteToConsole(L"\n");
|
|
}
|
|
|
|
|
|
// We need shutdown privileges enabled to be able to shut down our machines.
|
|
BOOL
|
|
enable_privileges(
|
|
LPCWSTR lpServerName,
|
|
BOOL fLogoff
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
NTSTATUS Status1 = STATUS_SUCCESS;
|
|
BOOLEAN fWasEnabled;
|
|
|
|
if (fLogoff)
|
|
{
|
|
//
|
|
// No privileges to get
|
|
//
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// We will always enable both privileges so
|
|
// it can work for telnet sessions.
|
|
//
|
|
Status = RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE,
|
|
TRUE,
|
|
FALSE,
|
|
&fWasEnabled);
|
|
|
|
Status1 = RtlAdjustPrivilege(SE_REMOTE_SHUTDOWN_PRIVILEGE,
|
|
TRUE,
|
|
FALSE,
|
|
&fWasEnabled);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
PowerOptionEnabled(
|
|
UINT option
|
|
)
|
|
// --------------------------------------------------------------------------
|
|
// PowerOptionEnabled
|
|
//
|
|
// Arguments: option
|
|
//
|
|
// Returns: TRUE indicates the option is enabled.
|
|
//
|
|
// Purpose: Detects if the specified power option is enabled on the system.
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
{
|
|
NTSTATUS status;
|
|
SYSTEM_POWER_CAPABILITIES spc;
|
|
BOOL RetVal = FALSE;
|
|
|
|
status = NtPowerInformation(SystemPowerCapabilities,
|
|
NULL,
|
|
0,
|
|
&spc,
|
|
sizeof(spc));
|
|
switch (option)
|
|
{
|
|
case POWER_OPTION_HIBERNATE:
|
|
if (NT_SUCCESS(status) && spc.HiberFilePresent){
|
|
RetVal = TRUE;
|
|
}
|
|
break;
|
|
case POWER_OPTION_POWEROFF:
|
|
if (NT_SUCCESS(status) && spc.SystemS5){
|
|
RetVal = TRUE;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return(RetVal);
|
|
}
|
|
|
|
BOOL WINAPI ConsoleHandlerRoutine(
|
|
DWORD dwCtrlType
|
|
)
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
// Ignore the Ctrl-C and Ctrl-Break.
|
|
//
|
|
// Arguments: type of the control signal
|
|
//
|
|
// Returns: TRUE if the function handles the control.
|
|
// if return FALSE, the next handler function in the list is used.
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
{
|
|
if ( dwCtrlType == CTRL_BREAK_EVENT ||
|
|
dwCtrlType == CTRL_C_EVENT )
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int __cdecl
|
|
wmain(
|
|
int argc,
|
|
wchar_t *argv[]
|
|
)
|
|
{
|
|
BOOL fLogoff;
|
|
BOOL fForce;
|
|
BOOL fReboot;
|
|
BOOL fAbort;
|
|
BOOL fPoweroff;
|
|
BOOL fAnnotate;
|
|
BOOL fHibernate;
|
|
LPWSTR lpServerName;
|
|
LPWSTR lpMessage;
|
|
DWORD dwTimeout;
|
|
DWORD dwReason = 0;
|
|
DWORD dwRet = 0;
|
|
INT_PTR hResult;
|
|
NTSTATUS Status;
|
|
WCHAR szComment[MAX_REASON_COMMENT_LEN];
|
|
|
|
HINSTANCE hInstance;
|
|
|
|
//
|
|
// ignore any control-c / control-break
|
|
//
|
|
SetConsoleCtrlHandler(ConsoleHandlerRoutine, TRUE);
|
|
|
|
if ( (hInstance = LoadLibrary( L"kernel32.dll" ) ) )
|
|
{
|
|
PSetThreadUILanguage SetThreadUILang = (PSetThreadUILanguage)GetProcAddress( hInstance, "SetThreadUILanguage" );
|
|
|
|
if ( SetThreadUILang )
|
|
(*SetThreadUILang)( 0 );
|
|
|
|
FreeLibrary( hInstance );
|
|
}
|
|
|
|
if(!GetEnvironmentVariableW(L"COMPUTERNAME", g_lpszLocalComputerName, MAX_PATH))
|
|
{
|
|
report_error(GetLastError(), NULL);
|
|
return 1;
|
|
}
|
|
|
|
g_hDllInstance = GetModuleHandle(NULL);
|
|
if (!g_hDllInstance)
|
|
{
|
|
report_error(GetLastError(), g_lpszLocalComputerName);
|
|
return 1;
|
|
}
|
|
|
|
// We use the program name for reporting errors.
|
|
g_lpszProgramName = argv[ 0 ];
|
|
|
|
//
|
|
// Userdomain is used as the default domain.
|
|
//
|
|
if(!GetEnvironmentVariableW(L"USERDOMAIN", g_lpszDefaultDomain, MAX_PATH))
|
|
{
|
|
report_error(GetLastError(), g_lpszLocalComputerName);
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// if there is no arguments, we will display help.
|
|
//
|
|
if(argc == 1)
|
|
{
|
|
usage();
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// If the first argument is -i or /i, we pop up UI.
|
|
//
|
|
if(wcsncmp(argv[1], L"-i", 2) == 0 || wcsncmp(argv[1], L"/i", 2) == 0
|
|
|| wcsncmp(argv[1], L"-I", 2) == 0 || wcsncmp(argv[1], L"/I", 2) == 0)
|
|
{
|
|
if(g_hDllInstance)
|
|
{
|
|
ShutdownHelp shutdownHelp;
|
|
int len = GetEnvironmentVariableW(L"SystemRoot", g_lpszHelpdir, 0);
|
|
if(len)
|
|
{
|
|
g_lpszHelpdir = new WCHAR[len + 6];
|
|
if(g_lpszHelpdir)
|
|
{
|
|
GetEnvironmentVariableW(L"SystemRoot", g_lpszHelpdir, len+6);
|
|
wcscat(g_lpszHelpdir, L"\\");
|
|
wcscat(g_lpszHelpdir, L"Help\\");
|
|
}
|
|
else
|
|
{
|
|
dwRet = ERROR_OUTOFMEMORY;
|
|
goto exit;
|
|
}
|
|
|
|
g_lpszHelpdirHlp = new WCHAR[wcslen(g_lpszHelpdir) + wcslen(HELP_FILE) + 1];
|
|
if (g_lpszHelpdirHlp)
|
|
{
|
|
wcscpy(g_lpszHelpdirHlp, g_lpszHelpdir);
|
|
wcscat(g_lpszHelpdirHlp, HELP_FILE);
|
|
}
|
|
else
|
|
{
|
|
dwRet = ERROR_OUTOFMEMORY;
|
|
goto exit;
|
|
}
|
|
|
|
g_lpszHelpdirChm = new WCHAR[wcslen(g_lpszHelpdir) + wcslen(CHM_FILE) + wcslen(CHM_MAIN) + 1];
|
|
if (g_lpszHelpdirChm)
|
|
{
|
|
wcscpy(g_lpszHelpdirChm, g_lpszHelpdir);
|
|
wcscat(g_lpszHelpdirChm, CHM_FILE);
|
|
wcscat(g_lpszHelpdirChm, CHM_MAIN);
|
|
}
|
|
else
|
|
{
|
|
dwRet = ERROR_OUTOFMEMORY;
|
|
goto exit;
|
|
}
|
|
|
|
g_lpszHelpdirWindows = new WCHAR[wcslen(g_lpszHelpdir) + wcslen(WINDOWS_HELP) + 1];
|
|
if (g_lpszHelpdirWindows)
|
|
{
|
|
wcscpy(g_lpszHelpdirWindows, g_lpszHelpdir);
|
|
wcscat(g_lpszHelpdirWindows, WINDOWS_HELP);
|
|
}
|
|
else
|
|
{
|
|
dwRet = ERROR_OUTOFMEMORY;
|
|
goto exit;
|
|
}
|
|
|
|
}
|
|
hResult = DialogBoxParam(g_hDllInstance, MAKEINTRESOURCE(IDD_DIALOGSHUTDOWN), NULL, Shutdown_DialogProc, NULL);
|
|
dwRet = (DWORD)HRESULTTOWIN32(hResult);
|
|
exit:
|
|
if(g_lpszHelpdir)
|
|
delete [] g_lpszHelpdir;
|
|
if(g_lpszHelpdirHlp)
|
|
delete [] g_lpszHelpdirHlp;
|
|
if(g_lpszHelpdirChm)
|
|
delete [] g_lpszHelpdirChm;
|
|
if(g_lpszHelpdirWindows)
|
|
delete [] g_lpszHelpdirWindows;
|
|
|
|
if (dwRet != 0)
|
|
report_error(dwRet, NULL);
|
|
WinHelpW((HWND)0, NULL, HELP_QUIT, 0) ;
|
|
}
|
|
return dwRet;
|
|
}
|
|
// Parse the options.
|
|
if( !parse_options( argc,
|
|
argv,
|
|
&fLogoff,
|
|
&fForce,
|
|
&fReboot,
|
|
&fHibernate,
|
|
&fAbort,
|
|
&fPoweroff,
|
|
&fAnnotate,
|
|
&lpServerName,
|
|
&lpMessage,
|
|
&dwTimeout,
|
|
&dwReason ))
|
|
{
|
|
usage();
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// Add the comment field for the reason.
|
|
//
|
|
if (g_reasons.RequireComment(dwReason, fAnnotate))
|
|
{
|
|
if (fAnnotate)
|
|
dwReason |= SHTDN_REASON_FLAG_DIRTY_PROBLEM_ID_REQUIRED;
|
|
else
|
|
dwReason |= SHTDN_REASON_FLAG_COMMENT_REQUIRED;
|
|
}
|
|
|
|
//
|
|
// Promote (no more)for comment if it is required and not given
|
|
//
|
|
if (g_reasons.RequireComment(dwReason, fAnnotate) && (!lpMessage || wcslen(lpMessage) == 0))
|
|
{
|
|
lpMessage = NULL;
|
|
}
|
|
|
|
// Get all privileges so that we can shutdown the machine.
|
|
enable_privileges( lpServerName, fLogoff );
|
|
|
|
// Do the work.
|
|
if( fAbort )
|
|
{
|
|
if( !AbortSystemShutdownW( lpServerName ))
|
|
{
|
|
dwRet = GetLastError();
|
|
report_error( dwRet, lpServerName);
|
|
}
|
|
}
|
|
else if (fLogoff)
|
|
{
|
|
if (!ExitWindowsEx(fForce ? (EWX_LOGOFF | EWX_FORCE) : (EWX_LOGOFF | EWX_FORCEIFHUNG),
|
|
0))
|
|
{
|
|
dwRet = GetLastError();
|
|
report_error( dwRet, g_lpszLocalComputerName);
|
|
}
|
|
}
|
|
else if (fHibernate)
|
|
{
|
|
if (!PowerOptionEnabled(POWER_OPTION_HIBERNATE))
|
|
{
|
|
ERROR_WITH_SZ(IDS_ERR_HIBERNATE_NOT_ENABLED, lpServerName, GetLastError());
|
|
}
|
|
else
|
|
{
|
|
Status = NtInitiatePowerAction(
|
|
PowerActionHibernate,
|
|
PowerSystemSleeping1,
|
|
fForce
|
|
? POWER_ACTION_CRITICAL
|
|
: POWER_ACTION_QUERY_ALLOWED,
|
|
FALSE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
dwRet = RtlNtStatusToDosError(Status);
|
|
report_error( dwRet, lpServerName);
|
|
}
|
|
}
|
|
}
|
|
else if (fPoweroff)
|
|
{
|
|
//
|
|
// Special case, we call ExitWindowsEx
|
|
// Check whether power off is supported, if not just shutdown the machine.
|
|
// Although we fixed ExitWindowsEx, but we will leave this so it will work
|
|
// with older builds.
|
|
//
|
|
if(PowerOptionEnabled(POWER_OPTION_POWEROFF))
|
|
{
|
|
if (!ExitWindowsEx(EWX_POWEROFF, dwReason))
|
|
{
|
|
dwRet = GetLastError();
|
|
report_error( dwRet, lpServerName);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!ExitWindowsEx(EWX_SHUTDOWN, dwReason))
|
|
{
|
|
dwRet = GetLastError();
|
|
report_error( dwRet, lpServerName);
|
|
}
|
|
}
|
|
}
|
|
else if (fAnnotate)
|
|
{
|
|
//
|
|
// Annotate dirty shutdown.
|
|
//
|
|
Annotate(lpServerName, &dwReason, lpMessage, &dwRet);
|
|
}
|
|
else
|
|
{
|
|
// Do the normal form.
|
|
if( !InitiateSystemShutdownExW( lpServerName,
|
|
lpMessage,
|
|
dwTimeout,
|
|
fForce,
|
|
fReboot,
|
|
dwReason ))
|
|
{
|
|
dwRet = GetLastError();
|
|
report_error( dwRet, lpServerName);
|
|
}
|
|
}
|
|
|
|
return dwRet;
|
|
}
|
|
|
|
//
|
|
// Get computername from ADSI path
|
|
// Here we only handle WinNT, LDAP, NWCOMPAT, and NDS.
|
|
//
|
|
BOOL GetComputerNameFromPath(LPWSTR szPath, LPWSTR szName)
|
|
{
|
|
static _PROVIDER p[] =
|
|
{
|
|
{L"LDAP://", 7},
|
|
{L"WinNT://", 8},
|
|
{L"NWCOMPAT://", 11},
|
|
{L"NDS://", 6}
|
|
};
|
|
|
|
static UINT np = sizeof(p)/sizeof(_PROVIDER);
|
|
LPWSTR lpsz = NULL;
|
|
|
|
if(!szPath || !szName)
|
|
return FALSE;
|
|
|
|
for(UINT i = 0; i < np; i++)
|
|
{
|
|
if(wcsncmp(szPath, p[i].szName, p[i].dwLen) == 0)
|
|
{
|
|
switch(i)
|
|
{
|
|
case 0: // LDAP
|
|
lpsz = wcsstr(szPath, L"CN=");
|
|
if(!lpsz)
|
|
return FALSE;
|
|
lpsz += 3;
|
|
|
|
while(*lpsz && *lpsz != ',')
|
|
*szName++ = *lpsz++;
|
|
*szName = 0;
|
|
return TRUE;
|
|
case 1: // WinNT
|
|
case 2: // NWCOMPAT
|
|
lpsz = szPath + p[i].dwLen;
|
|
//
|
|
// skip domain or provider path
|
|
//
|
|
while(*lpsz && *lpsz != '/')
|
|
lpsz++;
|
|
lpsz++;
|
|
|
|
while(*lpsz && *lpsz != '/')
|
|
*szName++ = *lpsz++;
|
|
*szName = 0;
|
|
return TRUE;
|
|
case 3: // NDS
|
|
lpsz = wcsstr(szPath, L"CN=");
|
|
if(!lpsz)
|
|
return FALSE;
|
|
lpsz += 3;
|
|
|
|
while(*lpsz && *lpsz != '/')
|
|
*szName++ = *lpsz++;
|
|
*szName = 0;
|
|
return TRUE;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// A centralized place for adjusting window states.
|
|
//
|
|
VOID AdjustWindowState()
|
|
{
|
|
if(g_dwActionSelect == ACTION_SHUTDOWN || g_dwActionSelect == ACTION_RESTART || g_dwActionSelect == ACTION_ANNOTATE)
|
|
{
|
|
if (g_dwActionSelect == ACTION_ANNOTATE)
|
|
{
|
|
EnableWindow(g_wins.hwndButtonWarning, FALSE);
|
|
EnableWindow(g_wins.hwndEditTimeout, FALSE);
|
|
}
|
|
else
|
|
{
|
|
EnableWindow(g_wins.hwndButtonWarning, TRUE);
|
|
if (IsDlgButtonChecked(g_wins.hwndShutdownDialog, IDC_CHECKWARNING) == BST_CHECKED)
|
|
EnableWindow(g_wins.hwndEditTimeout, TRUE);
|
|
else
|
|
EnableWindow(g_wins.hwndEditTimeout, FALSE);
|
|
}
|
|
|
|
EnableWindow(g_wins.hwndEditComment, TRUE);
|
|
if(g_bAssumeShutdown)
|
|
{
|
|
EnableWindow(g_wins.hwndComboOption, FALSE);
|
|
EnableWindow(g_wins.hwndChkPlanned, FALSE);
|
|
EnableWindow(g_wins.hwndButtonOK, TRUE);
|
|
}
|
|
else
|
|
{
|
|
EnableWindow(g_wins.hwndComboOption, TRUE);
|
|
EnableWindow(g_wins.hwndChkPlanned, TRUE);
|
|
if((g_reasons.m_dwReasonSelect != -1)
|
|
&& g_reasons.RequireComment(g_reasons.m_lpReasons[g_reasons.m_dwReasonSelect].dwCode, g_dwActionSelect == ACTION_ANNOTATE))
|
|
{
|
|
LPWSTR szStaticComment = LoadWString(IDS_COMMENT_REQUIRED);
|
|
if(szStaticComment)
|
|
{
|
|
SetWindowTextW(g_wins.hwndStaticComment, szStaticComment);
|
|
LocalFree(szStaticComment);
|
|
}
|
|
if(Edit_GetTextLength(g_wins.hwndEditComment) > MINCOMMENTLEN
|
|
&& ListBox_GetCount(g_wins.hwndListSelectComputers) > 0)
|
|
EnableWindow(g_wins.hwndButtonOK, TRUE);
|
|
else
|
|
EnableWindow(g_wins.hwndButtonOK, FALSE);
|
|
}
|
|
else
|
|
{
|
|
LPWSTR szStaticComment = LoadWString(IDS_COMMENT_OPTIONAL);
|
|
if(szStaticComment)
|
|
{
|
|
SetWindowTextW(g_wins.hwndStaticComment, szStaticComment);
|
|
LocalFree(szStaticComment);
|
|
}
|
|
|
|
if (g_reasons.m_dwReasonSelect == -1)
|
|
EnableWindow(g_wins.hwndComboOption, FALSE);
|
|
|
|
if(ListBox_GetCount(g_wins.hwndListSelectComputers) > 0)
|
|
EnableWindow(g_wins.hwndButtonOK, TRUE);
|
|
else
|
|
EnableWindow(g_wins.hwndButtonOK, FALSE);
|
|
}
|
|
}
|
|
EnableWindow(g_wins.hwndBtnAdd, TRUE);
|
|
EnableWindow(g_wins.hwndBtnBrowse, TRUE);
|
|
|
|
|
|
{
|
|
int cItems = 1024;
|
|
int lpItems[1024];
|
|
int cActualItems;
|
|
|
|
//
|
|
// Get the number of selected items.
|
|
//
|
|
cActualItems = ListBox_GetSelItems(g_wins.hwndListSelectComputers, cItems, lpItems);
|
|
if(cActualItems > 0)
|
|
EnableWindow(g_wins.hwndBtnRemove, TRUE);
|
|
else
|
|
EnableWindow(g_wins.hwndBtnRemove, FALSE);
|
|
}
|
|
EnableWindow(g_wins.hwndListSelectComputers, TRUE);
|
|
}
|
|
else
|
|
{
|
|
EnableWindow(g_wins.hwndChkPlanned, FALSE);
|
|
EnableWindow(g_wins.hwndButtonWarning, FALSE);
|
|
EnableWindow(g_wins.hwndBtnAdd, FALSE);
|
|
EnableWindow(g_wins.hwndBtnBrowse, FALSE);
|
|
EnableWindow(g_wins.hwndBtnRemove, FALSE);
|
|
EnableWindow(g_wins.hwndComboOption, FALSE);
|
|
EnableWindow(g_wins.hwndEditComment, FALSE);
|
|
EnableWindow(g_wins.hwndEditTimeout, FALSE);
|
|
EnableWindow(g_wins.hwndListSelectComputers, FALSE);
|
|
EnableWindow(g_wins.hwndButtonOK, TRUE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Init dialog handler for the shutdown dialog.
|
|
//
|
|
BOOL Shutdown_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam)
|
|
{
|
|
int i;
|
|
|
|
//
|
|
// Init all of the dialog items so we dont have to find
|
|
// them everytime we need them.
|
|
//
|
|
g_wins.hwndShutdownDialog = hwnd;
|
|
g_wins.hwndButtonWarning = GetDlgItem(hwnd, IDC_CHECKWARNING);
|
|
g_wins.hwndComboAction = GetDlgItem(hwnd, IDC_COMBOACTION);
|
|
g_wins.hwndComboOption = GetDlgItem(hwnd, IDC_COMBOOPTION);
|
|
g_wins.hwndEditComment = GetDlgItem(hwnd, IDC_EDITCOMMENT);
|
|
g_wins.hwndStaticDesc = GetDlgItem(hwnd, IDC_STATICDESCRIPTION);
|
|
g_wins.hwndEditTimeout = GetDlgItem(hwnd, IDC_EDITTIMEOUT);
|
|
g_wins.hwndListSelectComputers = GetDlgItem(hwnd, IDC_LISTSELECTEDCOMPUTERS);
|
|
g_wins.hwndBtnAdd = GetDlgItem(hwnd, IDC_BUTTONADDNEW);
|
|
g_wins.hwndBtnBrowse = GetDlgItem(hwnd, IDC_BUTTONBROWSE);
|
|
g_wins.hwndBtnRemove = GetDlgItem(hwnd, IDC_BUTTONREMOVE);
|
|
g_wins.hwndChkPlanned = GetDlgItem(hwnd, IDC_CHECK_PLANNED);
|
|
g_wins.hwndButtonOK = GetDlgItem(hwnd, IDOK);
|
|
g_wins.hwndStaticComment = GetDlgItem(hwnd, IDC_STATIC_COMMENT);
|
|
|
|
if(g_wins.hwndButtonWarning == NULL
|
|
|| g_wins.hwndComboAction == NULL
|
|
|| g_wins.hwndComboOption == NULL
|
|
|| g_wins.hwndEditComment == NULL
|
|
|| g_wins.hwndStaticDesc == NULL
|
|
|| g_wins.hwndEditTimeout == NULL
|
|
|| g_wins.hwndListSelectComputers == NULL
|
|
|| g_wins.hwndBtnAdd == NULL
|
|
|| g_wins.hwndBtnBrowse == NULL
|
|
|| g_wins.hwndBtnRemove == NULL
|
|
|| g_wins.hwndChkPlanned == NULL)
|
|
{
|
|
report_error( GetLastError( ), NULL);
|
|
EndDialog(hwnd, (int)-1);
|
|
return FALSE;
|
|
}
|
|
|
|
LoadString(g_hDllInstance, IDS_DIALOGTITLEWARNING, g_lpszTitleWarning, TITLEWARNINGLEN);
|
|
|
|
// Subclass the edit control.
|
|
wpOrigEditProc = (WNDPROC) SetWindowLongPtr(g_wins.hwndEditTimeout,
|
|
GWLP_WNDPROC, (LONG_PTR) EditSubclassProc);
|
|
|
|
// Limit timeout to 3 chars.
|
|
SendMessage(g_wins.hwndEditTimeout, EM_LIMITTEXT, (WPARAM)3, 0);
|
|
|
|
//
|
|
// Default timeout is set to 30 seconds.
|
|
//
|
|
Edit_SetText(g_wins.hwndEditTimeout, g_lpszDefaultTimeout);
|
|
if(! CheckDlgButton(hwnd, IDC_CHECKWARNING, DEFAULTWARNINGSTATE))
|
|
{
|
|
report_error( GetLastError( ), NULL);
|
|
EndDialog(hwnd, (int)-1);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// The for loop will load all of the actions into action combo.
|
|
// in the meantime we save them for later use.
|
|
//
|
|
for(i = 0; i < g_nActions; i++)
|
|
{
|
|
LoadString(g_hDllInstance, g_dwActions[i], g_lppszActions[i], MAX_PATH - 1);
|
|
ComboBox_AddString(g_wins.hwndComboAction, g_lppszActions[i]);
|
|
if(g_dwActions[i] == IDS_ACTION_RESTART)
|
|
{
|
|
ComboBox_SelectString(g_wins.hwndComboAction, -1, g_lppszActions[i]);
|
|
g_dwActionSelect = ACTION_RESTART;
|
|
}
|
|
}
|
|
|
|
if(g_reasons.m_cReasons)
|
|
g_bAssumeShutdown = FALSE;
|
|
else
|
|
g_bAssumeShutdown = TRUE;
|
|
|
|
//
|
|
// Set the default to be planned.
|
|
//
|
|
CheckDlgButton(hwnd, IDC_CHECK_PLANNED, BST_CHECKED);
|
|
|
|
g_reasons.FillCombo(g_wins.hwndComboOption, g_bDirty,
|
|
IsDlgButtonChecked(hwnd, IDC_CHECK_PLANNED) == BST_CHECKED, g_wins.hwndStaticDesc);
|
|
|
|
//
|
|
// Setup the comment box.
|
|
// We must fix the maximum characters.
|
|
//
|
|
SendMessage( g_wins.hwndEditComment, EM_LIMITTEXT, (WPARAM)MAX_REASON_COMMENT_LEN-1, (LPARAM)0 );
|
|
|
|
AdjustWindowState();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Init dialog handler for browse dialog
|
|
//
|
|
BOOL Browse_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam)
|
|
{
|
|
HWND hwndDomain = NULL;
|
|
int cItems = 1024;
|
|
int lpItems[1024];
|
|
int cActualItems;
|
|
WCHAR lpDomain[MAX_PATH];
|
|
|
|
hwndDomain = GetDlgItem(hwnd, IDC_EDITDOMAIN);
|
|
|
|
if(!hwndDomain)
|
|
return FALSE;
|
|
|
|
Edit_SetText(hwndDomain, g_lpszDefaultDomain);;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// winproc for shutdown dialog
|
|
//
|
|
INT_PTR CALLBACK Shutdown_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
HANDLE_MSG(hwnd, WM_INITDIALOG, Shutdown_OnInitDialog);
|
|
HANDLE_MSG(hwnd, WM_COMMAND, Shutdown_OnCommand);
|
|
|
|
case WM_SYSCOMMAND:
|
|
return (Shutdown_OnCommand(hwnd, (int)(LOWORD(wParam)), (HWND)(lParam), (UINT)HIWORD(wParam)), 0L);
|
|
|
|
case WM_HELP: // F1
|
|
if(g_lpszHelpdirHlp)
|
|
{
|
|
LPHELPINFO phinfo = (LPHELPINFO) lParam;
|
|
DWORD dwHelpID = 0;
|
|
|
|
if (IsStaticControl((HWND)phinfo->hItemHandle))
|
|
return (TRUE);
|
|
|
|
for(int i = 0; i < sizeof(ShutdownDialogHelpIds)/sizeof(DWORD); i++)
|
|
{
|
|
if(ShutdownDialogHelpIds[i] == phinfo->iCtrlId)
|
|
{
|
|
dwHelpID = ShutdownDialogHelpIds[++i];
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
if ( i == sizeof(ShutdownDialogHelpIds)/sizeof(DWORD))
|
|
return TRUE;
|
|
|
|
if (dwHelpID == 28443 || dwHelpID == 28444 || dwHelpID == 28445) // special case.
|
|
WinHelpW((HWND)phinfo->hItemHandle, g_lpszHelpdirWindows, HELP_CONTEXTPOPUP,dwHelpID);
|
|
else
|
|
WinHelpW((HWND)phinfo->hItemHandle, g_lpszHelpdirHlp, HELP_CONTEXTPOPUP,dwHelpID);
|
|
}
|
|
return (TRUE);
|
|
|
|
case WM_CONTEXTMENU: // right mouse click
|
|
if (IsStaticControl((HWND)wParam))
|
|
return (TRUE);
|
|
|
|
if(g_lpszHelpdirHlp)
|
|
WinHelpW((HWND)wParam, g_lpszHelpdirHlp, HELP_CONTEXTMENU,(DWORD_PTR)(LPSTR)&ShutdownDialogHelpIds[6]);
|
|
return (TRUE);
|
|
case WM_DESTROY:
|
|
// Remove the subclass from the edit control.
|
|
SetWindowLongPtr(g_wins.hwndEditTimeout, GWLP_WNDPROC,
|
|
(LONG_PTR)wpOrigEditProc);
|
|
//
|
|
// Continue the cleanup procedure.
|
|
//
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// winproc for AddNew dialog
|
|
//
|
|
INT_PTR CALLBACK AddNew_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
HANDLE_MSG(hwnd, WM_COMMAND, AddNew_OnCommand);
|
|
|
|
case WM_HELP: // F1
|
|
if (GetDlgCtrlID((HWND)wParam) == IDC_STATIC)
|
|
return (TRUE);
|
|
if(g_lpszHelpdirHlp)
|
|
{
|
|
LPHELPINFO phinfo = (LPHELPINFO) lParam;
|
|
DWORD dwHelpID = 0;
|
|
|
|
if (IsStaticControl((HWND)phinfo->hItemHandle))
|
|
return (TRUE);
|
|
|
|
for(int i = 0; i < sizeof(AddNewDialogHelpIds)/sizeof(DWORD); i++)
|
|
{
|
|
if(AddNewDialogHelpIds[i] == phinfo->iCtrlId)
|
|
{
|
|
dwHelpID = AddNewDialogHelpIds[++i];
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
if ( i == sizeof(AddNewDialogHelpIds)/sizeof(DWORD))
|
|
return TRUE;
|
|
|
|
if (dwHelpID == 28443 || dwHelpID == 28444 || dwHelpID == 28445) // special case.
|
|
WinHelpW((HWND)phinfo->hItemHandle, g_lpszHelpdirWindows, HELP_CONTEXTPOPUP,dwHelpID);
|
|
else
|
|
WinHelpW((HWND)phinfo->hItemHandle, g_lpszHelpdirHlp, HELP_CONTEXTPOPUP,dwHelpID);
|
|
}
|
|
return (TRUE);
|
|
|
|
case WM_CONTEXTMENU: // right mouse click
|
|
if (IsStaticControl((HWND)wParam))
|
|
return (TRUE);
|
|
if(g_lpszHelpdirHlp)
|
|
WinHelpW((HWND)wParam, g_lpszHelpdirHlp, HELP_CONTEXTMENU,(DWORD_PTR)(LPSTR)&AddNewDialogHelpIds[4]);
|
|
return (TRUE);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Command handler for the shutdown dialog.
|
|
//
|
|
BOOL Shutdown_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
|
|
{
|
|
BOOL fHandled = FALSE;
|
|
DWORD dwDlgResult = 0;
|
|
HINSTANCE h;
|
|
|
|
switch (id)
|
|
{
|
|
case IDCANCEL:
|
|
if (codeNotify == BN_CLICKED)
|
|
{
|
|
EndDialog(hwnd, (int) dwDlgResult);
|
|
}
|
|
fHandled = TRUE;
|
|
break;
|
|
case SC_CLOSE:
|
|
EndDialog(hwnd, (int) dwDlgResult);
|
|
fHandled = TRUE;
|
|
break;
|
|
case IDC_BUTTONREMOVE:
|
|
if (codeNotify == BN_CLICKED)
|
|
{
|
|
int cItems = 1024;
|
|
int lpItems[1024];
|
|
int cActualItems;
|
|
WCHAR lpServerName[MAX_PATH];
|
|
|
|
//
|
|
// Get the number of selected items. If there is any remove them one by one.
|
|
//
|
|
cActualItems = ListBox_GetSelItems(g_wins.hwndListSelectComputers, cItems, lpItems);
|
|
if(cActualItems > 0)
|
|
{
|
|
int i;
|
|
for(i = cActualItems-1; i >= 0; i--)
|
|
{
|
|
ListBox_DeleteString(g_wins.hwndListSelectComputers, lpItems[i]);
|
|
}
|
|
|
|
AdjustWindowState();
|
|
SetFocus(g_wins.hwndListSelectComputers);
|
|
}
|
|
fHandled = TRUE;
|
|
}
|
|
break;
|
|
case IDC_CHECK_PLANNED:
|
|
if (codeNotify == BN_CLICKED)
|
|
{
|
|
int iOption;
|
|
int iFirst = -1;
|
|
DWORD dwCheckState = 0x0;
|
|
|
|
//
|
|
// Get check button state.
|
|
//
|
|
if (IsDlgButtonChecked(hwnd, IDC_CHECK_PLANNED) == BST_CHECKED)
|
|
dwCheckState = SHTDN_REASON_FLAG_PLANNED;
|
|
|
|
g_reasons.FillCombo(g_wins.hwndComboOption, g_bDirty,
|
|
IsDlgButtonChecked(hwnd, IDC_CHECK_PLANNED) == BST_CHECKED, g_wins.hwndStaticDesc);
|
|
|
|
AdjustWindowState();
|
|
fHandled = TRUE;
|
|
}
|
|
break;
|
|
case IDC_EDITCOMMENT:
|
|
if( codeNotify == EN_CHANGE)
|
|
{
|
|
if(g_bAssumeShutdown)
|
|
{
|
|
EnableWindow(g_wins.hwndButtonOK, TRUE);
|
|
}
|
|
else if(g_reasons.m_dwReasonSelect != -1 &&
|
|
(g_reasons.m_lpReasons[g_reasons.m_dwReasonSelect].dwCode & SHTDN_REASON_FLAG_COMMENT_REQUIRED))
|
|
{
|
|
if(Edit_GetTextLength(g_wins.hwndEditComment) > MINCOMMENTLEN
|
|
&& ListBox_GetCount(g_wins.hwndListSelectComputers) > 0)
|
|
EnableWindow(g_wins.hwndButtonOK, TRUE);
|
|
else
|
|
EnableWindow(g_wins.hwndButtonOK, FALSE);
|
|
}
|
|
else
|
|
{
|
|
if(ListBox_GetCount(g_wins.hwndListSelectComputers) > 0)
|
|
EnableWindow(g_wins.hwndButtonOK, TRUE);
|
|
else
|
|
EnableWindow(g_wins.hwndButtonOK, FALSE);
|
|
}
|
|
fHandled = TRUE;
|
|
}
|
|
break;
|
|
case IDC_EDITTIMEOUT:
|
|
if( codeNotify == EN_KILLFOCUS)
|
|
{
|
|
WCHAR szTimeout[8];
|
|
int len = GetWindowTextW(g_wins.hwndEditTimeout, szTimeout, 7);
|
|
fHandled = TRUE;
|
|
|
|
if (_wtoi(szTimeout) > MAX_TIMEOUT)
|
|
SetWindowTextW(g_wins.hwndEditTimeout, g_lpszMaxTimeout);
|
|
}
|
|
break;
|
|
|
|
case IDC_BUTTONADDNEW:
|
|
if (codeNotify == BN_CLICKED)
|
|
{
|
|
WCHAR lpServerName[MAX_PATH];
|
|
LPWSTR lpBuffer;
|
|
DWORD dwIndex = 0;
|
|
INT_PTR hResult;
|
|
|
|
//
|
|
// Will pop up the addnew dialog. User can type in computer names seperated
|
|
// by white space. After click on OK, we will parse the computer names and
|
|
// add them to the selected computer list. No duplicates will be added.
|
|
//
|
|
hResult = DialogBoxParam(g_hDllInstance, MAKEINTRESOURCE(IDD_DIALOG_ADDNEW), hwnd, AddNew_DialogProc, NULL);
|
|
if(g_lpszNewComputers)
|
|
{
|
|
lpBuffer = g_lpszNewComputers;
|
|
while(*lpBuffer)
|
|
{
|
|
lpServerName[dwIndex] = '\0';
|
|
while(*lpBuffer && *lpBuffer != '\t' && *lpBuffer != '\n' && *lpBuffer != '\r' && *lpBuffer != ' ')
|
|
lpServerName[dwIndex++] = *lpBuffer++;
|
|
lpServerName[dwIndex] = '\0';
|
|
if(dwIndex > 0 && LB_ERR == ListBox_FindStringExact(g_wins.hwndListSelectComputers, -1, lpServerName))
|
|
ListBox_AddString(g_wins.hwndListSelectComputers, lpServerName);
|
|
dwIndex = 0;
|
|
while(*lpBuffer && (*lpBuffer == '\t' || *lpBuffer == '\n' || *lpBuffer == '\r' || *lpBuffer == ' '))
|
|
lpBuffer++;
|
|
}
|
|
AdjustWindowState();
|
|
LocalFree((HLOCAL)g_lpszNewComputers);
|
|
g_lpszNewComputers = NULL;
|
|
}
|
|
fHandled = TRUE;
|
|
}
|
|
break;
|
|
case IDOK:
|
|
//
|
|
// Here we gather all of the information and do the action.
|
|
//
|
|
if (codeNotify == BN_CLICKED)
|
|
{
|
|
int cItems = 1024;
|
|
int lpItems[1024];
|
|
int cActualItems;
|
|
BOOL fLogoff = FALSE;
|
|
BOOL fAbort = FALSE;
|
|
BOOL fForce = FALSE;
|
|
BOOL fReboot = FALSE;
|
|
BOOL fDisconnect = FALSE;
|
|
BOOL fStandby = FALSE;
|
|
BOOL fAnnotate = FALSE;
|
|
DWORD dwTimeout = 0;
|
|
DWORD dwReasonCode = 0;
|
|
WCHAR lpServerName[MAX_PATH];
|
|
WCHAR lpMsg[MAX_REASON_COMMENT_LEN] = L"";
|
|
DWORD dwCnt = 0;
|
|
DWORD dwActionCode = g_dwActionSelect;
|
|
WCHAR lpNotSupported[MAX_PATH];
|
|
WCHAR lpRes[MAX_PATH * 2];
|
|
WCHAR lpFailed[MAX_PATH];
|
|
WCHAR lpSuccess[MAX_PATH];
|
|
|
|
//
|
|
// The default reason code is 0 and default comment is L"".
|
|
//
|
|
if(IsDlgButtonChecked(hwnd, IDC_CHECKWARNING))
|
|
{
|
|
fForce = FALSE;
|
|
lpServerName[0] = '\0';
|
|
GetWindowText(g_wins.hwndEditTimeout, lpServerName, MAX_PATH);
|
|
if(lstrlen(lpServerName) == 0)
|
|
dwTimeout = 0;
|
|
else dwTimeout = _wtoi(lpServerName);
|
|
if(dwTimeout > MAX_TIMEOUT)
|
|
dwTimeout = MAX_TIMEOUT;
|
|
}
|
|
else
|
|
{
|
|
fForce = TRUE;
|
|
}
|
|
|
|
LoadString(g_hDllInstance, IDS_ACTIONNOTSUPPORTED, lpNotSupported, MAX_PATH);
|
|
GetWindowText(g_wins.hwndEditComment, lpMsg, MAX_REASON_COMMENT_LEN);
|
|
lpMsg[MAX_REASON_COMMENT_LEN-1] = 0;
|
|
|
|
|
|
if(dwActionCode == ACTION_LOGOFF)
|
|
{
|
|
fLogoff = TRUE;
|
|
}
|
|
else if (dwActionCode == ACTION_RESTART)
|
|
{
|
|
fReboot = TRUE;
|
|
}
|
|
else if (dwActionCode == ACTION_ANNOTATE)
|
|
fAnnotate = TRUE;
|
|
|
|
//
|
|
// Logoff is only for the local computer.
|
|
// Everything else will ingored.
|
|
//
|
|
if(fLogoff)
|
|
{
|
|
if (!ExitWindowsEx(fForce ? EWX_LOGOFF : (EWX_LOGOFF | EWX_FORCE),
|
|
0))
|
|
{
|
|
report_error(GetLastError(), g_lpszLocalComputerName);
|
|
}
|
|
EndDialog(hwnd, (int) dwDlgResult);
|
|
break;
|
|
}
|
|
|
|
if(! g_bAssumeShutdown)
|
|
{
|
|
dwReasonCode = g_reasons.m_lpReasons[g_reasons.m_dwReasonSelect].dwCode;
|
|
}
|
|
else if (fAnnotate)
|
|
break;
|
|
|
|
dwCnt = ListBox_GetCount(g_wins.hwndListSelectComputers);
|
|
if(dwCnt > 0)
|
|
{
|
|
DWORD i;
|
|
for(i = 0; i < dwCnt; i++)
|
|
{
|
|
ListBox_GetText(g_wins.hwndListSelectComputers, i, lpServerName);
|
|
|
|
|
|
//
|
|
// Get all privileges so that we can shutdown the machine.
|
|
//
|
|
enable_privileges(lpServerName, fLogoff);
|
|
|
|
//
|
|
// Do the work.
|
|
//
|
|
if( fAbort )
|
|
{
|
|
if( !AbortSystemShutdown( lpServerName ))
|
|
{
|
|
report_error( GetLastError( ), lpServerName);
|
|
}
|
|
}
|
|
else if (fAnnotate)
|
|
{
|
|
DWORD err;
|
|
Annotate(lpServerName, &dwReasonCode, lpMsg, &err);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Do the normal form.
|
|
//
|
|
if( !InitiateSystemShutdownEx( lpServerName,
|
|
lpMsg,
|
|
dwTimeout,
|
|
fForce,
|
|
fReboot,
|
|
dwReasonCode ))
|
|
{
|
|
report_error( GetLastError( ), lpServerName);
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We will keep the dialog up in case user forget to add computers.
|
|
//
|
|
break;
|
|
}
|
|
EndDialog(hwnd, (int) dwDlgResult);
|
|
}
|
|
break;
|
|
case IDC_CHECKWARNING:
|
|
//
|
|
// The checkbutton state decides the state of the timeout edit box.
|
|
//
|
|
if (codeNotify == BN_CLICKED)
|
|
{
|
|
if(BST_CHECKED == IsDlgButtonChecked(hwnd, IDC_CHECKWARNING))
|
|
{
|
|
EnableWindow(g_wins.hwndEditTimeout, TRUE);
|
|
}
|
|
else
|
|
{
|
|
EnableWindow(g_wins.hwndEditTimeout, FALSE);
|
|
}
|
|
fHandled = TRUE;
|
|
}
|
|
break;
|
|
case IDC_BUTTONBROWSE:
|
|
//
|
|
// Simply pop up the browse dialog. That dialog will be responsible
|
|
// for adding the user selection to the selected computer list.
|
|
//
|
|
if (codeNotify == BN_CLICKED)
|
|
{
|
|
HRESULT hr;
|
|
ICommonQuery* pcq;
|
|
OPENQUERYWINDOW oqw = { 0 };
|
|
DSQUERYINITPARAMS dqip = { 0 };
|
|
IDataObject *pdo;
|
|
|
|
FORMATETC fmte = {(CLIPFORMAT)RegisterClipboardFormat(CFSTR_DSOBJECTNAMES),
|
|
NULL,
|
|
DVASPECT_CONTENT,
|
|
-1,
|
|
TYMED_HGLOBAL};
|
|
FORMATETC fmte2 = {(CLIPFORMAT)RegisterClipboardFormat(CFSTR_DSQUERYPARAMS),
|
|
NULL,
|
|
DVASPECT_CONTENT,
|
|
-1,
|
|
TYMED_HGLOBAL};
|
|
STGMEDIUM medium = { TYMED_NULL, NULL, NULL };
|
|
|
|
DSOBJECTNAMES *pdon;
|
|
DSQUERYPARAMS *pdqp;
|
|
|
|
CoInitialize(NULL);
|
|
|
|
//
|
|
// Windows 2000: Always use IID_ICommonQueryW explicitly. IID_ICommonQueryA is not supported.
|
|
//
|
|
hr = CoCreateInstance(CLSID_CommonQuery, NULL, CLSCTX_INPROC_SERVER, IID_ICommonQuery, (void**)&pcq);
|
|
if (FAILED(hr)) {
|
|
//
|
|
// if failed return.
|
|
//
|
|
CoUninitialize();
|
|
DbgPrint("Cannot create ICommonQuery, return.\n");
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Initialize the OPENQUERYWINDOW structure to indicate
|
|
// we want to do a Directory Service
|
|
// Query, the default form is printers and the search
|
|
// should start once the window is initialized.
|
|
//
|
|
oqw.cbStruct = sizeof(oqw);
|
|
oqw.dwFlags = OQWF_OKCANCEL|OQWF_DEFAULTFORM|OQWF_HIDEMENUS|OQWF_REMOVEFORMS;
|
|
oqw.clsidHandler = CLSID_DsQuery;
|
|
oqw.pHandlerParameters = &dqip;
|
|
oqw.clsidDefaultForm = CLSID_DsFindComputer;
|
|
|
|
//
|
|
// Now initialize the handler specific parameters,
|
|
// in this case, the File/Save menu item is removed
|
|
//
|
|
dqip.cbStruct = sizeof(dqip);
|
|
dqip.dwFlags = DSQPF_NOSAVE;
|
|
|
|
|
|
//
|
|
// Call OpenQueryWindow, it will block until
|
|
// the Query Window is dismissed,
|
|
//
|
|
hr = pcq->OpenQueryWindow(hwnd, &oqw, &pdo);
|
|
if (FAILED(hr)) {
|
|
//
|
|
// if failed we return.
|
|
//
|
|
pcq->Release();
|
|
CoUninitialize();
|
|
break;
|
|
}
|
|
|
|
if (!pdo) {
|
|
//
|
|
// if cancelled,nothing needs to be done.
|
|
//
|
|
pcq->Release();
|
|
CoUninitialize();
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Get the CFSTR_DSOBJECTNAMES data. For each selected, the data
|
|
// includes the object class and an ADsPath to the selected object.
|
|
//
|
|
hr = pdo->GetData(&fmte, &medium);
|
|
|
|
if(! FAILED(hr))
|
|
{
|
|
pdon = (DSOBJECTNAMES*)GlobalLock(medium.hGlobal);
|
|
|
|
if ( pdon )
|
|
{
|
|
WCHAR szName[MAX_PATH];
|
|
LPWSTR lpsz = NULL;
|
|
UINT i;
|
|
|
|
for (i = 0; i < pdon->cItems; i++)
|
|
{
|
|
if(!GetComputerNameFromPath((LPWSTR) ((PBYTE) pdon + pdon->aObjects[i].offsetName), szName))
|
|
continue;
|
|
|
|
//
|
|
// We don't add dups.
|
|
//
|
|
if(LB_ERR == ListBox_FindStringExact(g_wins.hwndListSelectComputers, -1, szName))
|
|
{
|
|
ListBox_AddString(g_wins.hwndListSelectComputers, szName);
|
|
}
|
|
}
|
|
|
|
GlobalUnlock(medium.hGlobal);
|
|
}
|
|
else
|
|
{
|
|
DbgPrint("GlobalLock on medium failed.\n");
|
|
}
|
|
ReleaseStgMedium(&medium);
|
|
}
|
|
else
|
|
{
|
|
DbgPrint("pdo->GetData failed: 0x%x\n", hr);
|
|
}
|
|
|
|
//
|
|
// Release resources.
|
|
//
|
|
pdo->Release();
|
|
pcq->Release();
|
|
|
|
CoUninitialize();
|
|
AdjustWindowState();
|
|
fHandled = TRUE;
|
|
}
|
|
break;
|
|
case IDC_COMBOOPTION:
|
|
//
|
|
// Here is where you select shutdown reasons.
|
|
//
|
|
if (codeNotify == CBN_SELCHANGE)
|
|
{
|
|
g_reasons.SetDesc(g_wins.hwndComboOption, g_wins.hwndStaticDesc);
|
|
AdjustWindowState();
|
|
fHandled = TRUE;
|
|
}
|
|
break;
|
|
case IDC_COMBOACTION:
|
|
//
|
|
// Select user action here.
|
|
// according to the action. some item will be disabled or enabled.
|
|
//
|
|
if (codeNotify == CBN_SELCHANGE)
|
|
{
|
|
WCHAR name[MAX_PATH];
|
|
DWORD dwIndex;
|
|
DWORD dwOldActionSelect = g_dwActionSelect;
|
|
DWORD dwCheckState = 0x0;
|
|
int iFirst = -1;
|
|
|
|
if (IsDlgButtonChecked(hwnd, IDC_CHECK_PLANNED) == BST_CHECKED)
|
|
dwCheckState = SHTDN_REASON_FLAG_PLANNED;
|
|
|
|
GetWindowText(g_wins.hwndComboAction, (LPWSTR)name, MAX_PATH);
|
|
for(dwIndex = 0; dwIndex < g_nActions; dwIndex++)
|
|
{
|
|
if(lstrcmp(name, g_lppszActions[dwIndex]) == 0)
|
|
{
|
|
g_dwActionSelect = dwIndex;
|
|
|
|
if(g_dwActionSelect == ACTION_ANNOTATE)
|
|
g_bDirty = TRUE;
|
|
else
|
|
g_bDirty = FALSE;
|
|
|
|
//
|
|
// If change from clean to dirty or vsv, repopulate the combo.
|
|
//
|
|
if (dwOldActionSelect != g_dwActionSelect
|
|
&& (dwOldActionSelect == ACTION_ANNOTATE || g_dwActionSelect == ACTION_ANNOTATE))
|
|
{
|
|
g_reasons.FillCombo(g_wins.hwndComboOption, g_bDirty,
|
|
dwCheckState == SHTDN_REASON_FLAG_PLANNED, g_wins.hwndStaticDesc);
|
|
}
|
|
AdjustWindowState();
|
|
break;
|
|
}
|
|
}
|
|
fHandled = TRUE;
|
|
}
|
|
break;
|
|
case IDC_LISTSELECTEDCOMPUTERS:
|
|
//
|
|
// When selection change, update the remove button state.
|
|
//
|
|
if (codeNotify == LBN_SELCHANGE)
|
|
{
|
|
AdjustWindowState();
|
|
fHandled = TRUE;
|
|
}
|
|
else if (codeNotify == WM_MOUSEMOVE)
|
|
{
|
|
MessageBox(hwnd, L"Mouse move", NULL, 0);
|
|
fHandled = TRUE;
|
|
}
|
|
break;
|
|
case IDHELP:
|
|
//
|
|
// Open the .chm file.
|
|
//
|
|
if (codeNotify == BN_CLICKED)
|
|
{
|
|
if(g_lpszHelpdirChm)
|
|
HtmlHelpW(/*hwnd*/0, g_lpszHelpdirChm, HH_DISPLAY_TOPIC,(DWORD)0);
|
|
}
|
|
}
|
|
return fHandled;
|
|
}
|
|
|
|
//
|
|
// Command handler for the addnew dialog.
|
|
// It simply copy the text into a allocated buffer when OK is clicked.
|
|
//
|
|
BOOL AddNew_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
|
|
{
|
|
BOOL fHandled = FALSE;
|
|
DWORD dwDlgResult = 0;
|
|
HINSTANCE h;
|
|
|
|
switch (id)
|
|
{
|
|
case IDOK:
|
|
if (codeNotify == BN_CLICKED)
|
|
{
|
|
HWND hwndEdit;
|
|
DWORD dwTextlen = 0;
|
|
|
|
hwndEdit = GetDlgItem(hwnd, IDC_EDIT_ADDCOMPUTERS_COMPUTERS);
|
|
if(hwndEdit != NULL)
|
|
{
|
|
dwTextlen = Edit_GetTextLength(hwndEdit);
|
|
if(dwTextlen)
|
|
{
|
|
g_lpszNewComputers = (LPWSTR)LocalAlloc(LMEM_FIXED, sizeof(TCHAR) * (dwTextlen + 1));
|
|
if(g_lpszNewComputers){
|
|
Edit_GetText(hwndEdit, g_lpszNewComputers, dwTextlen + 1);
|
|
}
|
|
}
|
|
}
|
|
EndDialog(hwnd, (int) dwDlgResult);
|
|
}
|
|
break;
|
|
case IDCANCEL:
|
|
if (codeNotify == BN_CLICKED)
|
|
{
|
|
EndDialog(hwnd, (int) dwDlgResult);
|
|
}
|
|
break;
|
|
}
|
|
return fHandled;
|
|
}
|
|
|
|
//
|
|
// Annotate dirty shutdown on remote machine.
|
|
BOOL
|
|
Annotate(
|
|
LPCWSTR lpMachine,
|
|
LPDWORD lpdwReason,
|
|
LPCWSTR lpComment,
|
|
LPDWORD lpdwErr)
|
|
{
|
|
HKEY hRemote = NULL;
|
|
HKEY hKey = NULL;
|
|
HANDLE hEventLog = NULL;
|
|
PTOKEN_USER pTokenUser = NULL;
|
|
BOOL res = TRUE;
|
|
|
|
if(lpMachine && wcslen(lpMachine) > 1)
|
|
{
|
|
WCHAR szMachine[MAX_PATH];
|
|
szMachine[0] = '\0';
|
|
if(lpMachine[0] != '\\')
|
|
{
|
|
wcscpy(szMachine, L"\\\\");
|
|
}
|
|
wcsncat(szMachine, lpMachine, MAX_PATH - 3);
|
|
szMachine[MAX_PATH - 1] = '\0';
|
|
|
|
RegConnectRegistryW(szMachine, HKEY_LOCAL_MACHINE, &hRemote);
|
|
if(!hRemote)
|
|
{
|
|
*lpdwErr = GetLastError();
|
|
ERROR_WITH_SZ(IDS_FAILED_REG_CONN, lpMachine, *lpdwErr);
|
|
goto fail;
|
|
}
|
|
|
|
RegOpenKeyExW(hRemote,
|
|
REGSTR_PATH_RELIABILITY,
|
|
NULL,
|
|
KEY_ALL_ACCESS | KEY_WOW64_64KEY,
|
|
&hKey);
|
|
if(!hKey)
|
|
{
|
|
*lpdwErr = GetLastError();
|
|
ERROR_WITH_SZ(IDS_FAILED_REG_OPEN, lpMachine, *lpdwErr);
|
|
goto fail;
|
|
}
|
|
}
|
|
else // local machine
|
|
{
|
|
lpMachine = g_lpszLocalComputerName;
|
|
RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
|
REGSTR_PATH_RELIABILITY,
|
|
NULL,
|
|
KEY_ALL_ACCESS | KEY_WOW64_64KEY,
|
|
&hKey);
|
|
if(!hKey)
|
|
{
|
|
*lpdwErr = GetLastError();
|
|
ERROR_WITH_SZ(IDS_FAILED_REG_OPEN, lpMachine, *lpdwErr);
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
DWORD dwDirtyVal;
|
|
DWORD dwType;
|
|
DWORD dwSize = sizeof(DWORD);
|
|
|
|
static LPCWSTR lpszDirtyValName = L"DirtyShutdown";
|
|
|
|
if( ERROR_SUCCESS != RegQueryValueExW(hKey, lpszDirtyValName, NULL, &dwType, (LPBYTE)&dwDirtyVal,&dwSize))
|
|
{
|
|
*lpdwErr = GetLastError();
|
|
ERROR_WITH_SZ(IDS_NO_DIRTY_SHUTDOWN, lpMachine, *lpdwErr);
|
|
goto fail;
|
|
}
|
|
|
|
if (dwDirtyVal)
|
|
{
|
|
PSID pUserSid = NULL;
|
|
DWORD dwSidSize = sizeof(SID), dwEventID;
|
|
WCHAR szReason[MAX_REASON_NAME_LEN + 10];
|
|
LPCWSTR lpStrings[8];
|
|
WORD wEventType, wStringCnt;
|
|
WCHAR szMinorReason[32];
|
|
WCHAR szName[MAX_PATH + 1];
|
|
WCHAR szDomain[MAX_PATH + 1];
|
|
DWORD dwNameLen = MAX_PATH + 1;
|
|
DWORD dwDomainLen = MAX_PATH + 1;
|
|
SID_NAME_USE eUse;
|
|
|
|
// Get the user's SID so we can output their account name to the event log.
|
|
if (GetUserSid(&pTokenUser)) {
|
|
pUserSid = pTokenUser->User.Sid;
|
|
}
|
|
|
|
if ((hEventLog = RegisterEventSourceW(lpMachine, L"USER32")) == NULL) {
|
|
*lpdwErr = GetLastError();
|
|
ERROR_WITH_SZ(IDS_FAILED_EVENT_REG, lpMachine, *lpdwErr);
|
|
goto fail;
|
|
}
|
|
|
|
g_reasons.GetReasonTitle(*lpdwReason, szReason, ARRAY_SIZE(szReason));
|
|
|
|
// Get User name.
|
|
if (!LookupAccountSidW(NULL, pUserSid, szName, &dwNameLen, szDomain,
|
|
&dwDomainLen, &eUse)) {
|
|
goto fail;
|
|
}
|
|
szName[MAX_PATH] = 0;
|
|
szDomain[MAX_PATH] = 0;
|
|
|
|
// We need to pack into a buffer of MAX_PATH + 1 in the form L"domain\\username"
|
|
if (wcslen(szDomain) + wcslen(szName) > MAX_PATH - 1) {
|
|
goto fail;
|
|
}
|
|
if (wcslen(szDomain) > 0) {
|
|
wcscat(szDomain, L"\\");
|
|
}
|
|
wcscat(szDomain, szName);
|
|
|
|
// The minor reason is the low-order word of the reason code.
|
|
wsprintf(szMinorReason, L"0x%x", *lpdwReason);
|
|
wEventType = EVENTLOG_WARNING_TYPE;
|
|
dwEventID = WARNING_DIRTY_REBOOT;
|
|
wStringCnt = 6;
|
|
lpStrings[0] = szReason;
|
|
lpStrings[1] = szMinorReason;
|
|
lpStrings[2] = NULL;
|
|
lpStrings[3] = NULL;
|
|
lpStrings[4] = lpComment;
|
|
lpStrings[5] = szDomain;
|
|
|
|
|
|
if ( (*lpdwErr = RegDeleteValueW(hKey, lpszDirtyValName)) != ERROR_SUCCESS )
|
|
{
|
|
ERROR_WITH_SZ(IDS_NO_DIRTY_SHUTDOWN, lpMachine, *lpdwErr);
|
|
goto fail;
|
|
}
|
|
|
|
if(! ReportEventW(hEventLog, wEventType, 1, dwEventID, pUserSid,
|
|
wStringCnt, sizeof(DWORD),
|
|
lpStrings, lpdwReason))
|
|
{
|
|
DWORD dwDirtyShutdownFlag = 1;
|
|
*lpdwErr = GetLastError();
|
|
ERROR_WITH_SZ(IDS_FAILED_EVENT_REPORT, lpMachine, *lpdwErr);
|
|
|
|
RegSetValueEx( hKey,
|
|
lpszDirtyValName,
|
|
0,
|
|
REG_DWORD,
|
|
(PUCHAR) &dwDirtyShutdownFlag,
|
|
sizeof(DWORD));
|
|
|
|
goto fail;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RegDeleteValueW(hKey, lpszDirtyValName);
|
|
ERROR_WITH_SZ(IDS_NO_DIRTY_SHUTDOWN, lpMachine, 0);
|
|
}
|
|
|
|
goto exit;
|
|
fail:
|
|
res = FALSE;
|
|
exit:
|
|
if (pTokenUser != NULL) {
|
|
LocalFree(pTokenUser);
|
|
}
|
|
if(hEventLog)
|
|
DeregisterEventSource(hEventLog);
|
|
if(hKey)
|
|
RegCloseKey(hKey);
|
|
if(hRemote)
|
|
RegCloseKey(hRemote);
|
|
|
|
return res;
|
|
}
|
|
|
|
//
|
|
// Subclass procedure for edit box.
|
|
//
|
|
LRESULT APIENTRY EditSubclassProc(
|
|
HWND hwnd,
|
|
UINT uMsg,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
if (uMsg == WM_PASTE)
|
|
return TRUE;
|
|
|
|
if (uMsg == WM_CONTEXTMENU)
|
|
{
|
|
if(g_lpszHelpdir)
|
|
{
|
|
LPWSTR szHelp = new WCHAR[wcslen(g_lpszHelpdir) + 128];
|
|
if(szHelp)
|
|
{
|
|
wcscpy(szHelp, g_lpszHelpdir);
|
|
wcscat(szHelp, HELP_FILE);
|
|
WinHelpW(hwnd, szHelp, HELP_CONTEXTMENU,(DWORD_PTR)(LPSTR)ShutdownDialogHelpIds);
|
|
delete [] szHelp;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
return CallWindowProc(wpOrigEditProc, hwnd, uMsg,
|
|
wParam, lParam);
|
|
}
|
|
|
|
BOOL
|
|
GetUserSid(
|
|
PTOKEN_USER *ppTokenUser)
|
|
{
|
|
HANDLE TokenHandle;
|
|
PTOKEN_USER pTokenUser = NULL;
|
|
DWORD cbTokenUser = 0;
|
|
DWORD cbNeeded;
|
|
BOOL bRet = FALSE;
|
|
|
|
if (!GetTokenHandle(&TokenHandle)) {
|
|
return FALSE;
|
|
}
|
|
|
|
bRet = GetTokenInformation(TokenHandle,
|
|
TokenUser,
|
|
pTokenUser,
|
|
cbTokenUser,
|
|
&cbNeeded);
|
|
|
|
/*
|
|
* We've passed a NULL pointer and 0 for the amount of memory
|
|
* allocated. We expect to fail with bRet = FALSE and
|
|
* GetLastError = ERROR_INSUFFICIENT_BUFFER. If we do not
|
|
* have these conditions we will return FALSE.
|
|
*/
|
|
|
|
if (!bRet && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
|
|
|
|
pTokenUser = (PTOKEN_USER)LocalAlloc(LPTR, cbNeeded);
|
|
|
|
if (pTokenUser == NULL) {
|
|
goto GetUserSidDone;
|
|
}
|
|
|
|
cbTokenUser = cbNeeded;
|
|
|
|
bRet = GetTokenInformation(TokenHandle,
|
|
TokenUser,
|
|
pTokenUser,
|
|
cbTokenUser,
|
|
&cbNeeded);
|
|
} else {
|
|
/*
|
|
* Any other case -- return FALSE
|
|
*/
|
|
bRet = FALSE;
|
|
}
|
|
|
|
GetUserSidDone:
|
|
if (bRet == TRUE) {
|
|
*ppTokenUser = pTokenUser;
|
|
} else if (pTokenUser != NULL) {
|
|
LocalFree(pTokenUser);
|
|
}
|
|
|
|
CloseHandle(TokenHandle);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
BOOL
|
|
GetTokenHandle(
|
|
PHANDLE pTokenHandle)
|
|
{
|
|
if (!OpenThreadToken(GetCurrentThread(),
|
|
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
|
|
TRUE,
|
|
pTokenHandle)) {
|
|
if (GetLastError() == ERROR_NO_TOKEN) {
|
|
/* This means we are not impersonating anybody.
|
|
* Instead, lets get the token out of the process.
|
|
*/
|
|
|
|
if (!OpenProcessToken(GetCurrentProcess(),
|
|
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
|
|
pTokenHandle)) {
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
WCHAR*
|
|
LoadWString(int resid)
|
|
{
|
|
DWORD len, curlen = MAX_PATH;
|
|
LPWSTR szBuf = NULL;
|
|
|
|
szBuf = (LPWSTR)LocalAlloc(LMEM_FIXED, sizeof(WCHAR) * MAX_PATH);
|
|
if(!szBuf)
|
|
return NULL;
|
|
|
|
len = LoadStringW( g_hDllInstance, resid, szBuf, curlen);
|
|
while (len + 1 == curlen)
|
|
{
|
|
LocalFree(szBuf);
|
|
szBuf = (LPWSTR)LocalAlloc(LMEM_FIXED, curlen * 2 * sizeof(WCHAR));
|
|
if(!szBuf)
|
|
return NULL;
|
|
curlen *= 2;
|
|
len = LoadStringW( g_hDllInstance, resid, szBuf, curlen);
|
|
}
|
|
|
|
szBuf[len] = '\0';
|
|
|
|
return szBuf;
|
|
}
|
|
|
|
BOOL
|
|
IsStaticControl (HWND hwnd)
|
|
{
|
|
WCHAR name[128];
|
|
|
|
if (GetClassName(hwnd, name, 128)
|
|
&& (_wcsicmp(name, L"Static") == 0 || _wcsicmp(name, L"#32770") == 0))
|
|
{
|
|
// MessageBox(NULL, name, NULL, 0);
|
|
return TRUE;
|
|
}
|
|
// MessageBox(NULL, name, NULL, 0);
|
|
return FALSE;
|
|
}
|
|
|