diff --git a/ShutdownWithUpdates/INFO.txt b/ShutdownWithUpdates/INFO.txt new file mode 100644 index 0000000..c10bfff --- /dev/null +++ b/ShutdownWithUpdates/INFO.txt @@ -0,0 +1,116 @@ +Utility To Install Pre-Downloaded Windows Updates & Shutdown/Reboot +v.1.2.1.0 +Copyright (c) 2016-2018 by www.dennisbabkin.com. All rights reserved. +(Windows Vista/7/8/8.1/10) + +LAST MODIFIED: April 8, 2019 + + +DESCRIPTION: +================ +Utility that initiates installation of pre-downloaded updates on the Windows system & reboots, +or shuts it down. Note that if Windows updates were not downloaded prior to calling this utility, +the OS will simply perform the power operation. + +Windows 10: Updates will be installed during a reboot regardless of the options described below. + Some major updates may require user interaction to proceed. + +Additional: Major updates, such as Windows Feature Updates, may require user interaction in despite + of the options specified by this tool. Such requirement is stipulated by Microsoft and + cannot be overridden by this tool. + + +Usage: ShutdownWithUpdates [/s | /r | /hs | /g | /a | /?] [/f] [/v] [/nu] + [/m \\computer] [/t x] [/c "msg"] [/d [p|u:]xx:yy] + + /s Install updates & shut down computer. + (Updates must be already downloaded on computer being shut down.) + /r Install updates & reboot computer. + (Updates must be already downloaded on computer being rebooted.) + /hs Install updates & initiate hybrid shut-down of computer. (Windows 8,10) + (Updates must be already downloaded on computer being shut down.) + /g Install updates & reboot computer & restart registered applications. + (Updates must be already downloaded on computer being rebooted.) + /abo Go to advanced boot options menu. (Windows 8,10) + (Pre-Windows 10: Updates will not be installed.) + /a Abort previous shut-down/rebooting. + (Can be used only during previous time-out period.) + /? Show command line help. + /f Use forced action. + WARNING: May result in the loss of unsaved data on target computer! + /v Show user confirmation before proceeding. + (Local computer only. It is shown before time-out is initiated.) + /nu Not to install updates. + (Windows 10: This option is not supported.) + /m \\computer Specify target/remote computer. + /t x Set time-out before performing action to x seconds. + (Valid range is 0-315360000, or 10 yrs, with a default of 0.) + /c "msg" Message to be displayed in the interactive shutdown dialog box. + (Maximum of 512 characters is allowed.) + /d [p|u:]xx:yy Reason for shut-down or rebooting (used for logging): + p if action was planned. + u if action was user-defined. + (If neither p or u is used, assumes unplanned.) + xx = major reason number (less than 65536.) + yy = minor reason number (greater than 65536.) + (Reason numbers can be decimal or hex if begin with 0x) + For major and minor reason values check "System Shutdown Reason Codes": + msdn.microsoft.com/en-us/library/windows/desktop/aa376885(v=vs.85).aspx + + +Exit Codes: + 0 if success. + -1 if general failure in the module. + Other if error, will contain "System Error Code". For details check: + msdn.microsoft.com/en-us/library/windows/desktop/ms681381(v=vs.85).aspx + + +Examples: +(1) Install updates and reboot local computer without a delay: + (Fail if unsaved user data on computer.) + + ShutdownWithUpdates /r + +(2) Install updates and shut down local computer after 30 sec delay: + (Force applications with unsaved data to close & lose data! Show message.) + + ShutdownWithUpdates /s /f /t 30 /c "Forced shut-down in 30 sec!" + +(3) Do not install updates and reboot remote computer after a 20 sec delay: + (Not supported under Windows 10.) + (Fail if unsaved user data on remote computer.) + (Specify reason as planned, application issue, installation.) + + ShutdownWithUpdates /r /nu /m \\MYSERVER /t 20 /d p:0x00040000:0x00000002 + + + +** This is an Open Source project. ** + You can download its source code at: + https://github.com/dennisbabkin/ShutdownWithUpdates + + +For feedback & bug reports go to: +https://www.dennisbabkin.com/sfb/?what=info&name=ShutdownWithUpdates + + +Thank you! + + + + + + + + + + + + + + + + + + + diff --git a/ShutdownWithUpdates/ShutdownWithUpdates.sln b/ShutdownWithUpdates/ShutdownWithUpdates.sln new file mode 100644 index 0000000..1aab7e4 --- /dev/null +++ b/ShutdownWithUpdates/ShutdownWithUpdates.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ShutdownWithUpdates", "ShutdownWithUpdates\ShutdownWithUpdates.vcproj", "{73AFA8D4-A44E-41E9-925C-9BCEA5F6404A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {73AFA8D4-A44E-41E9-925C-9BCEA5F6404A}.Debug|Win32.ActiveCfg = Debug|Win32 + {73AFA8D4-A44E-41E9-925C-9BCEA5F6404A}.Debug|Win32.Build.0 = Debug|Win32 + {73AFA8D4-A44E-41E9-925C-9BCEA5F6404A}.Release|Win32.ActiveCfg = Release|Win32 + {73AFA8D4-A44E-41E9-925C-9BCEA5F6404A}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/ShutdownWithUpdates/ShutdownWithUpdates/Main.cpp b/ShutdownWithUpdates/ShutdownWithUpdates/Main.cpp new file mode 100644 index 0000000..d590e43 --- /dev/null +++ b/ShutdownWithUpdates/ShutdownWithUpdates/Main.cpp @@ -0,0 +1,1936 @@ +/* + * ShutdownWithUpdates + * "Utility To Install Pre-Downloaded Windows Updates & Shutdown/Reboot" + * Copyright (c) 2016-2019 www.dennisbabkin.com + * + * https://dennisbabkin.com/utilities/#ShutdownWithUpdates + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + + + +#include "StdAfx.h" +#include "Main.h" + + +int CMain::doWork(int argc, _TCHAR* argv[]) +{ + //RETURN: + // = See _tmain() + int nRes = ERROR_GEN_FAILURE; + + __try + { + nRes = doWork_RAW(argc, argv); + } + __except(1) + { + //Exception + nRes = -1; + + _tprintf(L"FATAL ERROR: Contact developers: support@dennisbabkin.com\n"); + } + + return nRes; +} + +int CMain::doWork_RAW(int argc, _TCHAR* argv[]) +{ + //DO NOT CALL directly + //RETURN: = See doWork() + int nResOSErrCode = ERROR_GEN_FAILURE; + + //Check if this file has a "mark of the web" stream attached to it & remove it + CheckForXP_SP2_FileBlockAndRemoveIt(); + + + + if(argc > 1) + { + //Check for arguments provided + + BOOL bDoActions = FALSE; //TRUE to do power op actions + + ACTIONS_INFO ai; + + //See what version of Windows we're running on + OSVERSIONINFO osi; + osi.dwOSVersionInfoSize = sizeof(osi); + BOOL bGV = RTL_OS_VERSION::GetVersionEx2(&osi); + + //Is it Windows 8 or later? + BOOL bWin8 = bGV && ((osi.dwMajorVersion == 6 && osi.dwMinorVersion >= 2) || osi.dwMajorVersion > 6); + + + //Go through command line arguments + for(int c = 1; c < argc; c++) + { + //Identify a command + LPCTSTR pStrCmd = argv[c]; + CMD_TYPE cmdType = getCommandType(pStrCmd); + + if(cmdType == CTP_SHOW_HELP) + { + //Show help -- and don't do anything else + bDoActions = FALSE; + nResOSErrCode = NO_ERROR; + + outputMainLogo(); + ShowHelpInfo(); + + break; + } + else if(cmdType == CTP_SHUT_DOWN) + { + //Shut-down + if(ai.pwrAction == PWR_OP_NONE) + { + bDoActions = TRUE; + ai.pwrAction = PWR_OP_SHUT_DOWN; + } + else + { + //Error + _tprintf(L"ERROR: Power action is already defined before parameter: %s\n", pStrCmd); + + bDoActions = FALSE; + nResOSErrCode = ERROR_DATATYPE_MISMATCH; + break; + } + } + else if(cmdType == CTP_REBOOT) + { + //Reboot + if(ai.pwrAction == PWR_OP_NONE) + { + bDoActions = TRUE; + ai.pwrAction = PWR_OP_REBOOT; + } + else + { + //Error + _tprintf(L"ERROR: Power action is already defined before parameter: %s\n", pStrCmd); + + bDoActions = FALSE; + nResOSErrCode = ERROR_DATATYPE_MISMATCH; + break; + } + } + else if(cmdType == CTP_HYBRID_SHUT_DOWN) + { + //Hybrid shut-down + + //Parameter only supported on Windows 8 and later OS + if(bWin8) + { + if(ai.pwrAction == PWR_OP_NONE) + { + bDoActions = TRUE; + ai.pwrAction = PWR_OP_HYBRID_SHUT_DOWN; + } + else + { + //Error + _tprintf(L"ERROR: Power action is already defined before parameter: %s\n", pStrCmd); + + bDoActions = FALSE; + nResOSErrCode = ERROR_DATATYPE_MISMATCH; + break; + } + } + else + { + //Error + _tprintf(L"ERROR: Parameter not supported on this OS: %s\n", pStrCmd); + + bDoActions = FALSE; + nResOSErrCode = ERROR_NOT_SUPPORTED; + break; + } + } + else if(cmdType == CTP_REBOOT_WITH_APP_RESTART) + { + //Reboot & restart apps + if(ai.pwrAction == PWR_OP_NONE) + { + bDoActions = TRUE; + ai.pwrAction = PWR_OP_REBOOT_WITH_APP_RESTART; + } + else + { + //Error + _tprintf(L"ERROR: Power action is already defined before parameter: %s\n", pStrCmd); + + bDoActions = FALSE; + nResOSErrCode = ERROR_DATATYPE_MISMATCH; + break; + } + } + else if(cmdType == CTP_ADVANCED_BOOT_MENU) + { + //Advanced boot options menu + + //Parameter only supported on Windows 8 and later OS + if(bWin8) + { + if(ai.pwrAction == PWR_OP_NONE) + { + bDoActions = TRUE; + ai.pwrAction = PWR_OP_ADVANCED_BOOT_OPTIONS_MENU; + } + else + { + //Error + _tprintf(L"ERROR: Power action is already defined before parameter: %s\n", pStrCmd); + + bDoActions = FALSE; + nResOSErrCode = ERROR_DATATYPE_MISMATCH; + break; + } + } + else + { + //Error + _tprintf(L"ERROR: Parameter not supported on this OS: %s\n", pStrCmd); + + bDoActions = FALSE; + nResOSErrCode = ERROR_NOT_SUPPORTED; + break; + } + } + else if(cmdType == CTP_ABORT_SHUT_DOWN) + { + //Abort previous power action + if(ai.pwrAction == PWR_OP_NONE) + { + bDoActions = TRUE; + ai.pwrAction = PWR_OP_ABORT_SHUT_DOWN; + } + else + { + //Error + _tprintf(L"ERROR: Power action is already defined before parameter: %s\n", pStrCmd); + + bDoActions = FALSE; + nResOSErrCode = ERROR_DATATYPE_MISMATCH; + break; + } + } + else if(cmdType == CTP_FORCED) + { + //Forced + ai.bForced = TRUE; + } + else if(cmdType == CTP_VERBOSE) + { + //Verbose + ai.bVerbose = TRUE; + } + else if(cmdType == CTP_NO_UPDATES) + { + //No updates + ai.bNoUpdates = TRUE; + } + else if(cmdType == CTP_REMOTE_COMPUTER) + { + //Remove computer + if(c + 1 < argc) + { + //Get next param + ai.pStrRemoteCompName = argv[++c]; + } + else + { + //Error + _tprintf(L"ERROR: Remote computer name is required for parameter: %s\n", pStrCmd); + + bDoActions = FALSE; + nResOSErrCode = ERROR_DATATYPE_MISMATCH; + break; + } + } + else if(cmdType == CTP_TIMEOUT) + { + //Timeout + if(c + 1 < argc) + { + //Get next param as timeout value + int nValTO = 0; + LPCTSTR pStrParam = argv[++c]; + if(CMain::parseDecimalInt32(pStrParam, &nValTO)) + { + if(nValTO >= 0) + { + //Use it + ai.nTimeoutSec = nValTO; + } + else + { + //Error + _tprintf(L"ERROR: Invalid time-out value (%s) for parameter: %s\n", pStrParam, pStrCmd); + + bDoActions = FALSE; + nResOSErrCode = ERROR_DATATYPE_MISMATCH; + break; + } + } + else + { + //Error + _tprintf(L"ERROR: Failed to parse time-out value for parameter: %s\n", pStrCmd); + + bDoActions = FALSE; + nResOSErrCode = ERROR_DATATYPE_MISMATCH; + break; + } + } + else + { + //Error + _tprintf(L"ERROR: Time-out value in seconds is required for parameter: %s\n", pStrCmd); + + bDoActions = FALSE; + nResOSErrCode = ERROR_DATATYPE_MISMATCH; + break; + } + } + else if(cmdType == CTP_SHOW_MESSAGE) + { + //Message + if(c + 1 < argc) + { + //Get next param + ai.pStrMessage = argv[++c]; + } + else + { + //Error + _tprintf(L"ERROR: Message text is required for parameter: %s\n", pStrCmd); + + bDoActions = FALSE; + nResOSErrCode = ERROR_DATATYPE_MISMATCH; + break; + } + } + else if(cmdType == CTP_REASON) + { + //Get reason that follows + if(c + 1 < argc) + { + //Get next param + LPCTSTR pStrParam = argv[++c]; + + if(!CMain::parseReasonParam(pStrParam, &ai.dwReason)) + { + //Failed to parse + _tprintf(L"ERROR: Failed to parse shut-down/rebooting reason parameter: %s\n", pStrParam); + + bDoActions = FALSE; + nResOSErrCode = ERROR_DATATYPE_MISMATCH; + break; + } + } + else + { + //Error + _tprintf(L"ERROR: Shut-down/rebooting reason value is required for parameter: %s\n", pStrCmd); + + bDoActions = FALSE; + nResOSErrCode = ERROR_DATATYPE_MISMATCH; + break; + } + } + else if(cmdType == CTP_NONE) + { + //Not a command + if(pStrCmd[0]) + { + //Show warning + _tprintf(L"WARNING: Unexpected argument: %s\n", pStrCmd); + } + else + ASSERT(NULL); + } + else if(cmdType == CTP_UNKNOWN) + { + //Unknown command + if(pStrCmd[0]) + { + //Show warning + _tprintf(L"WARNING: Unknown command: %s\n", pStrCmd); + } + else + ASSERT(NULL); + } + else + { + //Error + ASSERT(NULL); + + _tprintf(L"ERROR: Bad command: %s\n", pStrCmd); + + bDoActions = FALSE; + nResOSErrCode = ERROR_UNSUPPORTED_TYPE; + break; + } + } + + + //Are we doing the power op + if(bDoActions) + { + //Do we need to ask user? + if(ai.bVerbose) + { + //Need to ask by showing UI + // = 1 if we're allowed to continue + // = 0 if user canceled (and we're not allowed to continue) + // = -1 if error (check GetLastError() for info) -- we're not allowed to continue + int nResUI = ShowUserConfirmation(&ai); + if(nResUI == 0) + { + //User canceled + bDoActions = FALSE; + nResOSErrCode = ERROR_CANCELLED; + } + else if(nResUI != 1) + { + //Error + bDoActions = FALSE; + nResOSErrCode = ::GetLastError(); + } + } + + //Still allowed + if(bDoActions) + { + //Perform actions + nResOSErrCode = doActions(&ai); + + } + } + else + { + //Did we get an error? + if(nResOSErrCode == ERROR_GEN_FAILURE) + { + //Not enough parameters +#ifndef ERROR_INVALID_FIELD_IN_PARAMETER_LIST +#define ERROR_INVALID_FIELD_IN_PARAMETER_LIST 328 +#endif + nResOSErrCode = ERROR_INVALID_FIELD_IN_PARAMETER_LIST; + _tprintf(L"ERROR: Not enough parameters\n"); + } + } + } + else + { + //No arguments + outputMainLogo(); + + //Show how to get to help + _tprintf(L"Use -? to see command line options.\n"); + + nResOSErrCode = ERROR_EMPTY; + } + + return nResOSErrCode; +} + + +CMD_TYPE CMain::getCommandType(LPCTSTR pStrCmd) +{ + //Determine type of command in 'pStrCmd' + //RETURN: + // = Command type + CMD_TYPE res = CTP_NONE; + + if(pStrCmd && + pStrCmd[0]) + { + TCHAR z = pStrCmd[0]; + if(z == '/' || + z == '\\' || + z == '-') + { + pStrCmd++; + + //Assume Unidentified + res = CTP_UNKNOWN; + + static CMD_STR cmds[] = { + {CTP_SHOW_HELP, L"?"}, + {CTP_SHOW_HELP, L"help"}, + {CTP_SHUT_DOWN, L"s"}, + {CTP_REBOOT, L"r"}, + {CTP_HYBRID_SHUT_DOWN, L"hs"}, + {CTP_REBOOT_WITH_APP_RESTART, L"g"}, + {CTP_ABORT_SHUT_DOWN, L"a"}, + {CTP_FORCED, L"f"}, + {CTP_VERBOSE, L"v"}, + {CTP_NO_UPDATES, L"nu"}, + {CTP_REMOTE_COMPUTER, L"m"}, + {CTP_TIMEOUT, L"t"}, + {CTP_SHOW_MESSAGE, L"c"}, + {CTP_REASON, L"d"}, + {CTP_ADVANCED_BOOT_MENU, L"abo"}, + }; + + for(int c = 0; c < SIZEOF(cmds); c++) + { + if(::CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, pStrCmd, -1, cmds[c].pStrCmd, -1) == CSTR_EQUAL) + { + //Got a match + res = cmds[c].cmdID; + break; + } + } + + } + } + + return res; +} + +int CMain::CheckForXP_SP2_FileBlockAndRemoveIt() +{ + //Checks if the "mark of the web" stream is on the 'this' file & removes it if so + //(It will be there if file was downloaded from the Internet, or from another computer) + //RETURN: + // = 0 if not such block was found + // = 1 if block was found (and removed if 'bRemoveIt' == TRUE) + // = 2 if block was found, but COULD NOT remove it (if 'bRemoveIt' == TRUE) + // = -1 if error + // = -2 if not supported + + BOOL bRemoveIt = TRUE; + + //No, get current file + TCHAR strFilePath[MAX_PATH * 4]; + strFilePath[0] = 0; + if(!::GetModuleFileName(NULL, strFilePath, SIZEOF(strFilePath))) + return -1; + + strFilePath[SIZEOF(strFilePath) - 1] = 0; + + + //Load API needed for the process + NTQUERYINFORMATIONFILE pfnNtQueryInformationFile; + (FARPROC&)pfnNtQueryInformationFile = ::GetProcAddress(::GetModuleHandle(_T("ntdll.dll")), "NtQueryInformationFile"); + if(!pfnNtQueryInformationFile) + return -2; + + //Obtain SE_BACKUP_NAME privilege (required for opening a directory) + HANDLE hToken = NULL; + TOKEN_PRIVILEGES tp; + if(OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) + { + if(LookupPrivilegeValue(NULL, SE_BACKUP_NAME, &tp.Privileges[0].Luid)) + { + tp.PrivilegeCount = 1; + tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + if(AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) + { + //All done + } + } + } + if(hToken) + ::CloseHandle(hToken); + + //Now open the file + HANDLE hFile = ::CreateFile(strFilePath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + if(hFile == INVALID_HANDLE_VALUE) + return -1; + + //Get stream information block (assume memory size in the beginning) + LPBYTE pInfoBlock = NULL; + ULONG uInfoBlockSize = 0; + IO_STATUS_BLOCK ioStatus; + NTSTATUS status; + do + { + uInfoBlockSize += 16 * 1024; + if(pInfoBlock) + delete [] pInfoBlock; + + pInfoBlock = new BYTE [uInfoBlockSize]; + ((PFILE_STREAM_INFORMATION)pInfoBlock)->StreamNameLength = 0; + status = pfnNtQueryInformationFile(hFile, &ioStatus, (LPVOID)pInfoBlock, uInfoBlockSize, FileStreamInformation); + } + while(status == STATUS_BUFFER_OVERFLOW); + + //Close file handle + ::CloseHandle(hFile); + + //Go through stream info and look for our stream + int nRet = 0; + WCHAR wszStreamName[MAX_PATH]; + TCHAR szStreamName[MAX_PATH]; + PFILE_STREAM_INFORMATION pStreamInfo = (PFILE_STREAM_INFORMATION)(LPVOID)pInfoBlock; + for(;;) + { + // Check if stream info block is empty (directory may have no stream) + if(pStreamInfo->StreamNameLength == 0) + break; + + // Get stream name + memcpy(wszStreamName, pStreamInfo->StreamName, pStreamInfo->StreamNameLength); + wszStreamName[pStreamInfo->StreamNameLength / sizeof(WCHAR)] = L'\0'; + + // Remove attribute tag and convert to char + LPWSTR pTag = wcsstr(wszStreamName, L":$DATA"); + if (pTag) + *pTag = L'\0'; + ::StringCchCopy(szStreamName, SIZEOF(szStreamName), (const TCHAR*)wszStreamName); + + //Is it the name we're looking for? + if (lstrcmpi(szStreamName, _T(":Zone.Identifier")) == 0) + { + //Our Named stream? + nRet = 1; + + //Do we need to remove it? + if(!bRemoveIt) + break; + + //Make file name for the stream + TCHAR strStreamFileName[MAX_PATH * 4]; + strStreamFileName[0] = 0; + ::StringCchCopy(strStreamFileName, SIZEOF(strStreamFileName), strFilePath); + ::StringCchCat(strStreamFileName, SIZEOF(strStreamFileName), szStreamName); + strStreamFileName[SIZEOF(strStreamFileName) - 1] = 0; + + //Delete this stream + hFile = ::CreateFile(strStreamFileName, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, NULL); + if (hFile != INVALID_HANDLE_VALUE) + { + //Erased it + if(!::CloseHandle(hFile)) + { + //Error removing + nRet = 2; + } + } + else + { + //Error removing + nRet = 2; + } + } + + //Do we have more stream records? + if(pStreamInfo->NextEntryOffset == 0) + break; + + //Go to the next one + pStreamInfo = (PFILE_STREAM_INFORMATION)((LPBYTE)pStreamInfo + pStreamInfo->NextEntryOffset); + } + + //Free memory + if(pInfoBlock) + delete [] pInfoBlock; + + return nRet; +} + + + +void CMain::ShowHelpInfo() +{ + //Show help information + + _tprintf( + L"Usage: ShutdownWithUpdates [/s | /r | /hs | /g | /a | /?] [/f] [/v] [/nu]\n" + L" [/m \\\\computer] [/t x] [/c \"msg\"] [/d [p|u:]xx:yy]\n" + L"\n" + L" /s Install updates & shut down computer.\n" + L" (Updates must be already downloaded on computer being shut down.)\n" + L" /r Install updates & reboot computer.\n" + L" (Updates must be already downloaded on computer being rebooted.)\n" + L" /hs Install updates & initiate hybrid shut-down of computer. (Windows 8,10)\n" + L" (Updates must be already downloaded on computer being shut down.)\n" + L" /g Install updates & reboot computer & restart registered applications.\n" + L" (Updates must be already downloaded on computer being rebooted.)\n" + L" /abo Go to advanced boot options menu. (Windows 8,10)\n" + L" (Pre-Windows 10: Updates will not be installed.)\n" + L" /a Abort previous shut-down/rebooting.\n" + L" (Can be used only during previous time-out period.)\n" + L" /? Show command line help.\n" + L" /f Use forced action.\n" + L" WARNING: May result in the loss of unsaved data on target computer!\n" + L" /v Show user confirmation before proceeding.\n" + L" (Local computer only. It is shown before time-out is initiated.)\n" + L" /nu Not to install updates.\n" + L" (Windows 10: This option is not supported.)\n" + L" /m \\\\computer Specify target/remote computer.\n" + L" /t x Set time-out before performing action to x seconds.\n" + L" (Valid range is 0-%d, or 10 yrs, with a default of 0.)\n" + L" /c \"msg\" Message to be displayed in the interactive shutdown dialog box.\n" + L" (Maximum of 512 characters is allowed.)\n" + L" /d [p|u:]xx:yy Reason for shut-down or rebooting (used for logging):\n" + L" p if action was planned.\n" + L" u if action was user-defined.\n" + L" (If neither p or u is used, assumes unplanned.)\n" + L" xx = major reason number (less than %d.)\n" + L" yy = minor reason number (greater than %d.)\n" + L" (Reason numbers can be decimal or hex if begin with 0x)\n" + L" For major and minor reason values check \"System Shutdown Reason Codes\":\n" + L" msdn.microsoft.com/en-us/library/windows/desktop/aa376885(v=vs.85).aspx\n" + L"\n" + L"Exit Codes:\n" + L" 0 if success.\n" + L" -1 if general failure in the module.\n" + L" Other if error, will contain \"System Error Code\". For details check:\n" + L" msdn.microsoft.com/en-us/library/windows/desktop/ms681381(v=vs.85).aspx\n" + L"\n" + L"Examples:\n" + L"(1) Install updates and reboot local computer without a delay:\n" + L" (Fail if unsaved user data on computer.)\n" + L"\n" + L" ShutdownWithUpdates /r\n" + L"\n" + L"(2) Install updates and shut down local computer after 30 sec delay:\n" + L" (Force applications with unsaved data to close & lose data! Show message.)\n" + L"\n" + L" ShutdownWithUpdates /s /f /t 30 /c \"Forced shut-down in 30 sec!\"\n" + L"\n" + L"(3) Do not install updates and reboot remote computer after a 20 sec delay:\n" + L" (Not supported under Windows 10.)\n" + L" (Fail if unsaved user data on remote computer.)\n" + L" (Specify reason as planned, application issue, installation.)\n" + L"\n" + L" ShutdownWithUpdates /r /nu /m \\\\MYSERVER /t 20 /d p:0x00040000:0x00000002\n" + L"\n" + L"\n" + L"** This is an Open Source project. **\n" + L" You can download its source code at:\n" + L" https://github.com/dennisbabkin/ShutdownWithUpdates\n" + L"\n" + , + + MAX_SHUTDOWN_TIMEOUT, + 0x10000, + 0x10000 + ); + +} + + +int CMain::findCharInStrCaseSensitive(LPCTSTR pStr, TCHAR chSearch, int nIndBegin) +{ + //'nIndBegin' = 0-based index in 'pStr' to begin search from + //RETURN: + // = Index of 'chSearch' in 'pStr' + // = -1 if none + int nInd = -1; + + if(pStr) + { + for(int i = nIndBegin;; i++) + { + TCHAR z = pStr[i]; + if(z == 0) + { + //End of string + break; + } + else if(z == chSearch) + { + //Got it + nInd = i; + break; + } + } + } + + return nInd; +} + + +BOOL CMain::parseReasonInt32(LPCTSTR pStr, int* pnOutVal) +{ + //'pStr' = can be signed 32-bit integer, as decimal or hex + //'pnOutVal' = if not NULL, Value read + //RETURN: + // = TRUE if read OK + BOOL bRes = FALSE; + + int nV = 0; + + //First assume hex + UINT nValHex = 0; + if(parseHexUInt32(pStr, &nValHex)) + { + //Got it + nV = nValHex; + bRes = TRUE; + } + else + { + //Parse it as decimal + int nValDecimal = 0; + if(parseDecimalInt32(pStr, &nValDecimal)) + { + //Got it + nV = nValDecimal; + bRes = TRUE; + } + } + + if(pnOutVal) + *pnOutVal = nV; + + return bRes; +} + +BOOL CMain::parseDecimalInt32(LPCTSTR pStr, int* pnOutVal) +{ + //'pStr' = can be signed 32-bit integer + //'pnOutVal' = if not NULL, Value read + //RETURN: + // = TRUE if read OK + BOOL bRes = FALSE; + + int nV = 0; + + if(pStr && + pStr[0]) + { + int i = 0; + + //Check for sign first + TCHAR chSign = pStr[0]; + if(chSign == '+' || + chSign == '-') + { + i++; + } + + //Flag if we found at least one digit + BOOL bDigitFound = FALSE; + + //See if it's valid + for(;; i++) + { + TCHAR z = pStr[i]; + if(z == 0) + { + //End of line + if(bDigitFound) + { + //Scan it + int val = 0; + if(_stscanf_s(pStr, L"%d", &val) == 1) + { + //Done + nV = val; + + bRes = TRUE; + } + } + + break; + } + else if(z >= '0' && z <= '9') + { + //Found a digit + bDigitFound = TRUE; + } + else + { + //Something else -- wrong char + break; + } + } + } + + if(pnOutVal) + *pnOutVal = nV; + + return bRes; +} + + +BOOL CMain::parseHexUInt32(LPCTSTR pStr, UINT* pnOutVal) +{ + //'pStr' = can be unsigned 32-bit hex integer + //'pnOutVal' = if not NULL, Value read + //RETURN: + // = TRUE if read OK + BOOL bRes = FALSE; + + UINT nV = 0; + + if(pStr && + pStr[0]) + { + //Must begin with 0x + if(pStr[0] == '0') + { + if(pStr[1] == 'x') + { + //Flag if we found at least one digit + BOOL bDigitFound = FALSE; + + //See if it's valid + for(int i = 2;; i++) + { + TCHAR z = pStr[i]; + if(z == 0) + { + //End of line + if(bDigitFound) + { + //Scan it + int val = 0; + if(_stscanf_s(pStr, L"%x", &val) == 1) + { + //Done + nV = val; + + bRes = TRUE; + } + } + + break; + } + else if((z >= '0' && z <= '9') || + (z >= 'a' && z <= 'f') || + (z >= 'A' && z <= 'F')) + { + //Found a digit + bDigitFound = TRUE; + } + else + { + //Something else -- wrong char + break; + } + } + } + } + } + + if(pnOutVal) + *pnOutVal = nV; + + return bRes; +} + +BOOL CMain::parseReasonParam(LPCTSTR pStrParam, DWORD* pnOutReason) +{ + //Parse reason parameter from 'pStrParam' + //'pnOutReason' = if not NULL, receives the reason if success + //RETURN: + // = TRUE if success + // = FALSE if failed to parse + BOOL bRes = FALSE; + + DWORD dwReason = 0; + + //[p|u:]xx:yy + if(pStrParam && + pStrParam[0]) + { + //Split into segments by : + int nFnd0 = findCharInStrCaseSensitive(pStrParam, L':', 0); + if(nFnd0 >= 0) + { + TCHAR* pBuff0 = NULL; + TCHAR* pBuff1 = NULL; + + int nFnd1 = findCharInStrCaseSensitive(pStrParam, L':', nFnd0 + 1); + if(nFnd1 >= 0) + { + int nFnd2 = findCharInStrCaseSensitive(pStrParam, L':', nFnd1 + 1); + if(nFnd2 < 0) + { + //[p|u:]xx:yy + if(nFnd0 == 1) + { + TCHAR chPU = pStrParam[0]; + + int nchLn0 = nFnd1 - nFnd0; + pBuff0 = new (std::nothrow) TCHAR[nchLn0]; + if(pBuff0) + { + memcpy(pBuff0, pStrParam + nFnd0 + 1, (nchLn0 - 1) * sizeof(TCHAR)); + pBuff0[nchLn0 - 1] = 0; + + int nchLn1 = lstrlen(pStrParam) - nFnd1; + pBuff1 = new (std::nothrow) TCHAR[nchLn1]; + if(pBuff1) + { + memcpy(pBuff1, pStrParam + nFnd1 + 1, (nchLn1 - 1) * sizeof(TCHAR)); + pBuff1[nchLn1 - 1] = 0; + + //Parse values + UINT uiMajor = 0; + if(parseReasonInt32(pBuff0, (int*)&uiMajor)) + { + UINT uiMinor = 0; + if(parseReasonInt32(pBuff1, (int*)&uiMinor)) + { + //Check reason itself + if(chPU == 'p' || + chPU == 'P') + { + //Planned + dwReason = SHTDN_REASON_FLAG_PLANNED; + + goto lbl_use_rsn; + } + else if(chPU == 'u' || + chPU == 'U') + { + //User-defined + dwReason = SHTDN_REASON_FLAG_USER_DEFINED; +lbl_use_rsn: + //Use it + dwReason |= (uiMajor & 0xFFFF0000) | (uiMinor & 0xFFFF); + + bRes = TRUE; + } + } + } + } + } + } + } + } + else + { + //xx:yy + pBuff0 = new (std::nothrow) TCHAR[nFnd0 + 1]; + if(pBuff0) + { + memcpy(pBuff0, pStrParam, nFnd0 * sizeof(TCHAR)); + pBuff0[nFnd0] = 0; + + int nchLn1 = lstrlen(pStrParam) - nFnd0; + pBuff1 = new (std::nothrow) TCHAR[nchLn1]; + if(pBuff1) + { + memcpy(pBuff1, pStrParam + nFnd0 + 1, (nchLn1 - 1) * sizeof(TCHAR)); + pBuff1[nchLn1 - 1] = 0; + + //Parse two digits + UINT uiMajor = 0; + if(parseReasonInt32(pBuff0, (int*)&uiMajor)) + { + UINT uiMinor = 0; + if(parseReasonInt32(pBuff1, (int*)&uiMinor)) + { + //Got both + dwReason = (uiMajor & 0xFFFF0000) | (uiMinor & 0xFFFF); + + bRes = TRUE; + } + } + } + + } + } + + + //Free mem + if(pBuff0) + { + delete[] pBuff0; + pBuff0 = NULL; + } + + if(pBuff1) + { + delete[] pBuff1; + pBuff1 = NULL; + } + + + } + } + + if(pnOutReason) + *pnOutReason = dwReason; + + return bRes; +} + + +TCHAR* CMain::CapitalizeFirstLetter(TCHAR* pBuff) +{ + //Capitalize first letter in 'pBuff' + //RETURN: + // = Pointer to the buffer in 'pBuff' + + if(pBuff && + pBuff[0]) + { + TCHAR buff[2] = {pBuff[0], 0}; + ::CharUpper(buff); + + pBuff[0] = buff[0]; + } + + return pBuff; +} + + +BOOL CMain::FormatSecSmart(TCHAR* pBuff, int nchBuffSz, int nSeconds) +{ + //Format 'nSeconds' into days:hours:minutes:seconds + //'pBuff' = receives formatted string + //'nchBuffSz' = size of 'pBuff' in TCHARs + //'nSeconds' = number of seconds, can be negative + //RETURN: + // = TRUE if success + BOOL bRes = FALSE; + + if(pBuff && + nchBuffSz > 0) + { + BOOL bNegative = FALSE; + pBuff[0] = 0; + + //Is it negative? + if(nSeconds < 0) + { + nSeconds = -nSeconds; + bNegative = TRUE; + + if(FAILED(::StringCchCat(pBuff, nchBuffSz, L"-"))) + return FALSE; + } + + int n_Secs = nSeconds % 60; + + nSeconds /= 60; + int n_Mins = nSeconds % 60; + + nSeconds /= 60; + int n_Hrs = nSeconds % 24; + + nSeconds /= 24; + int ii_Days = nSeconds; + + //Format it + if(ii_Days != 0) + { + int nLn = lstrlen(pBuff); + if(FAILED(::StringCchPrintf(pBuff + nLn, nchBuffSz - nLn, L"%d%s", ii_Days, LOC_STRING(IDS_STRING118)))) + return FALSE; + } + + if(n_Hrs != 0 || pBuff[0]) + { + int nLn = lstrlen(pBuff); + if(FAILED(::StringCchPrintf(pBuff + nLn, nchBuffSz - nLn, L"%s%d%s", !pBuff[0] ? L"" : L":", n_Hrs, LOC_STRING(IDS_STRING117)))) + return FALSE; + } + + if(n_Mins != 0 || pBuff[0]) + { + int nLn = lstrlen(pBuff); + if(FAILED(::StringCchPrintf(pBuff + nLn, nchBuffSz - nLn, L"%s%d%s", !pBuff[0] ? L"" : L":", n_Mins, LOC_STRING(IDS_STRING116)))) + return FALSE; + } + + if(n_Secs != 0 || pBuff[0]) + { + int nLn = lstrlen(pBuff); + if(FAILED(::StringCchPrintf(pBuff + nLn, nchBuffSz - nLn, L"%s%d%s", !pBuff[0] ? L"" : L":", n_Secs, LOC_STRING(IDS_STRING110)))) + return FALSE; + } + + if(!pBuff[0]) + { + //If nothing there + if(FAILED(::StringCchPrintf(pBuff, nchBuffSz, L"0%s", LOC_STRING(IDS_STRING110)))) + return FALSE; + } + + //Done + bRes = TRUE; + } + + return bRes; +} + + +int CMain::ShowUserConfirmation(ACTIONS_INFO* pAI) +{ + //Request user confirmation for action in 'pAI' + //RETURN: + // = 1 if we're allowed to continue + // = 0 if user canceled (and we're not allowed to continue) + // = -1 if error (check GetLastError() for info) -- we're not allowed to continue + int nRes = -1; + int nOSError = NO_ERROR; + + if(pAI) + { + //Compose message + TCHAR buffMsg[1024 * 2 + 512] = {0}; + + //Power op + TCHAR buffPowerOp[1024] = {0}; + + //Are we aborting? + BOOL bAborting = pAI->pwrAction == PWR_OP_ABORT_SHUT_DOWN; + BOOL bAdvBootMenu = pAI->pwrAction == PWR_OP_ADVANCED_BOOT_OPTIONS_MENU; + + //Are we installing updates? + if(!pAI->bNoUpdates && + !bAborting && + !bAdvBootMenu) + { + ::StringCchPrintf(buffPowerOp, SIZEOF(buffPowerOp), L"%s, ", LOC_STRING(IDS_STRING101)); //L"install downloaded updates" + } + + + switch(pAI->pwrAction) + { + case PWR_OP_SHUT_DOWN: + ::StringCchCat(buffPowerOp, SIZEOF(buffPowerOp), LOC_STRING(IDS_STRING102)); //L"shut down computer" + break; + case PWR_OP_REBOOT: + ::StringCchCat(buffPowerOp, SIZEOF(buffPowerOp), LOC_STRING(IDS_STRING103)); //L"reboot computer" + break; + case PWR_OP_HYBRID_SHUT_DOWN: + ::StringCchCat(buffPowerOp, SIZEOF(buffPowerOp), LOC_STRING(IDS_STRING104)); //L"hybrid shut-down of computer" + break; + case PWR_OP_REBOOT_WITH_APP_RESTART: + ::StringCchCat(buffPowerOp, SIZEOF(buffPowerOp), LOC_STRING(IDS_STRING105)); //L"reboot & restart registered apps" + break; + + case PWR_OP_ADVANCED_BOOT_OPTIONS_MENU: + ::StringCchCopy(buffPowerOp, SIZEOF(buffPowerOp), LOC_STRING(IDS_STRING119)); //L"go to advanced boot options menu" + break; + + case PWR_OP_ABORT_SHUT_DOWN: + ::StringCchCopy(buffPowerOp, SIZEOF(buffPowerOp), LOC_STRING(IDS_STRING106)); //L"abort pending shut-down or rebooting" + break; + + default: + ASSERT(NULL); + buffPowerOp[0] = 0; + break; + } + + if(buffPowerOp[0]) + { + //Is it forced + TCHAR buffForced[256] = {0}; + if(!bAborting) + { + if(pAI->bForced) + { + ::StringCchPrintf(buffForced, SIZEOF(buffForced), L" (%s)", LOC_STRING(IDS_STRING107)); //L"forced" + } + } + + //Computer + TCHAR buffComp[256] = {0}; + if(pAI->pStrRemoteCompName && + pAI->pStrRemoteCompName[0]) + { + //Use name + ::StringCchCopy(buffComp, SIZEOF(buffComp), pAI->pStrRemoteCompName); + } + else + { + //Local + ::StringCchCopy(buffComp, SIZEOF(buffComp), CapitalizeFirstLetter(LOC_STRING(IDS_STRING108))); //L"local" + } + + //Time-out + TCHAR buffTimeout[256] = {0}; + if(!bAborting) + { + if(pAI->nTimeoutSec > 0) + { + //Set timeout + TCHAR buffTO_val[256]; + if(FormatSecSmart(buffTO_val, SIZEOF(buffTO_val), pAI->nTimeoutSec)) + { + ::StringCchPrintf(buffTimeout, SIZEOF(buffTimeout), L"\n%s: %s", + CapitalizeFirstLetter(LOC_STRING(IDS_STRING109)), //L"timeout" + buffTO_val + ); + } + else + { + //Failed -- use just seconds + ASSERT(NULL); + ::StringCchPrintf(buffTimeout, SIZEOF(buffTimeout), L"\n%s: %d %s", + CapitalizeFirstLetter(LOC_STRING(IDS_STRING109)), //L"timeout" + pAI->nTimeoutSec, + LOC_STRING(IDS_STRING110) //L"sec" + ); + } + } + else + { + //No timeout + ::StringCchPrintf(buffTimeout, SIZEOF(buffTimeout), L"\n%s: %s", + CapitalizeFirstLetter(LOC_STRING(IDS_STRING109)), //L"timeout" + CapitalizeFirstLetter(LOC_STRING(IDS_STRING111)) //L"none" + ); + } + } + + //Message to display + TCHAR buffUserMsg[512] = {0}; + if(!bAborting) + { + if(pAI->pStrMessage && + pAI->pStrMessage[0]) + { + ::StringCchPrintf(buffUserMsg, SIZEOF(buffUserMsg), L"\n%s: %s" + , + CapitalizeFirstLetter(LOC_STRING(IDS_STRING112)), //L"User Message" + pAI->pStrMessage); + } + } + + + //Make main message + ::StringCchPrintf(buffMsg, SIZEOF(buffMsg), + L"%s\n\n" + L"\"%s%s\"\n\n" + L"%s: %s" + L"%s" + L"%s" + , + LOC_STRING(IDS_STRING113), //Do you want to perform the following operation? + CapitalizeFirstLetter(buffPowerOp), buffForced, + CapitalizeFirstLetter(LOC_STRING(IDS_STRING114)), //Computer + buffComp, + buffTimeout, + buffUserMsg + ); + + //Show UI + ::SetLastError(NO_ERROR); + int nResMB = ::MessageBox(NULL, buffMsg, + LOC_STRING(IDS_STRING115), + MB_YESNOCANCEL | MB_DEFBUTTON2 | MB_ICONQUESTION | MB_SYSTEMMODAL | MB_SETFOREGROUND); + + if(nResMB == IDYES) + { + //User chose to continue + nRes = 1; + } + else if(nResMB == IDNO || + nResMB == IDCANCEL) + { + //User chose not to continue + nRes = 0; + } + else + { + //Error + nOSError = ::GetLastError(); + if(nOSError == NO_ERROR) + nOSError = ERROR_INVALID_LEVEL; + } + } + else + { + //Error + nOSError = ERROR_INVALID_PARAMETER; + } + } + else + nOSError = ERROR_INVALID_PARAMETER; + + ::SetLastError(nOSError); + return nRes; +} + + +BOOL CMain::AdjustPrivilege(LPCTSTR pStrMachine, LPCTSTR pPrivilegeName, BOOL bEnable, HANDLE hProcess) +{ + //Tries to adjust the 'pPrivilegeName' privilege for the process + //'pStrMachine' = computer name, or NULL for local computer + //'bEnable' = TRUE to enable, FALSE to disable a privilege + //'hProcess' = Process to adjust privilege for, or NULL for current process + //RETURN: - TRUE if done; + // - FALSE if privileges were not adjusted (check GetLastError() for info) + BOOL bRes = FALSE; + int nOSError = NO_ERROR; + + HANDLE hToken; + TOKEN_PRIVILEGES tkp; + + //Get a token for this process. + if(!OpenProcessToken(hProcess ? hProcess : GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) + return FALSE; + + //Get the LUID for the shutdown privilege. + if(LookupPrivilegeValue(pStrMachine, pPrivilegeName, &tkp.Privileges[0].Luid)) + { + //One privilege to set + tkp.PrivilegeCount = 1; + tkp.Privileges[0].Attributes = bEnable ? SE_PRIVILEGE_ENABLED : SE_PRIVILEGE_REMOVED; + + //Adjust it now + bRes = AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0); + nOSError = GetLastError(); + if(bRes) + { + //See if no error + if(nOSError != ERROR_SUCCESS) + bRes = FALSE; + } + } + else + { + //Failed + nOSError = ::GetLastError(); + } + + //Close handle + CloseHandle(hToken); + + ::SetLastError(nOSError); + return bRes; +} + + +TCHAR* CMain::trimBuffer(TCHAR* pBuff) +{ + //Remove spaces from left and right side of 'pBuff' + //RETURN: + // = Pointer to the buffer from 'pBuff' + + //Do left & then right + return trimBufferRight(trimBufferLeft(pBuff)); +} + +TCHAR* CMain::trimBufferLeft(TCHAR* pBuff) +{ + //Remove spaces from left side of 'pBuff' + //RETURN: + // = Pointer to the buffer from 'pBuff' + if(pBuff) + { + for(int i = 0;; i++) + { + TCHAR z = pBuff[i]; + if(z == ' ' || + z == '\t' || + z == '\n' || + z == '\r') + { + } + else + { + //See if we need to remove it + if(i > 0) + { + //Find the end of string + int nEndInd = i; + for(;; nEndInd++) + { + if(pBuff[nEndInd] == 0) + break; + } + + memcpy(pBuff, pBuff + i, (nEndInd - i + 1) * sizeof(TCHAR)); + } + + break; + } + } + } + + return pBuff; +} + +TCHAR* CMain::trimBufferRight(TCHAR* pBuff) +{ + //Remove spaces from right side of 'pBuff' + //RETURN: + // = Pointer to the buffer from 'pBuff' + if(pBuff) + { + //Find the end of string + int nLn = 0; + while(pBuff[nLn]) + { + nLn++; + } + + for(int i = nLn - 1; i >= 0; i--) + { + TCHAR z = pBuff[i]; + if(z == ' ' || + z == '\t' || + z == '\n' || + z == '\r') + { + } + else + { + //See if we need to remove it + if(i < nLn - 1) + { + pBuff[i + 1] = 0; + } + + break; + } + } + } + + return pBuff; +} + + +TCHAR* CMain::FormatErrorMessage(int nOSError, TCHAR* pBuff, int nchLnBuff, BOOL bUseDescriptionForNoError) +{ + //Convert OS error code to string + //'nOSError' = OS error code to format + //'pBuff' = text buffer to use for formatting + //'nchLnBuff' = length of 'pBuff' in TCHARs + //'bUseDescriptionForNoError' = TRUE to use system provided description for NO_ERROR (that is "Operation completed successfully") + // = FALSE to use empty string for NO_ERROR + //RETURN: = Error description buffer in 'pBuff' + int nPrev_OSError = ::GetLastError(); + LPVOID lpMsgBuf = NULL; + DWORD dwRes; + + if(pBuff && + nchLnBuff > 0) + { + //Reset buffer + pBuff[0] = 0; + + if(nOSError != NO_ERROR || + bUseDescriptionForNoError) + { + //Convert error code + if(nOSError >= INTERNET_ERROR_BASE && nOSError <= INTERNET_ERROR_LAST) + { + //Get system folder + TCHAR buffSysFolder[MAX_PATH * 2]; + buffSysFolder[0] = 0; + if(int nLn = ::GetSystemDirectory(buffSysFolder, SIZEOF(buffSysFolder))) + { + //Make sure folder ends with slash + if(buffSysFolder[nLn - 1] != '/' && + buffSysFolder[nLn - 1] != '\\') + { + ::StringCchCat(buffSysFolder, SIZEOF(buffSysFolder), L"\\"); + } + + ::StringCchCat(buffSysFolder, SIZEOF(buffSysFolder), L"wininet.dll"); + } + + if(buffSysFolder[0]) + { + //Spec code + HMODULE hMod = LoadLibrary(buffSysFolder); + dwRes = ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, + hMod, nOSError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL); + FreeLibrary(hMod); + } + } + else + { + //Regular + dwRes = ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + nOSError, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &lpMsgBuf, 0, NULL); + } + + if(lpMsgBuf) + { + //Copy data to our buffer + ::StringCchCopy(pBuff, nchLnBuff, (LPCTSTR)lpMsgBuf); + + //Free mem + LocalFree(lpMsgBuf); + } + + //Did we get something? + trimBuffer(pBuff); + if(!pBuff[0]) + { + //If we get nothing, then use just the code + ::StringCchPrintf(pBuff, nchLnBuff, L"Error %d", nOSError); + } + } + } + + ::SetLastError(nPrev_OSError); + return pBuff; +} + + + + +BOOL CMain::SetNeededShutdownPrivileges(LPCTSTR pStrRemoteCompName) +{ + //Set privileges needed for a shut-down + //RETURN: + // = TRUE if success + BOOL bRes = TRUE; + + //Set SE_DEBUG_NAME privilege + if(!AdjustPrivilege(NULL, L"SeShutdownPrivilege", TRUE)) + { + //Error + //int nErr = ::GetLastError(); + //TCHAR buffErr[1024]; + + //_tprintf(L"WARNING: Failed to set shut-down privilege: (%d) %s\n" + // , + // nErr, + // FormatErrorMessage(nErr, buffErr, SIZEOF(buffErr)) + // ); + + bRes = FALSE; + } + + //Is it a remove computer? + if(pStrRemoteCompName && + pStrRemoteCompName[0]) + { + //Set privilege + //INFO: You can grant SeRemoteShutdownPrivilege through secpol.msc > Local Policies > User Rights Assignment, + // by editing the "Force shutdown from remote system" entry. + // + if(!AdjustPrivilege(NULL /*pStrRemoteCompName*/, L"SeRemoteShutdownPrivilege", TRUE)) + { + //Error + //int nErr = ::GetLastError(); + //TCHAR buffErr[1024]; + + //_tprintf(L"WARNING: Failed to set shut-down privilege for computer: \"%s\", (%d) %s\n" + // , + // pStrRemoteCompName, + // nErr, + // FormatErrorMessage(nErr, buffErr, SIZEOF(buffErr)) + // ); + + bRes = FALSE; + } + + } + + return bRes; +} + + + + +int CMain::doActions(ACTIONS_INFO* pAI) +{ + //Perform power actions in 'pAI' + //RETURN: + // = Value to return _tmain() + int nResOSErrCode = ERROR_INTERNAL_ERROR; + + + //Make shut-down flags + DWORD dwShutDownFlags = 0; + + BOOL bSetShutdownPriv = FALSE; + + //Is it a remote computer + BOOL bRemoteComp = pAI->pStrRemoteCompName && pAI->pStrRemoteCompName[0]; + + + //For some reason the API needs non-constant strings + TCHAR* pStrMessage = NULL; + int nLnRemoteCompName = pAI->pStrRemoteCompName && pAI->pStrRemoteCompName[0] ? lstrlen(pAI->pStrRemoteCompName) : 0; + TCHAR* pStrRemoteCompName = new (std::nothrow) TCHAR[nLnRemoteCompName + 1]; + if(pStrRemoteCompName) + { + //Copy it + if(nLnRemoteCompName > 0) + memcpy(pStrRemoteCompName, pAI->pStrRemoteCompName, nLnRemoteCompName * sizeof(TCHAR)); + + pStrRemoteCompName[nLnRemoteCompName] = 0; + + //Now the message to the user + int nLnMessage = pAI->pStrMessage && pAI->pStrMessage[0] ? lstrlen(pAI->pStrMessage) : 0; + pStrMessage = new (std::nothrow) TCHAR[nLnMessage + 1]; + if(pStrMessage) + { + //Copy it + if(nLnMessage > 0) + memcpy(pStrMessage, pAI->pStrMessage, nLnMessage * sizeof(TCHAR)); + + pStrMessage[nLnMessage] = 0; + + + //Error message buffer + TCHAR buffErrMsg[1024]; + buffErrMsg[0] = 0; + + LPCTSTR pStrPwrOpName = L""; + + + //Go by power op type + switch(pAI->pwrAction) + { + case PWR_OP_SHUT_DOWN: + { + pStrPwrOpName = L"shut-down"; + dwShutDownFlags = SHUTDOWN_POWEROFF; + bSetShutdownPriv = TRUE; + } + break; + + case PWR_OP_REBOOT: + { + pStrPwrOpName = L"rebooting"; + dwShutDownFlags = SHUTDOWN_RESTART; + } + break; + + case PWR_OP_HYBRID_SHUT_DOWN: + { + pStrPwrOpName = L"hybrid shut-down"; + dwShutDownFlags = SHUTDOWN_POWEROFF | SHUTDOWN_HYBRID; + bSetShutdownPriv = TRUE; + } + break; + + case PWR_OP_REBOOT_WITH_APP_RESTART: + { + pStrPwrOpName = L"rebooting & restart of registered apps"; + dwShutDownFlags = SHUTDOWN_RESTART | SHUTDOWN_RESTARTAPPS; + } + break; + + case PWR_OP_ADVANCED_BOOT_OPTIONS_MENU: + { + pStrPwrOpName = L"going to advanced boot options menu"; + dwShutDownFlags = SHUTDOWN_RESTART; + dwShutDownFlags |= 0x400; //Special flag for Windows 10 + + //Cannot install updates, but it can be forced + pAI->bNoUpdates = TRUE; + } + break; + + case PWR_OP_ABORT_SHUT_DOWN: + { + //Abort previous + + //Don't run main action + dwShutDownFlags = 0; + + //Set needed privelge (it will post warnings internally) + SetNeededShutdownPrivileges(pAI->pStrRemoteCompName); + + //Abort + if(::AbortSystemShutdown(nLnRemoteCompName > 0 ? pStrRemoteCompName : NULL)) + { + //Success + nResOSErrCode = NO_ERROR; + _tprintf(L"Success aborting power operation...\n"); + } + else + { + //Failed + nResOSErrCode = ::GetLastError(); + _tprintf(L"ERROR: Failed to abort power operation: (%d) %s\n", + nResOSErrCode, + CMain::FormatErrorMessage(nResOSErrCode, buffErrMsg, SIZEOF(buffErrMsg)) + ); + } + } + break; + + default: + { + //Wrong flag + ASSERT(NULL); + nResOSErrCode = ERROR_INVALID_USER_BUFFER; + _tprintf(L"INTERNAL ERROR: Wrong action type=%d\n", pAI->pwrAction); + } + break; + } + + + if(dwShutDownFlags != 0) + { + //Adjust computer name + LPTSTR pStrCompNm = nLnRemoteCompName > 0 ? pStrRemoteCompName : NULL; + + //See if we have first two leading slashes + if(nLnRemoteCompName > 2) + { + if(pStrCompNm[0] == '\\' || + pStrCompNm[1] == '\\') + { + //Skip slashes + pStrCompNm = pStrCompNm + 2; + } + } + + //Set needed privelge (it will post warnings internally) + SetNeededShutdownPrivileges(pStrCompNm); + + + //See what API do we need to use? + if(!pAI->bNoUpdates || + (pAI->pwrAction != PWR_OP_SHUT_DOWN && + pAI->pwrAction != PWR_OP_REBOOT) + ) + { + //Installing updates? + if(!pAI->bNoUpdates) + { + dwShutDownFlags |= SHUTDOWN_INSTALL_UPDATES; + } + + //Forced? + if(pAI->bForced) + { + dwShutDownFlags |= SHUTDOWN_FORCE_SELF | SHUTDOWN_FORCE_OTHERS; + } + + + //Now execute power op + ::SetLastError(NO_ERROR); + nResOSErrCode = ::InitiateShutdown( + pStrCompNm, + nLnMessage > 0 ? pStrMessage : NULL, + pAI->nTimeoutSec, + dwShutDownFlags, + pAI->dwReason); + } + else + { + //Use different API + //INFO: We need to do this because InitiateShutdown() does not work on remote computers! + + //Pick operation + BOOL bRebootAfter = -1; + if(pAI->pwrAction == PWR_OP_SHUT_DOWN) + { + bRebootAfter = FALSE; + } + else if(pAI->pwrAction == PWR_OP_REBOOT) + { + bRebootAfter = TRUE; + } + + if(bRebootAfter == TRUE || + bRebootAfter == FALSE) + { + //Initiate now + ::SetLastError(NO_ERROR); + if(::InitiateSystemShutdownEx( + pStrCompNm, + nLnMessage > 0 ? pStrMessage : NULL, + pAI->nTimeoutSec, + pAI->bForced, + bRebootAfter, + pAI->dwReason)) + { + //Success + nResOSErrCode = NO_ERROR; + } + else + { + //Failed + nResOSErrCode = ::GetLastError(); + } + } + else + { + //Bad power op + nResOSErrCode = ERROR_INVALID_FUNCTION; + } + } + + //Check result + if(nResOSErrCode == NO_ERROR) + { + //Success + _tprintf(L"Success initiating %s...\n", pStrPwrOpName); + } + else + { + //Failed + _tprintf(L"ERROR: Failed to initiate %s: (%d) %s\n", + pStrPwrOpName, + nResOSErrCode, + CMain::FormatErrorMessage(nResOSErrCode, buffErrMsg, SIZEOF(buffErrMsg)) + ); + } + } + + } + else + nResOSErrCode = ERROR_OUTOFMEMORY; + } + else + nResOSErrCode = ERROR_OUTOFMEMORY; + + + + //Free mem + if(pStrRemoteCompName) + { + delete[] pStrRemoteCompName; + pStrRemoteCompName = NULL; + } + + if(pStrMessage) + { + delete[] pStrMessage; + pStrMessage = NULL; + } + + return nResOSErrCode; +} + + + +void CMain::outputMainLogo() +{ + SYSTEMTIME st; + ::GetSystemTime(&st); + + _tprintf( + L"Utility To Install Pre-Downloaded Windows Updates & Shutdown/Reboot\n" + L"v.%s\n" + L"Copyright (c) %s%d by www.dennisbabkin.com. All rights reserved.\n" + L"\n" + , + PROG_VER, + st.wYear > 2016 ? L"2016-" : L"", + st.wYear + ); + +} + + + + + diff --git a/ShutdownWithUpdates/ShutdownWithUpdates/Main.h b/ShutdownWithUpdates/ShutdownWithUpdates/Main.h new file mode 100644 index 0000000..967dfe0 --- /dev/null +++ b/ShutdownWithUpdates/ShutdownWithUpdates/Main.h @@ -0,0 +1,62 @@ +/* + * ShutdownWithUpdates + * "Utility To Install Pre-Downloaded Windows Updates & Shutdown/Reboot" + * Copyright (c) 2016-2019 www.dennisbabkin.com + * + * https://dennisbabkin.com/utilities/#ShutdownWithUpdates + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + + + +#pragma once + + +#include "types.h" +#include "resource.h" + + + +class CMain +{ +private: + CMain(void); + ~CMain(void); +public: + static int doWork(int argc, _TCHAR* argv[]); +private: + static int doWork_RAW(int argc, _TCHAR* argv[]); +protected: + static int CheckForXP_SP2_FileBlockAndRemoveIt(); + static CMD_TYPE getCommandType(LPCTSTR pStrCmd); + static void ShowHelpInfo(); + static int findCharInStrCaseSensitive(LPCTSTR pStr, TCHAR chSearch, int nIndBegin = 0); + static BOOL parseReasonInt32(LPCTSTR pStr, int* pnOutVal = NULL); + static BOOL parseDecimalInt32(LPCTSTR pStr, int* pnOutVal = NULL); + static BOOL parseHexUInt32(LPCTSTR pStr, UINT* pnOutVal = NULL); + static BOOL parseReasonParam(LPCTSTR pStrParam, DWORD* pnOutReason = NULL); + static TCHAR* CapitalizeFirstLetter(TCHAR* pBuff); + static BOOL FormatSecSmart(TCHAR* pBuff, int nchBuffSz, int nSeconds); + static int ShowUserConfirmation(ACTIONS_INFO* pAI); + static BOOL AdjustPrivilege(LPCTSTR pStrMachine, LPCTSTR pPrivilegeName, BOOL bEnable, HANDLE hProcess = NULL); + static TCHAR* trimBuffer(TCHAR* pBuff); + static TCHAR* trimBufferLeft(TCHAR* pBuff); + static TCHAR* trimBufferRight(TCHAR* pBuff); + static TCHAR* FormatErrorMessage(int nOSError, TCHAR* pBuff, int nchLnBuff, BOOL bUseDescriptionForNoError = FALSE); + static BOOL SetNeededShutdownPrivileges(LPCTSTR pStrRemoteCompName); + static int doActions(ACTIONS_INFO* pAI); + static void outputMainLogo(); + +}; diff --git a/ShutdownWithUpdates/ShutdownWithUpdates/ShutdownWithUpdates.cpp b/ShutdownWithUpdates/ShutdownWithUpdates/ShutdownWithUpdates.cpp new file mode 100644 index 0000000..d6d81ed --- /dev/null +++ b/ShutdownWithUpdates/ShutdownWithUpdates/ShutdownWithUpdates.cpp @@ -0,0 +1,37 @@ +/* + * ShutdownWithUpdates + * "Utility To Install Pre-Downloaded Windows Updates & Shutdown/Reboot" + * Copyright (c) 2016-2019 www.dennisbabkin.com + * + * https://dennisbabkin.com/utilities/#ShutdownWithUpdates + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + + + +#include "stdafx.h" +#include "Main.h" + + +int _tmain(int argc, _TCHAR* argv[]) +{ + //RETURN: + // = Exit code from the main process: + // = 0 if success + // = -1 if general exception in the process + // = Other - OS Error code + return CMain::doWork(argc, argv); +} + diff --git a/ShutdownWithUpdates/ShutdownWithUpdates/ShutdownWithUpdates.rc b/ShutdownWithUpdates/ShutdownWithUpdates/ShutdownWithUpdates.rc new file mode 100644 index 0000000..f6e1a4f --- /dev/null +++ b/ShutdownWithUpdates/ShutdownWithUpdates/ShutdownWithUpdates.rc @@ -0,0 +1,135 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,2,1,0 + PRODUCTVERSION 1,2,1,0 + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x0L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "www.dennisbabkin.com" + VALUE "FileDescription", "Utility To Install Pre-Downloaded Windows Updates & Shutdown/Reboot" + VALUE "FileVersion", "1.2.1.0" + VALUE "InternalName", "ShutdownWithUpdates.exe" + VALUE "LegalCopyright", "Copyright (c) 2016-2019 by dennisbabkin.com. All rights reserved." + VALUE "OriginalFilename", "ShutdownWithUpdates.exe" + VALUE "ProductName", "Utility To Install Pre-Downloaded Windows Updates & Shutdown/Reboot" + VALUE "ProductVersion", "1.2.1.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_STRING101 "install downloaded updates" + IDS_STRING102 "shut down computer" + IDS_STRING103 "reboot computer" + IDS_STRING104 "hybrid shut-down of computer" + IDS_STRING105 "reboot & restart registered apps" + IDS_STRING106 "abort pending shut-down or rebooting" + IDS_STRING107 "forced" + IDS_STRING108 "local" + IDS_STRING109 "timeout" + IDS_STRING110 "s" + IDS_STRING111 "none" +END + +STRINGTABLE +BEGIN + IDS_STRING112 "User Message" + IDS_STRING113 "Do you want to perform the following operation?" + IDS_STRING114 "computer" + IDS_STRING115 "ShutdownWithUpdates Tool" + IDS_STRING116 "m" + IDS_STRING117 "h" + IDS_STRING118 "d" + IDS_STRING119 "go to advanced boot options menu" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/ShutdownWithUpdates/ShutdownWithUpdates/ShutdownWithUpdates.vcproj b/ShutdownWithUpdates/ShutdownWithUpdates/ShutdownWithUpdates.vcproj new file mode 100644 index 0000000..0be2bf8 --- /dev/null +++ b/ShutdownWithUpdates/ShutdownWithUpdates/ShutdownWithUpdates.vcproj @@ -0,0 +1,249 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ShutdownWithUpdates/ShutdownWithUpdates/Types.h b/ShutdownWithUpdates/ShutdownWithUpdates/Types.h new file mode 100644 index 0000000..87876ae --- /dev/null +++ b/ShutdownWithUpdates/ShutdownWithUpdates/Types.h @@ -0,0 +1,214 @@ +//Custom types + +/* + * ShutdownWithUpdates + * "Utility To Install Pre-Downloaded Windows Updates & Shutdown/Reboot" + * Copyright (c) 2016-2019 www.dennisbabkin.com + * + * https://dennisbabkin.com/utilities/#ShutdownWithUpdates + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + + + +#pragma once + + + + + +enum CMD_TYPE{ + CTP_NONE, + CTP_UNKNOWN, //Unknown command + CTP_SHOW_HELP, //-? + CTP_SHUT_DOWN, //-s + CTP_REBOOT, //-r + CTP_HYBRID_SHUT_DOWN, //-hs + CTP_REBOOT_WITH_APP_RESTART, //-g + CTP_ADVANCED_BOOT_MENU, //-abo + CTP_ABORT_SHUT_DOWN, //-a + CTP_FORCED, //-f + CTP_VERBOSE, //-v + CTP_NO_UPDATES, //-nu + CTP_REMOTE_COMPUTER, //-m + CTP_TIMEOUT, //-t + CTP_SHOW_MESSAGE, //-c + CTP_REASON, //-d +}; + + + +struct CMD_STR{ + CMD_TYPE cmdID; + LPCTSTR pStrCmd; +}; + + + +enum POWER_OP{ + PWR_OP_NONE, + PWR_OP_SHUT_DOWN, + PWR_OP_REBOOT, + PWR_OP_HYBRID_SHUT_DOWN, + PWR_OP_REBOOT_WITH_APP_RESTART, + PWR_OP_ABORT_SHUT_DOWN, + PWR_OP_ADVANCED_BOOT_OPTIONS_MENU, +}; + + +#ifndef SHUTDOWN_HYBRID +#define SHUTDOWN_HYBRID 0x00000200 +#endif + + + + +struct ACTIONS_INFO{ + POWER_OP pwrAction; + DWORD dwReason; + BOOL bForced; + BOOL bVerbose; + BOOL bNoUpdates; + + LPCTSTR pStrRemoteCompName; + int nTimeoutSec; + + LPCTSTR pStrMessage; + + ACTIONS_INFO() + { + pwrAction = PWR_OP_NONE; + dwReason = SHTDN_REASON_FLAG_PLANNED | 0xFF; + bForced = FALSE; + bVerbose = FALSE; + bNoUpdates = FALSE; + + pStrRemoteCompName = NULL; + nTimeoutSec = 0; + + pStrMessage = NULL; + } +}; + + +#pragma warning(push) +#pragma warning(disable: 4200) +struct MY_STRINGRESOURCEIMAGE +{ + WORD nLength; + WCHAR achString[]; +}; +#pragma warning(pop) // C4200 + + + +struct LOC_STRING{ + LOC_STRING(UINT nID) + { + //'nID' = string ID from resources + pString = NULL; + loadStringFromRsrc(nID); + } + + ~LOC_STRING() + { + //Free mem + freeString(); + } + + operator const TCHAR*() + { + return pString ? pString : L""; + } + operator TCHAR*() + { + return pString ? pString : L""; + } + +private: + TCHAR* pString; //Loaded string, or NULL if error + +private: + void freeString() + { + if(pString) + { + delete[] pString; + pString = NULL; + } + } + + BOOL loadStringFromRsrc(UINT nID) + { + //RETURN: + // = TRUE if success + BOOL bRes = FALSE; + + HMODULE hInstance = ::GetModuleHandle(NULL); + if(hInstance) + { + HRSRC hResource = ::FindResource(hInstance, MAKEINTRESOURCE( ((nID>>4)+1) ), RT_STRING); + if(hResource) + { + HGLOBAL hGlobal = ::LoadResource(hInstance, hResource); + if(hGlobal) + { + const MY_STRINGRESOURCEIMAGE* pImage = (const MY_STRINGRESOURCEIMAGE*)::LockResource(hGlobal); + if(pImage) + { + ULONG nResourceSize = ::SizeofResource(hInstance, hResource); + + const MY_STRINGRESOURCEIMAGE* pImageEnd = (const MY_STRINGRESOURCEIMAGE*)(LPBYTE( pImage )+nResourceSize); + UINT iIndex = nID & 0x000f; + + while( (iIndex > 0) && (pImage < pImageEnd) ) + { + pImage = (const MY_STRINGRESOURCEIMAGE*)(LPBYTE( pImage )+(sizeof( MY_STRINGRESOURCEIMAGE )+(pImage->nLength * sizeof( WCHAR )))); + iIndex--; + } + + if(pImage < pImageEnd) + { + if(pImage->nLength != 0) + { + //Free old string + freeString(); + + //Reserve mem + pString = new (std::nothrow) TCHAR[pImage->nLength + 1]; + if(pString) + { + //Copy string + memcpy(pString, pImage->achString, pImage->nLength * sizeof(TCHAR)); + pString[pImage->nLength] = 0; + + bRes = TRUE; + } + } + } + } + } + } + } + + return bRes; + } +}; + + + + + + diff --git a/ShutdownWithUpdates/ShutdownWithUpdates/altstreams.h b/ShutdownWithUpdates/ShutdownWithUpdates/altstreams.h new file mode 100644 index 0000000..9541799 --- /dev/null +++ b/ShutdownWithUpdates/ShutdownWithUpdates/altstreams.h @@ -0,0 +1,81 @@ +// +// This header contains definitions from DDK +// +// ADS-related definitions +// +#include + + +//#ifndef NTSTATUS +//typedef INT NTSTATUS; +//#endif + +// The only return code we check for +#define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L) + + +typedef struct _IO_STATUS_BLOCK { + NTSTATUS Status; + ULONG Information; +} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; + + +typedef enum _FILE_INFORMATION_CLASS { + FileDirectoryInformation = 1, // 1 + FileFullDirectoryInformation, // 2 + FileBothDirectoryInformation, // 3 + FileBasicInformation, // 4 + FileStandardInformation, // 5 + FileInternalInformation, // 6 + FileEaInformation, // 7 + FileAccessInformation, // 8 + FileNameInformation, // 9 + FileRenameInformation, // 10 + FileLinkInformation, // 11 + FileNamesInformation, // 12 + FileDispositionInformation, // 13 + FilePositionInformation, // 14 + FileModeInformation = 16, // 16 + FileAlignmentInformation, // 17 + FileAllInformation, // 18 + FileAllocationInformation, // 19 + FileEndOfFileInformation, // 20 + FileAlternateNameInformation, // 21 + FileStreamInformation, // 22 + FilePipeInformation, // 23 + FilePipeLocalInformation, // 24 + FilePipeRemoteInformation, // 25 + FileMailslotQueryInformation, // 26 + FileMailslotSetInformation, // 27 + FileCompressionInformation, // 28 + FileObjectIdInformation, // 29 + FileCompletionInformation, // 30 + FileMoveClusterInformation, // 31 + FileQuotaInformation, // 32 + FileReparsePointInformation, // 33 + FileNetworkOpenInformation, // 34 + FileAttributeTagInformation, // 35 + FileTrackingInformation // 36 +} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS; + + +#pragma pack(push, 4) + +typedef struct _FILE_STREAM_INFORMATION { // Information Class 22 + ULONG NextEntryOffset; + ULONG StreamNameLength; + LARGE_INTEGER EndOfStream; + LARGE_INTEGER AllocationSize; + WCHAR StreamName[1]; +} FILE_STREAM_INFORMATION, *PFILE_STREAM_INFORMATION; + +#pragma pack(pop) + + +// Define pointer to function type for dynamic loading +typedef NTSTATUS (NTAPI *NTQUERYINFORMATIONFILE)( + IN HANDLE FileHandle, + OUT PIO_STATUS_BLOCK IoStatusBlock, + OUT PVOID FileInformation, + IN ULONG Length, + IN FILE_INFORMATION_CLASS FileInformationClass); diff --git a/ShutdownWithUpdates/ShutdownWithUpdates/ntddstor.h b/ShutdownWithUpdates/ShutdownWithUpdates/ntddstor.h new file mode 100644 index 0000000..b5e34b6 --- /dev/null +++ b/ShutdownWithUpdates/ShutdownWithUpdates/ntddstor.h @@ -0,0 +1,750 @@ +/*++ BUILD Version: 0001 // Increment this if a change has global effects + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + ntddstor.h + +Abstract: + + This is the include file that defines all common constants and types + accessing the storage class drivers + +Author: + + Peter Wieland 19-Jun-1996 + +Revision History: + +--*/ + +#pragma once + +//#include + +// +// Interface GUIDs +// +// need these GUIDs outside conditional includes so that user can +// #include in precompiled header +// #include in a single source file +// #include in that source file a second time to instantiate the GUIDs +// +#ifdef DEFINE_GUID +// +// Make sure FAR is defined... +// +#ifndef FAR +#ifdef _WIN32 +#define FAR +#else +#define FAR _far +#endif +#endif + +/* +// begin_wioctlguids +DEFINE_GUID(GUID_DEVINTERFACE_DISK, 0x53f56307L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); +DEFINE_GUID(GUID_DEVINTERFACE_CDROM, 0x53f56308L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); +DEFINE_GUID(GUID_DEVINTERFACE_PARTITION, 0x53f5630aL, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); +DEFINE_GUID(GUID_DEVINTERFACE_TAPE, 0x53f5630bL, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); +DEFINE_GUID(GUID_DEVINTERFACE_WRITEONCEDISK, 0x53f5630cL, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); +DEFINE_GUID(GUID_DEVINTERFACE_VOLUME, 0x53f5630dL, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); +DEFINE_GUID(GUID_DEVINTERFACE_MEDIUMCHANGER, 0x53f56310L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); +DEFINE_GUID(GUID_DEVINTERFACE_FLOPPY, 0x53f56311L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); +DEFINE_GUID(GUID_DEVINTERFACE_CDCHANGER, 0x53f56312L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); +DEFINE_GUID(GUID_DEVINTERFACE_STORAGEPORT, 0x2accfe60L, 0xc130, 0x11d2, 0xb0, 0x82, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); +// end_wioctlguids +*/ +// begin_wioctlobsoleteguids +#define DiskClassGuid GUID_DEVINTERFACE_DISK +#define CdRomClassGuid GUID_DEVINTERFACE_CDROM +#define PartitionClassGuid GUID_DEVINTERFACE_PARTITION +#define TapeClassGuid GUID_DEVINTERFACE_TAPE +#define WriteOnceDiskClassGuid GUID_DEVINTERFACE_WRITEONCEDISK +#define VolumeClassGuid GUID_DEVINTERFACE_VOLUME +#define MediumChangerClassGuid GUID_DEVINTERFACE_MEDIUMCHANGER +#define FloppyClassGuid GUID_DEVINTERFACE_FLOPPY +#define CdChangerClassGuid GUID_DEVINTERFACE_CDCHANGER +#define StoragePortClassGuid GUID_DEVINTERFACE_STORAGEPORT +// end_wioctlobsoleteguids +#endif + +// begin_winioctl + +#ifndef _NTDDSTOR_H_ +#define _NTDDSTOR_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +// +// IoControlCode values for storage devices +// + +#define IOCTL_STORAGE_BASE FILE_DEVICE_MASS_STORAGE + +// +// The following device control codes are common for all class drivers. They +// should be used in place of the older IOCTL_DISK, IOCTL_CDROM and IOCTL_TAPE +// common codes +// + +#define IOCTL_STORAGE_CHECK_VERIFY CTL_CODE(IOCTL_STORAGE_BASE, 0x0200, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_STORAGE_CHECK_VERIFY2 CTL_CODE(IOCTL_STORAGE_BASE, 0x0200, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_STORAGE_MEDIA_REMOVAL CTL_CODE(IOCTL_STORAGE_BASE, 0x0201, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_STORAGE_EJECT_MEDIA CTL_CODE(IOCTL_STORAGE_BASE, 0x0202, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_STORAGE_LOAD_MEDIA CTL_CODE(IOCTL_STORAGE_BASE, 0x0203, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_STORAGE_LOAD_MEDIA2 CTL_CODE(IOCTL_STORAGE_BASE, 0x0203, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_STORAGE_RESERVE CTL_CODE(IOCTL_STORAGE_BASE, 0x0204, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_STORAGE_RELEASE CTL_CODE(IOCTL_STORAGE_BASE, 0x0205, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_STORAGE_FIND_NEW_DEVICES CTL_CODE(IOCTL_STORAGE_BASE, 0x0206, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_STORAGE_EJECTION_CONTROL CTL_CODE(IOCTL_STORAGE_BASE, 0x0250, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_STORAGE_MCN_CONTROL CTL_CODE(IOCTL_STORAGE_BASE, 0x0251, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_STORAGE_GET_MEDIA_TYPES CTL_CODE(IOCTL_STORAGE_BASE, 0x0300, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_STORAGE_GET_MEDIA_TYPES_EX CTL_CODE(IOCTL_STORAGE_BASE, 0x0301, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER CTL_CODE(IOCTL_STORAGE_BASE, 0x0304, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_STORAGE_GET_HOTPLUG_INFO CTL_CODE(IOCTL_STORAGE_BASE, 0x0305, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_STORAGE_SET_HOTPLUG_INFO CTL_CODE(IOCTL_STORAGE_BASE, 0x0306, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +#define IOCTL_STORAGE_RESET_BUS CTL_CODE(IOCTL_STORAGE_BASE, 0x0400, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_STORAGE_RESET_DEVICE CTL_CODE(IOCTL_STORAGE_BASE, 0x0401, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_STORAGE_BREAK_RESERVATION CTL_CODE(IOCTL_STORAGE_BASE, 0x0405, METHOD_BUFFERED, FILE_READ_ACCESS) + +#define IOCTL_STORAGE_GET_DEVICE_NUMBER CTL_CODE(IOCTL_STORAGE_BASE, 0x0420, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_STORAGE_PREDICT_FAILURE CTL_CODE(IOCTL_STORAGE_BASE, 0x0440, METHOD_BUFFERED, FILE_ANY_ACCESS) + +// end_winioctl + + +#define IOCTL_STORAGE_QUERY_PROPERTY CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS) + + +// begin_winioctl + +// +// These ioctl codes are obsolete. They are defined here to avoid resuing them +// and to allow class drivers to respond to them more easily. +// + +#define OBSOLETE_IOCTL_STORAGE_RESET_BUS CTL_CODE(IOCTL_STORAGE_BASE, 0x0400, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) +#define OBSOLETE_IOCTL_STORAGE_RESET_DEVICE CTL_CODE(IOCTL_STORAGE_BASE, 0x0401, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) + + +// +// IOCTL_STORAGE_GET_HOTPLUG_INFO +// + +typedef struct _STORAGE_HOTPLUG_INFO { + ULONG Size; // version + BOOLEAN MediaRemovable; // ie. zip, jaz, cdrom, mo, etc. vs hdd + BOOLEAN MediaHotplug; // ie. does the device succeed a lock even though its not lockable media? + BOOLEAN DeviceHotplug; // ie. 1394, USB, etc. + BOOLEAN WriteCacheEnableOverride; // This field should not be relied upon because it is no longer used +} STORAGE_HOTPLUG_INFO, *PSTORAGE_HOTPLUG_INFO; + +// +// IOCTL_STORAGE_GET_DEVICE_NUMBER +// +// input - none +// +// output - STORAGE_DEVICE_NUMBER structure +// The values in the STORAGE_DEVICE_NUMBER structure are guaranteed +// to remain unchanged until the system is rebooted. They are not +// guaranteed to be persistant across boots. +// + +/* +typedef struct _STORAGE_DEVICE_NUMBER { + + // + // The FILE_DEVICE_XXX type for this device. + // + + DEVICE_TYPE DeviceType; + + // + // The number of this device + // + + ULONG DeviceNumber; + + // + // If the device is partitionable, the partition number of the device. + // Otherwise -1 + // + + ULONG PartitionNumber; +} STORAGE_DEVICE_NUMBER, *PSTORAGE_DEVICE_NUMBER; +*/ + +// +// Define the structures for scsi resets +// + +typedef struct _STORAGE_BUS_RESET_REQUEST { + UCHAR PathId; +} STORAGE_BUS_RESET_REQUEST, *PSTORAGE_BUS_RESET_REQUEST; + +// +// IOCTL_STORAGE_MEDIA_REMOVAL disables the mechanism +// on a storage device that ejects media. This function +// may or may not be supported on storage devices that +// support removable media. +// +// TRUE means prevent media from being removed. +// FALSE means allow media removal. +// + +typedef struct _PREVENT_MEDIA_REMOVAL { + BOOLEAN PreventMediaRemoval; +} PREVENT_MEDIA_REMOVAL, *PPREVENT_MEDIA_REMOVAL; + +// begin_ntminitape + + +typedef struct _TAPE_STATISTICS { + ULONG Version; + ULONG Flags; + LARGE_INTEGER RecoveredWrites; + LARGE_INTEGER UnrecoveredWrites; + LARGE_INTEGER RecoveredReads; + LARGE_INTEGER UnrecoveredReads; + UCHAR CompressionRatioReads; + UCHAR CompressionRatioWrites; +} TAPE_STATISTICS, *PTAPE_STATISTICS; + +#define RECOVERED_WRITES_VALID 0x00000001 +#define UNRECOVERED_WRITES_VALID 0x00000002 +#define RECOVERED_READS_VALID 0x00000004 +#define UNRECOVERED_READS_VALID 0x00000008 +#define WRITE_COMPRESSION_INFO_VALID 0x00000010 +#define READ_COMPRESSION_INFO_VALID 0x00000020 + +typedef struct _TAPE_GET_STATISTICS { + ULONG Operation; +} TAPE_GET_STATISTICS, *PTAPE_GET_STATISTICS; + +#define TAPE_RETURN_STATISTICS 0L +#define TAPE_RETURN_ENV_INFO 1L +#define TAPE_RESET_STATISTICS 2L + +// +// IOCTL_STORAGE_GET_MEDIA_TYPES_EX will return an array of DEVICE_MEDIA_INFO +// structures, one per supported type, embedded in the GET_MEDIA_TYPES struct. +// + +typedef enum _STORAGE_MEDIA_TYPE { + // + // Following are defined in ntdddisk.h in the MEDIA_TYPE enum + // + // Unknown, // Format is unknown + // F5_1Pt2_512, // 5.25", 1.2MB, 512 bytes/sector + // F3_1Pt44_512, // 3.5", 1.44MB, 512 bytes/sector + // F3_2Pt88_512, // 3.5", 2.88MB, 512 bytes/sector + // F3_20Pt8_512, // 3.5", 20.8MB, 512 bytes/sector + // F3_720_512, // 3.5", 720KB, 512 bytes/sector + // F5_360_512, // 5.25", 360KB, 512 bytes/sector + // F5_320_512, // 5.25", 320KB, 512 bytes/sector + // F5_320_1024, // 5.25", 320KB, 1024 bytes/sector + // F5_180_512, // 5.25", 180KB, 512 bytes/sector + // F5_160_512, // 5.25", 160KB, 512 bytes/sector + // RemovableMedia, // Removable media other than floppy + // FixedMedia, // Fixed hard disk media + // F3_120M_512, // 3.5", 120M Floppy + // F3_640_512, // 3.5" , 640KB, 512 bytes/sector + // F5_640_512, // 5.25", 640KB, 512 bytes/sector + // F5_720_512, // 5.25", 720KB, 512 bytes/sector + // F3_1Pt2_512, // 3.5" , 1.2Mb, 512 bytes/sector + // F3_1Pt23_1024, // 3.5" , 1.23Mb, 1024 bytes/sector + // F5_1Pt23_1024, // 5.25", 1.23MB, 1024 bytes/sector + // F3_128Mb_512, // 3.5" MO 128Mb 512 bytes/sector + // F3_230Mb_512, // 3.5" MO 230Mb 512 bytes/sector + // F8_256_128, // 8", 256KB, 128 bytes/sector + // F3_200Mb_512, // 3.5", 200M Floppy (HiFD) + // + + DDS_4mm = 0x20, // Tape - DAT DDS1,2,... (all vendors) + MiniQic, // Tape - miniQIC Tape + Travan, // Tape - Travan TR-1,2,3,... + QIC, // Tape - QIC + MP_8mm, // Tape - 8mm Exabyte Metal Particle + AME_8mm, // Tape - 8mm Exabyte Advanced Metal Evap + AIT1_8mm, // Tape - 8mm Sony AIT + DLT, // Tape - DLT Compact IIIxt, IV + NCTP, // Tape - Philips NCTP + IBM_3480, // Tape - IBM 3480 + IBM_3490E, // Tape - IBM 3490E + IBM_Magstar_3590, // Tape - IBM Magstar 3590 + IBM_Magstar_MP, // Tape - IBM Magstar MP + STK_DATA_D3, // Tape - STK Data D3 + SONY_DTF, // Tape - Sony DTF + DV_6mm, // Tape - 6mm Digital Video + DMI, // Tape - Exabyte DMI and compatibles + SONY_D2, // Tape - Sony D2S and D2L + CLEANER_CARTRIDGE, // Cleaner - All Drive types that support Drive Cleaners + CD_ROM, // Opt_Disk - CD + CD_R, // Opt_Disk - CD-Recordable (Write Once) + CD_RW, // Opt_Disk - CD-Rewriteable + DVD_ROM, // Opt_Disk - DVD-ROM + DVD_R, // Opt_Disk - DVD-Recordable (Write Once) + DVD_RW, // Opt_Disk - DVD-Rewriteable + MO_3_RW, // Opt_Disk - 3.5" Rewriteable MO Disk + MO_5_WO, // Opt_Disk - MO 5.25" Write Once + MO_5_RW, // Opt_Disk - MO 5.25" Rewriteable (not LIMDOW) + MO_5_LIMDOW, // Opt_Disk - MO 5.25" Rewriteable (LIMDOW) + PC_5_WO, // Opt_Disk - Phase Change 5.25" Write Once Optical + PC_5_RW, // Opt_Disk - Phase Change 5.25" Rewriteable + PD_5_RW, // Opt_Disk - PhaseChange Dual Rewriteable + ABL_5_WO, // Opt_Disk - Ablative 5.25" Write Once Optical + PINNACLE_APEX_5_RW, // Opt_Disk - Pinnacle Apex 4.6GB Rewriteable Optical + SONY_12_WO, // Opt_Disk - Sony 12" Write Once + PHILIPS_12_WO, // Opt_Disk - Philips/LMS 12" Write Once + HITACHI_12_WO, // Opt_Disk - Hitachi 12" Write Once + CYGNET_12_WO, // Opt_Disk - Cygnet/ATG 12" Write Once + KODAK_14_WO, // Opt_Disk - Kodak 14" Write Once + MO_NFR_525, // Opt_Disk - Near Field Recording (Terastor) + NIKON_12_RW, // Opt_Disk - Nikon 12" Rewriteable + IOMEGA_ZIP, // Mag_Disk - Iomega Zip + IOMEGA_JAZ, // Mag_Disk - Iomega Jaz + SYQUEST_EZ135, // Mag_Disk - Syquest EZ135 + SYQUEST_EZFLYER, // Mag_Disk - Syquest EzFlyer + SYQUEST_SYJET, // Mag_Disk - Syquest SyJet + AVATAR_F2, // Mag_Disk - 2.5" Floppy + MP2_8mm, // Tape - 8mm Hitachi + DST_S, // Ampex DST Small Tapes + DST_M, // Ampex DST Medium Tapes + DST_L, // Ampex DST Large Tapes + VXATape_1, // Ecrix 8mm Tape + VXATape_2, // Ecrix 8mm Tape + STK_9840, // STK 9840 + LTO_Ultrium, // IBM, HP, Seagate LTO Ultrium + LTO_Accelis, // IBM, HP, Seagate LTO Accelis + DVD_RAM, // Opt_Disk - DVD-RAM + AIT_8mm, // AIT2 or higher + ADR_1, // OnStream ADR Mediatypes + ADR_2 +} STORAGE_MEDIA_TYPE, *PSTORAGE_MEDIA_TYPE; + +#define MEDIA_ERASEABLE 0x00000001 +#define MEDIA_WRITE_ONCE 0x00000002 +#define MEDIA_READ_ONLY 0x00000004 +#define MEDIA_READ_WRITE 0x00000008 + +#define MEDIA_WRITE_PROTECTED 0x00000100 +#define MEDIA_CURRENTLY_MOUNTED 0x80000000 + +// +// Define the different storage bus types +// Bus types below 128 (0x80) are reserved for Microsoft use +// + +typedef enum _STORAGE_BUS_TYPE { + BusTypeUnknown = 0x00, + BusTypeScsi, + BusTypeAtapi, + BusTypeAta, + BusType1394, + BusTypeSsa, + BusTypeFibre, + BusTypeUsb, + BusTypeRAID, + BusTypeMaxReserved = 0x7F +} STORAGE_BUS_TYPE, *PSTORAGE_BUS_TYPE; + +typedef struct _DEVICE_MEDIA_INFO { + union { + struct { + LARGE_INTEGER Cylinders; + STORAGE_MEDIA_TYPE MediaType; + ULONG TracksPerCylinder; + ULONG SectorsPerTrack; + ULONG BytesPerSector; + ULONG NumberMediaSides; + ULONG MediaCharacteristics; // Bitmask of MEDIA_XXX values. + } DiskInfo; + + struct { + LARGE_INTEGER Cylinders; + STORAGE_MEDIA_TYPE MediaType; + ULONG TracksPerCylinder; + ULONG SectorsPerTrack; + ULONG BytesPerSector; + ULONG NumberMediaSides; + ULONG MediaCharacteristics; // Bitmask of MEDIA_XXX values. + } RemovableDiskInfo; + + struct { + STORAGE_MEDIA_TYPE MediaType; + ULONG MediaCharacteristics; // Bitmask of MEDIA_XXX values. + ULONG CurrentBlockSize; + STORAGE_BUS_TYPE BusType; + + // + // Bus specific information describing the medium supported. + // + + union { + struct { + UCHAR MediumType; + UCHAR DensityCode; + } ScsiInformation; + } BusSpecificData; + + } TapeInfo; + } DeviceSpecific; +} DEVICE_MEDIA_INFO, *PDEVICE_MEDIA_INFO; + +typedef struct _GET_MEDIA_TYPES { + ULONG DeviceType; // FILE_DEVICE_XXX values + ULONG MediaInfoCount; + DEVICE_MEDIA_INFO MediaInfo[1]; +} GET_MEDIA_TYPES, *PGET_MEDIA_TYPES; + + +// +// IOCTL_STORAGE_PREDICT_FAILURE +// +// input - none +// +// output - STORAGE_PREDICT_FAILURE structure +// PredictFailure returns zero if no failure predicted and non zero +// if a failure is predicted. +// +// VendorSpecific returns 512 bytes of vendor specific information +// if a failure is predicted +// +typedef struct _STORAGE_PREDICT_FAILURE +{ + ULONG PredictFailure; + UCHAR VendorSpecific[512]; +} STORAGE_PREDICT_FAILURE, *PSTORAGE_PREDICT_FAILURE; + +// end_ntminitape +// end_winioctl + +// +// Property Query Structures +// + +// +// IOCTL_STORAGE_QUERY_PROPERTY +// +// Input Buffer: +// a STORAGE_PROPERTY_QUERY structure which describes what type of query +// is being done, what property is being queried for, and any additional +// parameters which a particular property query requires. +// +// Output Buffer: +// Contains a buffer to place the results of the query into. Since all +// property descriptors can be cast into a STORAGE_DESCRIPTOR_HEADER, +// the IOCTL can be called once with a small buffer then again using +// a buffer as large as the header reports is necessary. +// + + +// +// Types of queries +// + +typedef enum _STORAGE_QUERY_TYPE { + PropertyStandardQuery = 0, // Retrieves the descriptor + PropertyExistsQuery, // Used to test whether the descriptor is supported + PropertyMaskQuery, // Used to retrieve a mask of writeable fields in the descriptor + PropertyQueryMaxDefined // use to validate the value +} STORAGE_QUERY_TYPE, *PSTORAGE_QUERY_TYPE; + +// +// define some initial property id's +// + +typedef enum _STORAGE_PROPERTY_ID { + StorageDeviceProperty = 0, + StorageAdapterProperty, + StorageDeviceIdProperty +} STORAGE_PROPERTY_ID, *PSTORAGE_PROPERTY_ID; + +// +// Query structure - additional parameters for specific queries can follow +// the header +// + +typedef struct _STORAGE_PROPERTY_QUERY { + + // + // ID of the property being retrieved + // + + STORAGE_PROPERTY_ID PropertyId; + + // + // Flags indicating the type of query being performed + // + + STORAGE_QUERY_TYPE QueryType; + + // + // Space for additional parameters if necessary + // + + UCHAR AdditionalParameters[1]; + +} STORAGE_PROPERTY_QUERY, *PSTORAGE_PROPERTY_QUERY; + +// +// Standard property descriptor header. All property pages should use this +// as their first element or should contain these two elements +// + +typedef struct _STORAGE_DESCRIPTOR_HEADER { + + ULONG Version; + + ULONG Size; + +} STORAGE_DESCRIPTOR_HEADER, *PSTORAGE_DESCRIPTOR_HEADER; + +// +// Device property descriptor - this is really just a rehash of the inquiry +// data retrieved from a scsi device +// +// This may only be retrieved from a target device. Sending this to the bus +// will result in an error +// + +typedef struct _STORAGE_DEVICE_DESCRIPTOR { + + // + // Sizeof(STORAGE_DEVICE_DESCRIPTOR) + // + + ULONG Version; + + // + // Total size of the descriptor, including the space for additional + // data and id strings + // + + ULONG Size; + + // + // The SCSI-2 device type + // + + UCHAR DeviceType; + + // + // The SCSI-2 device type modifier (if any) - this may be zero + // + + UCHAR DeviceTypeModifier; + + // + // Flag indicating whether the device's media (if any) is removable. This + // field should be ignored for media-less devices + // + + BOOLEAN RemovableMedia; + + // + // Flag indicating whether the device can support mulitple outstanding + // commands. The actual synchronization in this case is the responsibility + // of the port driver. + // + + BOOLEAN CommandQueueing; + + // + // Byte offset to the zero-terminated ascii string containing the device's + // vendor id string. For devices with no such ID this will be zero + // + + ULONG VendorIdOffset; + + // + // Byte offset to the zero-terminated ascii string containing the device's + // product id string. For devices with no such ID this will be zero + // + + ULONG ProductIdOffset; + + // + // Byte offset to the zero-terminated ascii string containing the device's + // product revision string. For devices with no such string this will be + // zero + // + + ULONG ProductRevisionOffset; + + // + // Byte offset to the zero-terminated ascii string containing the device's + // serial number. For devices with no serial number this will be zero + // + + ULONG SerialNumberOffset; + + // + // Contains the bus type (as defined above) of the device. It should be + // used to interpret the raw device properties at the end of this structure + // (if any) + // + + STORAGE_BUS_TYPE BusType; + + // + // The number of bytes of bus-specific data which have been appended to + // this descriptor + // + + ULONG RawPropertiesLength; + + // + // Place holder for the first byte of the bus specific property data + // + + UCHAR RawDeviceProperties[1]; + +} STORAGE_DEVICE_DESCRIPTOR, *PSTORAGE_DEVICE_DESCRIPTOR; + + +// +// Adapter properties +// +// This descriptor can be retrieved from a target device object of from the +// device object for the bus. Retrieving from the target device object will +// forward the request to the underlying bus +// + +typedef struct _STORAGE_ADAPTER_DESCRIPTOR { + + ULONG Version; + + ULONG Size; + + ULONG MaximumTransferLength; + + ULONG MaximumPhysicalPages; + + ULONG AlignmentMask; + + BOOLEAN AdapterUsesPio; + + BOOLEAN AdapterScansDown; + + BOOLEAN CommandQueueing; + + BOOLEAN AcceleratedTransfer; + + UCHAR BusType; + + USHORT BusMajorVersion; + + USHORT BusMinorVersion; + +} STORAGE_ADAPTER_DESCRIPTOR, *PSTORAGE_ADAPTER_DESCRIPTOR; + +// +// Storage identification descriptor. +// The definitions here are based on the SCSI/SBP vital product data +// device identifier page. +// + +typedef enum _STORAGE_IDENTIFIER_CODE_SET { + StorageIdCodeSetReserved = 0, + StorageIdCodeSetBinary = 1, + StorageIdCodeSetAscii = 2 +} STORAGE_IDENTIFIER_CODE_SET, *PSTORAGE_IDENTIFIER_CODE_SET; + +typedef enum _STORAGE_IDENTIFIER_TYPE { + StorageIdTypeVendorSpecific = 0, + StorageIdTypeVendorId = 1, + StorageIdTypeEUI64 = 2, + StorageIdTypeFCPHName = 3, + StorageIdTypePortRelative = 4 +} STORAGE_IDENTIFIER_TYPE, *PSTORAGE_IDENTIFIER_TYPE; + +typedef enum _STORAGE_ASSOCIATION_TYPE { + StorageIdAssocDevice = 0, + StorageIdAssocPort = 1 +} STORAGE_ASSOCIATION_TYPE, *PSTORAGE_ASSOCIATION_TYPE; + +typedef struct _STORAGE_IDENTIFIER { + STORAGE_IDENTIFIER_CODE_SET CodeSet; + STORAGE_IDENTIFIER_TYPE Type; + USHORT IdentifierSize; + USHORT NextOffset; + + // + // Add new fields here since existing code depends on + // the above layout not changing. + // + + STORAGE_ASSOCIATION_TYPE Association; + + // + // The identifier is a variable length array of bytes. + // + + UCHAR Identifier[1]; +} STORAGE_IDENTIFIER, *PSTORAGE_IDENTIFIER; + +typedef struct _STORAGE_DEVICE_ID_DESCRIPTOR { + + ULONG Version; + + ULONG Size; + + // + // The number of identifiers reported by the device. + // + + ULONG NumberOfIdentifiers; + + // + // The following field is actually a variable length array of identification + // descriptors. Unfortunately there's no C notation for an array of + // variable length structures so we're forced to just pretend. + // + + UCHAR Identifiers[1]; +} STORAGE_DEVICE_ID_DESCRIPTOR, *PSTORAGE_DEVICE_ID_DESCRIPTOR; + + +#pragma warning(push) +#pragma warning(disable:4200) +typedef struct _STORAGE_MEDIA_SERIAL_NUMBER_DATA { + + USHORT Reserved; + + // + // the SerialNumberLength will be set to zero + // if the command is supported and the media + // does not have a valid serial number. + // + + USHORT SerialNumberLength; + + // + // the following data is binary, and is not guaranteed + // to be NULL terminated. this is an excercise for the + // caller. + // + + UCHAR SerialNumber[0]; + +} STORAGE_MEDIA_SERIAL_NUMBER_DATA, *PSTORAGE_MEDIA_SERIAL_NUMBER_DATA; +#pragma warning(push) + + +// begin_winioctl + +#ifdef __cplusplus +} +#endif + +#endif // _NTDDSTOR_H_ +// end_winioctl diff --git a/ShutdownWithUpdates/ShutdownWithUpdates/resource.h b/ShutdownWithUpdates/ShutdownWithUpdates/resource.h new file mode 100644 index 0000000..3135896 --- /dev/null +++ b/ShutdownWithUpdates/ShutdownWithUpdates/resource.h @@ -0,0 +1,34 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by ShutdownWithUpdates.rc +// +#define IDS_STRING101 101 +#define IDS_STRING102 102 +#define IDS_STRING103 103 +#define IDS_STRING104 104 +#define IDS_STRING105 105 +#define IDS_STRING106 106 +#define IDS_STRING107 107 +#define IDS_STRING108 108 +#define IDS_STRING109 109 +#define IDS_STRING110 110 +#define IDS_STRING111 111 +#define IDS_STRING112 112 +#define IDS_STRING113 113 +#define IDS_STRING114 114 +#define IDS_STRING115 115 +#define IDS_STRING116 116 +#define IDS_STRING117 117 +#define IDS_STRING118 118 +#define IDS_STRING119 119 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/ShutdownWithUpdates/ShutdownWithUpdates/rtlVersion.h b/ShutdownWithUpdates/ShutdownWithUpdates/rtlVersion.h new file mode 100644 index 0000000..3e947df --- /dev/null +++ b/ShutdownWithUpdates/ShutdownWithUpdates/rtlVersion.h @@ -0,0 +1,224 @@ +//Determines "true" version of the OS + +/* + * ShutdownWithUpdates + * "Utility To Install Pre-Downloaded Windows Updates & Shutdown/Reboot" + * Copyright (c) 2016-2019 www.dennisbabkin.com + * + * https://dennisbabkin.com/utilities/#ShutdownWithUpdates + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + + + +#pragma once + + + +#define TXT_SIZEOF(f) ((sizeof(f) / sizeof(f[0])) - 1) + + +struct RTL_OS_VERSION{ + static BOOL GetVersionEx2(OSVERSIONINFO* pOutVersionInfo, BOOL bAllowOriginalAPIAsFallback = TRUE) + { + //Retrieve "real" OS version + //INFO: It uses kernel-mode API that does not depend on the manifest included in the executable! + //'pOutVersionInfo' = struct to fill in, may point to OSVERSIONINFO or OSVERSIONINFOEX structs + // INFO: Make sure to fill in its 'dwOSVersionInfoSize' to the struct size! + //'bAllowOriginalAPIAsFallback' = TRUE to use original "deprecated" API if this kernel-mode fails, FALSE - not to + //RETURN: + // = TRUE if success + // = FALSE if error (check GetLastError() for info) + BOOL bRes = FALSE; + int nOSError = NO_ERROR; + + if(pOutVersionInfo) + { + //Get info + RTL_OSVERSIONINFOEXW ver = {0}; + if(__rtl_get_version(ver)) + { + //Check struct size + if(pOutVersionInfo->dwOSVersionInfoSize == sizeof(OSVERSIONINFO)) + { + //Smaller struct + pOutVersionInfo->dwMajorVersion = ver.dwMajorVersion; + pOutVersionInfo->dwMinorVersion = ver.dwMinorVersion; + pOutVersionInfo->dwBuildNumber = ver.dwBuildNumber; + pOutVersionInfo->dwPlatformId = ver.dwPlatformId; + + if(TXT_SIZEOF(pOutVersionInfo->szCSDVersion) == TXT_SIZEOF(ver.szCSDVersion)) + { +#ifdef UNICODE + //Unicode version + memcpy(pOutVersionInfo->szCSDVersion, ver.szCSDVersion, sizeof(ver.szCSDVersion)); +#else + //ANSI version + ver.szCSDVersion[(sizeof(ver.szCSDVersion) / sizeof(ver.szCSDVersion[0])) - 1] = 0; + + int nLn = lstrlenW(ver.szCSDVersion); + if(nLn > TXT_SIZEOF(pOutVersionInfo->szCSDVersion)) + nLn = TXT_SIZEOF(pOutVersionInfo->szCSDVersion); + + nLn++; + + CHAR* pTgt = pOutVersionInfo->szCSDVersion; + pTgt[0] = 0; + ::WideCharToMultiByte(CP_ACP, 0, ver.szCSDVersion, nLn, pTgt, nLn, NULL, NULL); + pTgt[(sizeof(pOutVersionInfo->szCSDVersion) / sizeof(pOutVersionInfo->szCSDVersion[0])) - 1] = 0; +#endif + + //Done + bRes = TRUE; + } + else + nOSError = ERROR_INVALID_BLOCK_LENGTH; + } + else if(pOutVersionInfo->dwOSVersionInfoSize == sizeof(OSVERSIONINFOEX)) + { + //Larger struct + ((OSVERSIONINFOEX*)pOutVersionInfo)->dwMajorVersion = ver.dwMajorVersion; + ((OSVERSIONINFOEX*)pOutVersionInfo)->dwMinorVersion = ver.dwMinorVersion; + ((OSVERSIONINFOEX*)pOutVersionInfo)->dwBuildNumber = ver.dwBuildNumber; + ((OSVERSIONINFOEX*)pOutVersionInfo)->dwPlatformId = ver.dwPlatformId; + ((OSVERSIONINFOEX*)pOutVersionInfo)->wServicePackMajor = ver.wServicePackMajor; + ((OSVERSIONINFOEX*)pOutVersionInfo)->wServicePackMinor = ver.wServicePackMinor; + ((OSVERSIONINFOEX*)pOutVersionInfo)->wSuiteMask = ver.wSuiteMask; + ((OSVERSIONINFOEX*)pOutVersionInfo)->wProductType = ver.wProductType; + ((OSVERSIONINFOEX*)pOutVersionInfo)->wReserved = ver.wReserved; + + if(TXT_SIZEOF(((OSVERSIONINFOEX*)pOutVersionInfo)->szCSDVersion) == TXT_SIZEOF(ver.szCSDVersion)) + { +#ifdef UNICODE + //Unicode version + memcpy(((OSVERSIONINFOEX*)pOutVersionInfo)->szCSDVersion, ver.szCSDVersion, sizeof(ver.szCSDVersion)); +#else + //ANSI version + ver.szCSDVersion[(sizeof(ver.szCSDVersion) / sizeof(ver.szCSDVersion[0])) - 1] = 0; + + int nLn = lstrlenW(ver.szCSDVersion); + if(nLn > TXT_SIZEOF(((OSVERSIONINFOEX*)pOutVersionInfo)->szCSDVersion)) + nLn = TXT_SIZEOF(((OSVERSIONINFOEX*)pOutVersionInfo)->szCSDVersion); + + nLn++; + + CHAR* pTgt = ((OSVERSIONINFOEX*)pOutVersionInfo)->szCSDVersion; + pTgt[0] = 0; + ::WideCharToMultiByte(CP_ACP, 0, ver.szCSDVersion, nLn, pTgt, nLn, NULL, NULL); + pTgt[(sizeof(((OSVERSIONINFOEX*)pOutVersionInfo)->szCSDVersion) / sizeof(((OSVERSIONINFOEX*)pOutVersionInfo)->szCSDVersion[0])) - 1] = 0; +#endif + + //Done + bRes = TRUE; + } + else + nOSError = ERROR_INVALID_BLOCK_LENGTH; + } + else + nOSError = ERROR_INSUFFICIENT_BUFFER; + } + else + nOSError = ::GetLastError(); + + //Did we fail? + if(!bRes && + bAllowOriginalAPIAsFallback) + { + //Use original API as a fall-back + bRes = ::GetVersionEx(pOutVersionInfo); + nOSError = ::GetLastError(); + } + } + else + nOSError = ERROR_INVALID_PARAMETER; + + ::SetLastError(nOSError); + return bRes; + } + + static DWORD ConvertNtStatusToWin32Error(LONG ntstatus) + { + //For more info on error codes check: + // http://msdn.microsoft.com/en-us/library/cc704588.aspx + // http://support.microsoft.com/kb/113996 + // and + // http://www.cprogramming.com/snippets/source-code/convert-ntstatus-win32-error + //RETURN: + // = GetLastError() type error for 'ntstatus', or + // = unaltered 'ntstatus' as the last resort + DWORD result; + DWORD br = 0; + OVERLAPPED o = {0}; + + o.Internal = ntstatus; + + ::GetOverlappedResult(NULL, &o, &br, FALSE); + result = GetLastError(); + + if(result == NO_ERROR) + { + //Failed + result = ntstatus; + } + + ::SetLastError(result); + return result; + } + + +private: + static BOOL __rtl_get_version(RTL_OSVERSIONINFOEXW& verOut) + { + //Try to use kernel-mode API that doesn't "lie" about the results + BOOL bRes = FALSE; + int nOSError = NO_ERROR; + + __try + { + LONG (WINAPI *pfnRtlGetVersion)(RTL_OSVERSIONINFOEXW*); + (FARPROC&)pfnRtlGetVersion = GetProcAddress(GetModuleHandle(_T("ntdll.dll")), "RtlGetVersion"); + if(pfnRtlGetVersion) + { + verOut.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW); + + LONG status = pfnRtlGetVersion(&verOut); + if(status == 0) //STATUS_SUCCESS + { + bRes = TRUE; + } + else + { + //Convert to Win32 error code + nOSError = ConvertNtStatusToWin32Error(status); + } + } + else + nOSError = ERROR_NOT_SUPPORTED; + } + __except(1) + { + //Failed + bRes = FALSE; + nOSError = ERROR_ARENA_TRASHED; + } + + ::SetLastError(nOSError); + return bRes; + } +}; + + + + diff --git a/ShutdownWithUpdates/ShutdownWithUpdates/stdafx.cpp b/ShutdownWithUpdates/ShutdownWithUpdates/stdafx.cpp new file mode 100644 index 0000000..a62437a --- /dev/null +++ b/ShutdownWithUpdates/ShutdownWithUpdates/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// ShutdownWithUpdates.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/ShutdownWithUpdates/ShutdownWithUpdates/stdafx.h b/ShutdownWithUpdates/ShutdownWithUpdates/stdafx.h new file mode 100644 index 0000000..73be216 --- /dev/null +++ b/ShutdownWithUpdates/ShutdownWithUpdates/stdafx.h @@ -0,0 +1,46 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +#include +#include + + + +// TODO: reference additional headers your program requires here + +#include +#include + +#include + +#include + + +#include "AltStreams.h" //Needed for CheckForXP_SP2_FileBlockAndRemoveIt() +#include "ntddstor.h" //Needed for CheckForXP_SP2_FileBlockAndRemoveIt() + +#include "rtlVersion.h" + + +#define PROG_VER L"1.2.1.0" //Program version + + + +#define SIZEOF(f) (sizeof(f)/sizeof(f[0])) + +#ifndef ASSERT +#ifdef _DEBUG +#define ASSERT(f) if(!(f)){char ___bf029[1024]; ::StringCchPrintfA(___bf029, 1024, "Assertion Failed:\n\nFile: %s\nLine: %d", __FILE__, __LINE__); ::MessageBoxA(NULL, ___bf029, "** ASSERTION **", MB_ICONERROR | MB_OK);} +#else +#define ASSERT(f) +#endif +#endif + + + diff --git a/ShutdownWithUpdates/ShutdownWithUpdates/targetver.h b/ShutdownWithUpdates/ShutdownWithUpdates/targetver.h new file mode 100644 index 0000000..a38195a --- /dev/null +++ b/ShutdownWithUpdates/ShutdownWithUpdates/targetver.h @@ -0,0 +1,13 @@ +#pragma once + +// The following macros define the minimum required platform. The minimum required platform +// is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run +// your application. The macros work by enabling all features available on platform versions up to and +// including the version specified. + +// Modify the following defines if you have to target a platform prior to the ones specified below. +// Refer to MSDN for the latest info on corresponding values for different platforms. +#ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows Vista. +#define _WIN32_WINNT 0x0600 // Change this to the appropriate value to target other versions of Windows. +#endif +