15#define WIN32_LEAN_AND_MEAN
23template <
typename IntType> std::string
Num2Hex(IntType
n)
25 std::stringstream sMsg;
26 sMsg <<
"0x" << std::uppercase << std::setfill(
'0') << std::setw(
sizeof(
n) * 2) << std::hex
31template <
typename IntType> std::string Num2Dec(IntType
n)
33 std::stringstream sMsg;
38std::string Win32ErrorMessage(
const char* sFunc, DWORD nWin32Error)
40 std::stringstream sMsg;
41 sMsg << sFunc <<
" failed with Win32 error code " <<
Num2Hex(nWin32Error) <<
"!";
46void ThrowHResult(
const char* sFunc, HRESULT
hr)
48 std::stringstream sMsg;
49 sMsg << sFunc <<
" failed (HRESULT = " <<
Num2Hex(
hr) <<
")!";
51 throw std::exception(sMsg.str().c_str());
54void CheckHResult(
const char* sFunc, HRESULT
hr)
57 ThrowHResult(sFunc,
hr);
62 throw std::exception(Win32ErrorMessage(sFunc, nWin32Error).c_str());
67void CheckWin32Error(
const char* sFunc, DWORD nWin32Error)
69 if (nWin32Error != ERROR_SUCCESS)
73std::wstring GetKnownFolder(
const KNOWNFOLDERID& rfid)
75 PWSTR sPath =
nullptr;
76 HRESULT
hr = SHGetKnownFolderPath(rfid, KF_FLAG_DEFAULT,
nullptr, &sPath);
77 CheckHResult(
"SHGetKnownFolderPath",
hr);
78 std::wstring sResult(sPath);
83void WriteLogElem(MSIHANDLE hInst, MSIHANDLE hRecord, std::ostringstream& sTmpl, UINT)
85 MsiRecordSetStringA(hRecord, 0, sTmpl.str().c_str());
86 MsiProcessMessage(hInst, INSTALLMESSAGE_INFO, hRecord);
89void RecSetString(MSIHANDLE hRec, UINT nField, LPCSTR sVal)
91 MsiRecordSetStringA(hRec, nField, sVal);
94void RecSetString(MSIHANDLE hRec, UINT nField, LPCWSTR sVal)
96 MsiRecordSetStringW(hRec, nField, sVal);
99template <
class S1,
class... SOther>
100void WriteLogElem(MSIHANDLE hInst, MSIHANDLE hRec, std::ostringstream& sTmpl, UINT nField,
101 const S1& elem,
const SOther&... others);
103template <
class Ch,
class... SOther>
104void WriteLogElem(MSIHANDLE hInst, MSIHANDLE hRec, std::ostringstream& sTmpl, UINT nField,
105 const Ch* elem,
const SOther&... others)
107 sTmpl <<
" [" << nField <<
"]";
108 RecSetString(hRec, nField, elem);
109 WriteLogElem(hInst, hRec, sTmpl, nField + 1, others...);
112template <
class S1,
class... SOther>
113void WriteLogElem(MSIHANDLE hInst, MSIHANDLE hRec, std::ostringstream& sTmpl, UINT nField,
114 const S1& elem,
const SOther&... others)
116 WriteLogElem(hInst, hRec, sTmpl, nField, elem.c_str(), others...);
119std::string sLogPrefix;
121template <
class... StrType>
void WriteLog(MSIHANDLE hInst,
const StrType&... elements)
123 PMSIHANDLE hRec = MsiCreateRecord(
sizeof...(elements));
127 std::ostringstream sTemplate;
128 sTemplate << sLogPrefix;
129 WriteLogElem(hInst, hRec, sTemplate, 1, elements...);
133void ShowWarning(MSIHANDLE hInst,
const std::wstring& sKBNo,
const char*
sMessage)
137 PMSIHANDLE hRec = MsiCreateRecord(3);
139 MsiRecordSetInteger(hRec, 1, 25000);
140 MsiRecordSetStringW(hRec, 2, sKBNo.c_str());
143 MsiRecordSetStringA(hRec, 3, s.c_str());
144 MsiProcessMessage(hInst, INSTALLMESSAGE_WARNING, hRec);
148void SetStatusText(MSIHANDLE hInst,
const std::wstring& actName,
const std::wstring& actDesc)
150 PMSIHANDLE hRec = MsiCreateRecord(3);
153 MsiRecordSetStringW(hRec, 1, actName.c_str());
155 MsiRecordSetStringW(hRec, 2, actDesc.c_str());
157 MsiProcessMessage(hInst, INSTALLMESSAGE_ACTIONSTART, hRec);
160typedef std::unique_ptr<void,
decltype(&CloseHandle)> CloseHandleGuard;
161CloseHandleGuard Guard(HANDLE
h) {
return CloseHandleGuard(
h, CloseHandle); }
163typedef std::unique_ptr<
const wchar_t,
decltype(&DeleteFileW)> DeleteFileGuard;
164DeleteFileGuard Guard(
const wchar_t* sFileName) {
return DeleteFileGuard(sFileName, DeleteFileW); }
166typedef std::unique_ptr<SC_HANDLE__,
decltype(&CloseServiceHandle)> CloseServiceHandleGuard;
167CloseServiceHandleGuard Guard(SC_HANDLE
h)
169 return CloseServiceHandleGuard(
h, CloseServiceHandle);
172std::wstring GetTempFile()
175 DWORD nResult = GetTempPathW(
sizeof(sPath) /
sizeof(*sPath), sPath);
180 nResult = GetTempFileNameW(sPath, L
"TMP", 0, sFile);
190 typedef BOOL(WINAPI * LPFN_ISWOW64PROCESS)(HANDLE, PBOOL);
191 LPFN_ISWOW64PROCESS fnIsWow64Process =
reinterpret_cast<LPFN_ISWOW64PROCESS
>(
192 GetProcAddress(GetModuleHandleW(L
"kernel32"),
"IsWow64Process"));
194 if (fnIsWow64Process)
196 if (!fnIsWow64Process(GetCurrentProcess(), &bResult))
209class UserInputChecker
216 UserInputChecker(MSIHANDLE hInstall)
217 : m_hInstall(hInstall)
218 , m_hProgressRec(MsiCreateRecord(3))
221 MsiRecordSetInteger(m_hProgressRec, 1, 1);
222 MsiRecordSetInteger(m_hProgressRec, 2, 1);
223 MsiRecordSetInteger(m_hProgressRec, 3, 0);
224 int nResult = MsiProcessMessage(m_hInstall, INSTALLMESSAGE_PROGRESS, m_hProgressRec);
225 if (nResult == IDCANCEL)
226 throw eUserCancelled();
228 MsiRecordSetInteger(m_hProgressRec, 1, 2);
229 MsiRecordSetInteger(m_hProgressRec, 2, 0);
230 MsiRecordSetInteger(m_hProgressRec, 3, 0);
233 void ThrowIfUserCancelled()
236 int nResult = MsiProcessMessage(m_hInstall, INSTALLMESSAGE_PROGRESS, m_hProgressRec);
237 if (nResult == IDCANCEL)
238 throw eUserCancelled();
242 MSIHANDLE m_hInstall;
243 PMSIHANDLE m_hProgressRec;
249class WUServiceEnabler
252 WUServiceEnabler(MSIHANDLE hInstall)
253 : mhInstall(hInstall)
254 , mhService(EnableWUService(hInstall))
264 EnsureServiceEnabled(mhInstall, mhService.get(),
false);
268 catch (std::exception& e)
270 WriteLog(mhInstall, e.what());
275 static CloseServiceHandleGuard EnableWUService(MSIHANDLE hInstall)
277 auto hSCM = Guard(OpenSCManagerW(
nullptr,
nullptr, SC_MANAGER_ALL_ACCESS));
280 WriteLog(hInstall,
"Opened service control manager");
282 auto hService = Guard(OpenServiceW(hSCM.get(), L
"wuauserv",
283 SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG
284 | SERVICE_QUERY_STATUS | SERVICE_STOP));
287 WriteLog(hInstall,
"Obtained WU service handle");
289 const DWORD nCurrentStatus = ServiceStatus(hInstall, hService.get());
294 if (nCurrentStatus == SERVICE_RUNNING)
297 if (nCurrentStatus == SERVICE_RUNNING
298 || !EnsureServiceEnabled(hInstall, hService.get(),
true))
302 WriteLog(hInstall,
"Service configuration is unchanged");
309 static bool EnsureServiceEnabled(MSIHANDLE hInstall, SC_HANDLE hService,
bool bEnabled)
311 bool bConfigChanged =
false;
313 DWORD nCbRequired = 0;
314 if (!QueryServiceConfigW(hService,
nullptr, 0, &nCbRequired))
316 if (DWORD nError = GetLastError(); nError != ERROR_INSUFFICIENT_BUFFER)
319 std::unique_ptr<char[]> pBuf(
new char[nCbRequired]);
320 LPQUERY_SERVICE_CONFIGW pConfig =
reinterpret_cast<LPQUERY_SERVICE_CONFIGW
>(pBuf.get());
321 if (!QueryServiceConfigW(hService, pConfig, nCbRequired, &nCbRequired))
323 WriteLog(hInstall,
"Obtained service config");
325 DWORD eNewStartType = 0;
326 if (bEnabled && pConfig->dwStartType == SERVICE_DISABLED)
328 bConfigChanged =
true;
329 eNewStartType = SERVICE_DEMAND_START;
330 WriteLog(hInstall,
"Service is disabled, and requested to enable");
332 else if (!bEnabled && pConfig->dwStartType != SERVICE_DISABLED)
334 bConfigChanged =
true;
335 eNewStartType = SERVICE_DISABLED;
336 WriteLog(hInstall,
"Service is enabled, and requested to disable");
341 if (!ChangeServiceConfigW(hService, SERVICE_NO_CHANGE, eNewStartType, SERVICE_NO_CHANGE,
342 nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
345 WriteLog(hInstall,
"WU service config successfully changed");
348 WriteLog(hInstall,
"No need to modify service config");
350 return bConfigChanged;
353 static DWORD ServiceStatus(MSIHANDLE hInstall, SC_HANDLE hService)
355 SERVICE_STATUS aServiceStatus{};
356 if (!QueryServiceStatus(hService, &aServiceStatus))
360 switch (aServiceStatus.dwCurrentState)
362 case SERVICE_STOPPED:
363 sStatus =
"SERVICE_STOPPED";
365 case SERVICE_START_PENDING:
366 sStatus =
"SERVICE_START_PENDING";
368 case SERVICE_STOP_PENDING:
369 sStatus =
"SERVICE_STOP_PENDING";
371 case SERVICE_RUNNING:
372 sStatus =
"SERVICE_RUNNING";
374 case SERVICE_CONTINUE_PENDING:
375 sStatus =
"SERVICE_CONTINUE_PENDING";
377 case SERVICE_PAUSE_PENDING:
378 sStatus =
"SERVICE_PAUSE_PENDING";
381 sStatus =
"SERVICE_PAUSED";
384 sStatus =
Num2Hex(aServiceStatus.dwCurrentState);
386 WriteLog(hInstall,
"Service status is", sStatus);
388 return aServiceStatus.dwCurrentState;
391 static void StopService(MSIHANDLE hInstall, SC_HANDLE hService,
bool bWait)
395 if (ServiceStatus(hInstall, hService) != SERVICE_STOPPED)
397 SERVICE_STATUS aServiceStatus{};
398 if (!ControlService(hService, SERVICE_CONTROL_STOP, &aServiceStatus))
401 "Successfully sent SERVICE_CONTROL_STOP code to Windows Update service");
402 if (aServiceStatus.dwCurrentState != SERVICE_STOPPED && bWait)
405 UserInputChecker aInputChecker(hInstall);
408 for (
int nWait = 0; nWait < 30; ++nWait)
410 for (
int i = 0;
i < 2; ++
i)
413 aInputChecker.ThrowIfUserCancelled();
416 if (!QueryServiceStatus(hService, &aServiceStatus))
419 if (aServiceStatus.dwCurrentState == SERVICE_STOPPED)
423 if (aServiceStatus.dwCurrentState == SERVICE_STOPPED)
424 WriteLog(hInstall,
"Successfully stopped Windows Update service");
426 WriteLog(hInstall,
"Wait for Windows Update stop timed out - proceeding");
429 WriteLog(hInstall,
"Windows Update service is not running");
431 catch (std::exception& e)
433 WriteLog(hInstall, e.what());
438 CloseServiceHandleGuard mhService;
446extern "C" __declspec(dllexport) UINT __stdcall UnpackMSUForInstall(MSIHANDLE hInstall)
450 sLogPrefix =
"UnpackMSUForInstall:";
451 WriteLog(hInstall,
"started");
453 WriteLog(hInstall,
"Checking value of InstMSUBinary");
454 wchar_t sInstMSUBinary[
MAX_PATH + 10];
455 DWORD nCCh =
sizeof(sInstMSUBinary) /
sizeof(*sInstMSUBinary);
456 CheckWin32Error(
"MsiGetPropertyW",
457 MsiGetPropertyW(hInstall, L
"InstMSUBinary", sInstMSUBinary, &nCCh));
458 WriteLog(hInstall,
"Got InstMSUBinary value:",
460 const wchar_t* sBinaryName = wcschr(sInstMSUBinary, L
'|');
462 throw std::exception(
"No KB number in InstMSUBinary!");
464 const std::wstring sKBNo(sInstMSUBinary, sBinaryName - sInstMSUBinary);
467 PMSIHANDLE hDatabase = MsiGetActiveDatabase(hInstall);
470 WriteLog(hInstall,
"MsiGetActiveDatabase succeeded");
472 std::wstringstream sQuery;
473 sQuery <<
"SELECT `Data` FROM `Binary` WHERE `Name`='" << sBinaryName <<
"'";
475 PMSIHANDLE hBinaryView;
476 CheckWin32Error(
"MsiDatabaseOpenViewW",
477 MsiDatabaseOpenViewW(hDatabase, sQuery.str().c_str(), &hBinaryView));
478 WriteLog(hInstall,
"MsiDatabaseOpenViewW succeeded");
480 CheckWin32Error(
"MsiViewExecute", MsiViewExecute(hBinaryView, 0));
481 WriteLog(hInstall,
"MsiViewExecute succeeded");
483 PMSIHANDLE hBinaryRecord;
484 CheckWin32Error(
"MsiViewFetch", MsiViewFetch(hBinaryView, &hBinaryRecord));
485 WriteLog(hInstall,
"MsiViewFetch succeeded");
487 const std::wstring sBinary = GetTempFile();
488 auto aDeleteFileGuard(Guard(sBinary.c_str()));
489 WriteLog(hInstall,
"Temp file path:", sBinary.c_str());
491 CheckWin32Error(
"MsiSetPropertyW",
492 MsiSetPropertyW(hInstall, L
"cleanup_msu", sBinary.c_str()));
495 HANDLE hFile = CreateFileW(sBinary.c_str(), GENERIC_WRITE, 0,
nullptr, CREATE_ALWAYS,
496 FILE_ATTRIBUTE_NORMAL,
nullptr);
497 if (hFile == INVALID_HANDLE_VALUE)
499 auto aFileHandleGuard(Guard(hFile));
501 const DWORD nBufSize = 1024 * 1024;
502 std::unique_ptr<char[]> buf(
new char[nBufSize]);
508 CheckWin32Error(
"MsiRecordReadStream",
509 MsiRecordReadStream(hBinaryRecord, 1, buf.get(), &nRead));
514 if (!WriteFile(hFile, buf.get(), nRead, &nWritten,
nullptr))
518 }
while (nRead == nBufSize);
520 WriteLog(hInstall,
"Successfully wrote", Num2Dec(nTotal),
"bytes");
522 const std::wstring s_inst_msu = sKBNo + L
"|" + sBinary;
523 CheckWin32Error(
"MsiSetPropertyW",
524 MsiSetPropertyW(hInstall, L
"inst_msu", s_inst_msu.c_str()));
527 (void)aDeleteFileGuard.release();
528 return ERROR_SUCCESS;
530 catch (std::exception& e)
532 WriteLog(hInstall, e.what());
534 return ERROR_INSTALL_FAILURE;
539extern "C" __declspec(dllexport) UINT __stdcall InstallMSU(MSIHANDLE hInstall)
544 sLogPrefix =
"InstallMSU:";
545 WriteLog(hInstall,
"started");
547 WriteLog(hInstall,
"Checking value of CustomActionData");
548 wchar_t sCustomActionData[
MAX_PATH + 10];
549 DWORD nCCh =
sizeof(sCustomActionData) /
sizeof(*sCustomActionData);
550 CheckWin32Error(
"MsiGetPropertyW",
551 MsiGetPropertyW(hInstall, L
"CustomActionData", sCustomActionData, &nCCh));
552 WriteLog(hInstall,
"Got CustomActionData value:", sCustomActionData);
553 const wchar_t* sBinaryName = wcschr(sCustomActionData, L
'|');
555 throw std::exception(
"No KB number in CustomActionData!");
556 sKBNo = std::wstring(sCustomActionData, sBinaryName - sCustomActionData);
558 auto aDeleteFileGuard(Guard(sBinaryName));
560 SetStatusText(hInstall, L
"WU service state check",
561 L
"Checking Windows Update service state");
565 WUServiceEnabler aWUServiceEnabler(hInstall);
567 SetStatusText(hInstall, sKBNo + L
" installation", L
"Installing " + sKBNo);
569 const bool bWow64Process = IsWow64Process();
570 WriteLog(hInstall,
"Is Wow64 Process:", bWow64Process ?
"YES" :
"NO");
571 std::wstring sWUSAPath = bWow64Process ? GetKnownFolder(FOLDERID_Windows) + L
"\\SysNative"
572 : GetKnownFolder(FOLDERID_System);
573 sWUSAPath += L
"\\wusa.exe";
574 WriteLog(hInstall,
"Prepared wusa path:", sWUSAPath);
576 std::wstring sWUSACmd
577 = L
"\"" + sWUSAPath + L
"\" \"" + sBinaryName + L
"\" /quiet /norestart";
578 WriteLog(hInstall,
"Prepared wusa command:", sWUSACmd);
582 PROCESS_INFORMATION pi{};
583 if (!CreateProcessW(sWUSAPath.c_str(),
const_cast<LPWSTR
>(sWUSACmd.c_str()),
nullptr,
584 nullptr, FALSE, CREATE_NO_WINDOW,
nullptr,
nullptr, &si, &pi))
586 CloseHandle(pi.hThread);
587 auto aCloseProcHandleGuard(Guard(pi.hProcess));
588 WriteLog(hInstall,
"CreateProcessW succeeded");
594 UserInputChecker aInputChecker(hInstall);
597 DWORD nWaitResult = WaitForSingleObject(pi.hProcess, 500);
598 if (nWaitResult == WAIT_OBJECT_0)
600 else if (nWaitResult == WAIT_TIMEOUT)
601 aInputChecker.ThrowIfUserCancelled();
608 if (!GetExitCodeProcess(pi.hProcess, &nExitCode))
611 HRESULT
hr =
static_cast<HRESULT
>(nExitCode);
615 if (HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED) ==
hr)
617 hr = ERROR_SUCCESS_REBOOT_REQUIRED;
623 case WU_S_ALREADY_INSTALLED:
624 case WU_E_NOT_APPLICABLE:
625 case ERROR_SUCCESS_REBOOT_REQUIRED:
626 case WU_S_REBOOT_REQUIRED:
627 WriteLog(hInstall,
"wusa.exe succeeded with exit code",
Num2Hex(nExitCode));
628 return ERROR_SUCCESS;
631 ThrowHResult(
"Execution of wusa.exe", hr);
634 catch (
const UserInputChecker::eUserCancelled&)
636 return ERROR_INSTALL_USEREXIT;
638 catch (std::exception& e)
640 WriteLog(hInstall, e.what());
641 ShowWarning(hInstall, sKBNo, e.what());
643 return ERROR_SUCCESS;
649extern "C" __declspec(dllexport) UINT __stdcall CleanupMSU(MSIHANDLE hInstall)
653 sLogPrefix =
"CleanupMSU:";
654 WriteLog(hInstall,
"started");
656 WriteLog(hInstall,
"Checking value of CustomActionData");
658 DWORD nCCh =
sizeof(sBinaryName) /
sizeof(*sBinaryName);
659 CheckWin32Error(
"MsiGetPropertyW",
660 MsiGetPropertyW(hInstall, L
"CustomActionData", sBinaryName, &nCCh));
661 WriteLog(hInstall,
"Got CustomActionData value:", sBinaryName);
663 if (!DeleteFileW(sBinaryName))
665 if (DWORD nError = GetLastError(); nError != ERROR_FILE_NOT_FOUND)
668 WriteLog(hInstall,
"File successfully removed");
670 catch (std::exception& e)
672 WriteLog(hInstall, e.what());
675 return ERROR_SUCCESS;
__declspec(dllexport) UINT __stdcall UnpackMSUForInstall(MSIHANDLE hInstall)
const wchar_t *typedef BOOL
std::string Num2Hex(IntType n)
void ThrowLastError(const char *sFunc)
void ThrowWin32Error(const char *sFunc, DWORD nWin32Error)