nss: upgrade to release 3.73
[LibreOffice.git] / setup_native / source / win32 / customactions / inst_msu / inst_msu.cxx
blob5dc47993a132b7d6556b6ee94c5e782f76722797
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include <memory>
11 #include <string>
12 #include <sstream>
13 #include <iomanip>
15 #define WIN32_LEAN_AND_MEAN
16 #include <windows.h>
17 #include <Shlobj.h>
18 #include <Wuerror.h>
19 #include <msiquery.h>
21 namespace
23 template <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
27 << n;
28 return sMsg.str();
31 template <typename IntType> std::string Num2Dec(IntType n)
33 std::stringstream sMsg;
34 sMsg << n;
35 return sMsg.str();
38 std::string Win32ErrorMessage(const char* sFunc, DWORD nWin32Error)
40 std::stringstream sMsg;
41 sMsg << sFunc << " failed with Win32 error code " << Num2Hex(nWin32Error) << "!";
43 return sMsg.str();
46 void 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());
54 void CheckHResult(const char* sFunc, HRESULT hr)
56 if (FAILED(hr))
57 ThrowHResult(sFunc, hr);
60 void ThrowWin32Error(const char* sFunc, DWORD nWin32Error)
62 throw std::exception(Win32ErrorMessage(sFunc, nWin32Error).c_str());
65 void ThrowLastError(const char* sFunc) { ThrowWin32Error(sFunc, GetLastError()); }
67 void CheckWin32Error(const char* sFunc, DWORD nWin32Error)
69 if (nWin32Error != ERROR_SUCCESS)
70 ThrowWin32Error(sFunc, nWin32Error);
73 std::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);
79 CoTaskMemFree(sPath);
80 return sResult;
83 void WriteLogElem(MSIHANDLE hInst, MSIHANDLE hRecord, std::ostringstream& sTmpl, UINT)
85 MsiRecordSetStringA(hRecord, 0, sTmpl.str().c_str());
86 MsiProcessMessage(hInst, INSTALLMESSAGE_INFO, hRecord);
89 void RecSetString(MSIHANDLE hRec, UINT nField, LPCSTR sVal)
91 MsiRecordSetStringA(hRec, nField, sVal);
94 void RecSetString(MSIHANDLE hRec, UINT nField, LPCWSTR sVal)
96 MsiRecordSetStringW(hRec, nField, sVal);
99 template <class Ch, class... SOther>
100 void WriteLogElem(MSIHANDLE hInst, MSIHANDLE hRec, std::ostringstream& sTmpl, UINT nField,
101 const Ch* elem, const SOther&... others)
103 sTmpl << " [" << nField << "]";
104 RecSetString(hRec, nField, elem);
105 WriteLogElem(hInst, hRec, sTmpl, nField + 1, others...);
108 template <class S1, class... SOther>
109 void WriteLogElem(MSIHANDLE hInst, MSIHANDLE hRec, std::ostringstream& sTmpl, UINT nField,
110 const S1& elem, const SOther&... others)
112 WriteLogElem(hInst, hRec, sTmpl, nField, elem.c_str(), others...);
115 std::string sLogPrefix;
117 template <class... StrType> void WriteLog(MSIHANDLE hInst, const StrType&... elements)
119 PMSIHANDLE hRec = MsiCreateRecord(sizeof...(elements));
120 if (!hRec)
121 return;
123 std::ostringstream sTemplate;
124 sTemplate << sLogPrefix;
125 WriteLogElem(hInst, hRec, sTemplate, 1, elements...);
128 // Show a warning message box. This will be automatically suppressed in unattended installation.
129 void ShowWarning(MSIHANDLE hInst, const std::wstring& sKBNo, const char* sMessage)
131 // Error table's message #25000: "Installing a pre-requisite [2] failed.
132 // You might need to manually install it from Microsoft site to be able to run the product.[3]"
133 PMSIHANDLE hRec = MsiCreateRecord(3);
134 // To show a message from Error table, record's Field 0 must be null
135 MsiRecordSetInteger(hRec, 1, 25000);
136 MsiRecordSetStringW(hRec, 2, sKBNo.c_str());
137 std::string s("\n");
138 s += sMessage;
139 MsiRecordSetStringA(hRec, 3, s.c_str());
140 MsiProcessMessage(hInst, INSTALLMESSAGE_WARNING, hRec);
143 // Set custom action description visible in progress dialog
144 void SetStatusText(MSIHANDLE hInst, const std::wstring& actName, const std::wstring& actDesc)
146 PMSIHANDLE hRec = MsiCreateRecord(3);
147 // For INSTALLMESSAGE_ACTIONSTART, record's Field 0 must be null
148 // Field 1: Action name - must be non-null
149 MsiRecordSetStringW(hRec, 1, actName.c_str());
150 // Field 2: Action description - displayed in dialog
151 MsiRecordSetStringW(hRec, 2, actDesc.c_str());
152 // Let Field 3 stay null - no action template
153 MsiProcessMessage(hInst, INSTALLMESSAGE_ACTIONSTART, hRec);
156 typedef std::unique_ptr<void, decltype(&CloseHandle)> CloseHandleGuard;
157 CloseHandleGuard Guard(HANDLE h) { return CloseHandleGuard(h, CloseHandle); }
159 typedef std::unique_ptr<const wchar_t, decltype(&DeleteFileW)> DeleteFileGuard;
160 DeleteFileGuard Guard(const wchar_t* sFileName) { return DeleteFileGuard(sFileName, DeleteFileW); }
162 typedef std::unique_ptr<SC_HANDLE__, decltype(&CloseServiceHandle)> CloseServiceHandleGuard;
163 CloseServiceHandleGuard Guard(SC_HANDLE h)
165 return CloseServiceHandleGuard(h, CloseServiceHandle);
168 std::wstring GetTempFile()
170 wchar_t sPath[MAX_PATH + 1];
171 DWORD nResult = GetTempPathW(sizeof(sPath) / sizeof(*sPath), sPath);
172 if (!nResult)
173 ThrowLastError("GetTempPathW");
175 wchar_t sFile[MAX_PATH + 1];
176 nResult = GetTempFileNameW(sPath, L"TMP", 0, sFile);
177 if (!nResult)
178 ThrowLastError("GetTempFileNameW");
179 return sFile;
182 bool IsWow64Process()
184 #if !defined _WIN64
185 BOOL bResult = FALSE;
186 typedef BOOL(WINAPI * LPFN_ISWOW64PROCESS)(HANDLE, PBOOL);
187 LPFN_ISWOW64PROCESS fnIsWow64Process = reinterpret_cast<LPFN_ISWOW64PROCESS>(
188 GetProcAddress(GetModuleHandleW(L"kernel32"), "IsWow64Process"));
190 if (fnIsWow64Process)
192 if (!fnIsWow64Process(GetCurrentProcess(), &bResult))
193 ThrowLastError("IsWow64Process");
196 return bResult;
197 #else
198 return false;
199 #endif
202 // This class uses MsiProcessMessage to check for user input: it returns IDCANCEL when user cancels
203 // installation. It throws a special exception, to be intercepted in main action function to return
204 // corresponding exit code.
205 class UserInputChecker
207 public:
208 class eUserCancelled
212 UserInputChecker(MSIHANDLE hInstall)
213 : m_hInstall(hInstall)
214 , m_hProgressRec(MsiCreateRecord(3))
216 // Use explicit progress messages
217 MsiRecordSetInteger(m_hProgressRec, 1, 1);
218 MsiRecordSetInteger(m_hProgressRec, 2, 1);
219 MsiRecordSetInteger(m_hProgressRec, 3, 0);
220 int nResult = MsiProcessMessage(m_hInstall, INSTALLMESSAGE_PROGRESS, m_hProgressRec);
221 if (nResult == IDCANCEL)
222 throw eUserCancelled();
223 // Prepare the record to following progress update calls
224 MsiRecordSetInteger(m_hProgressRec, 1, 2);
225 MsiRecordSetInteger(m_hProgressRec, 2, 0); // step by 0 - don't move progress
226 MsiRecordSetInteger(m_hProgressRec, 3, 0);
229 void ThrowIfUserCancelled()
231 // Check if user has cancelled
232 int nResult = MsiProcessMessage(m_hInstall, INSTALLMESSAGE_PROGRESS, m_hProgressRec);
233 if (nResult == IDCANCEL)
234 throw eUserCancelled();
237 private:
238 MSIHANDLE m_hInstall;
239 PMSIHANDLE m_hProgressRec;
242 // Checks if Windows Update service is disabled, and if it is, enables it temporarily.
243 // Also stops the service if it's currently running, because it seems that wusa.exe
244 // does not freeze when it starts the service itself.
245 class WUServiceEnabler
247 public:
248 WUServiceEnabler(MSIHANDLE hInstall)
249 : mhInstall(hInstall)
250 , mhService(EnableWUService(hInstall))
254 ~WUServiceEnabler()
258 if (mhService)
260 EnsureServiceEnabled(mhInstall, mhService.get(), false);
261 StopService(mhInstall, mhService.get(), false);
264 catch (std::exception& e)
266 WriteLog(mhInstall, e.what());
270 private:
271 static CloseServiceHandleGuard EnableWUService(MSIHANDLE hInstall)
273 auto hSCM = Guard(OpenSCManagerW(nullptr, nullptr, SC_MANAGER_ALL_ACCESS));
274 if (!hSCM)
275 ThrowLastError("OpenSCManagerW");
276 WriteLog(hInstall, "Opened service control manager");
278 auto hService = Guard(OpenServiceW(hSCM.get(), L"wuauserv",
279 SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG
280 | SERVICE_QUERY_STATUS | SERVICE_STOP));
281 if (!hService)
282 ThrowLastError("OpenServiceW");
283 WriteLog(hInstall, "Obtained WU service handle");
285 const DWORD nCurrentStatus = ServiceStatus(hInstall, hService.get());
286 // Stop currently running service to prevent wusa.exe from hanging trying to detect if the
287 // update is applicable (sometimes this freezes it ~indefinitely; it seems that it doesn't
288 // happen if wusa.exe starts the service itself: https://superuser.com/questions/1044528/).
289 // tdf#124794: Wait for service to stop.
290 if (nCurrentStatus == SERVICE_RUNNING)
291 StopService(hInstall, hService.get(), true);
293 if (nCurrentStatus == SERVICE_RUNNING
294 || !EnsureServiceEnabled(hInstall, hService.get(), true))
296 // No need to restore anything back, since we didn't change config
297 hService.reset();
298 WriteLog(hInstall, "Service configuration is unchanged");
301 return hService;
304 // Returns if the service configuration was actually changed
305 static bool EnsureServiceEnabled(MSIHANDLE hInstall, SC_HANDLE hService, bool bEnabled)
307 bool bConfigChanged = false;
309 DWORD nCbRequired = 0;
310 if (!QueryServiceConfigW(hService, nullptr, 0, &nCbRequired))
312 if (DWORD nError = GetLastError(); nError != ERROR_INSUFFICIENT_BUFFER)
313 ThrowWin32Error("QueryServiceConfigW", nError);
315 std::unique_ptr<char[]> pBuf(new char[nCbRequired]);
316 LPQUERY_SERVICE_CONFIGW pConfig = reinterpret_cast<LPQUERY_SERVICE_CONFIGW>(pBuf.get());
317 if (!QueryServiceConfigW(hService, pConfig, nCbRequired, &nCbRequired))
318 ThrowLastError("QueryServiceConfigW");
319 WriteLog(hInstall, "Obtained service config");
321 DWORD eNewStartType = 0;
322 if (bEnabled && pConfig->dwStartType == SERVICE_DISABLED)
324 bConfigChanged = true;
325 eNewStartType = SERVICE_DEMAND_START;
326 WriteLog(hInstall, "Service is disabled, and requested to enable");
328 else if (!bEnabled && pConfig->dwStartType != SERVICE_DISABLED)
330 bConfigChanged = true;
331 eNewStartType = SERVICE_DISABLED;
332 WriteLog(hInstall, "Service is enabled, and requested to disable");
335 if (bConfigChanged)
337 if (!ChangeServiceConfigW(hService, SERVICE_NO_CHANGE, eNewStartType, SERVICE_NO_CHANGE,
338 nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
339 nullptr))
340 ThrowLastError("ChangeServiceConfigW");
341 WriteLog(hInstall, "WU service config successfully changed");
343 else
344 WriteLog(hInstall, "No need to modify service config");
346 return bConfigChanged;
349 static DWORD ServiceStatus(MSIHANDLE hInstall, SC_HANDLE hService)
351 SERVICE_STATUS aServiceStatus{};
352 if (!QueryServiceStatus(hService, &aServiceStatus))
353 ThrowLastError("QueryServiceStatus");
355 std::string sStatus;
356 switch (aServiceStatus.dwCurrentState)
358 case SERVICE_STOPPED:
359 sStatus = "SERVICE_STOPPED";
360 break;
361 case SERVICE_START_PENDING:
362 sStatus = "SERVICE_START_PENDING";
363 break;
364 case SERVICE_STOP_PENDING:
365 sStatus = "SERVICE_STOP_PENDING";
366 break;
367 case SERVICE_RUNNING:
368 sStatus = "SERVICE_RUNNING";
369 break;
370 case SERVICE_CONTINUE_PENDING:
371 sStatus = "SERVICE_CONTINUE_PENDING";
372 break;
373 case SERVICE_PAUSE_PENDING:
374 sStatus = "SERVICE_PAUSE_PENDING";
375 break;
376 case SERVICE_PAUSED:
377 sStatus = "SERVICE_PAUSED";
378 break;
379 default:
380 sStatus = Num2Hex(aServiceStatus.dwCurrentState);
382 WriteLog(hInstall, "Service status is", sStatus);
384 return aServiceStatus.dwCurrentState;
387 static void StopService(MSIHANDLE hInstall, SC_HANDLE hService, bool bWait)
391 if (ServiceStatus(hInstall, hService) != SERVICE_STOPPED)
393 SERVICE_STATUS aServiceStatus{};
394 if (!ControlService(hService, SERVICE_CONTROL_STOP, &aServiceStatus))
395 ThrowLastError("ControlService");
396 WriteLog(hInstall,
397 "Successfully sent SERVICE_CONTROL_STOP code to Windows Update service");
398 if (aServiceStatus.dwCurrentState != SERVICE_STOPPED && bWait)
400 // Let user cancel too long wait
401 UserInputChecker aInputChecker(hInstall);
402 // aServiceStatus.dwWaitHint is unreasonably high for Windows Update (30000),
403 // so don't use it, but simply poll service status each second
404 for (int nWait = 0; nWait < 30; ++nWait) // arbitrary limit of 30 s
406 for (int i = 0; i < 2; ++i) // check user input twice a second
408 Sleep(500);
409 aInputChecker.ThrowIfUserCancelled();
412 if (!QueryServiceStatus(hService, &aServiceStatus))
413 ThrowLastError("QueryServiceStatus");
415 if (aServiceStatus.dwCurrentState == SERVICE_STOPPED)
416 break;
419 if (aServiceStatus.dwCurrentState == SERVICE_STOPPED)
420 WriteLog(hInstall, "Successfully stopped Windows Update service");
421 else if (bWait)
422 WriteLog(hInstall, "Wait for Windows Update stop timed out - proceeding");
424 else
425 WriteLog(hInstall, "Windows Update service is not running");
427 catch (std::exception& e)
429 WriteLog(hInstall, e.what());
433 MSIHANDLE mhInstall;
434 CloseServiceHandleGuard mhService;
438 // Immediate action "unpack_msu" that has access to installation database and properties; checks
439 // "InstMSUBinary" property and unpacks the binary with that name to a temporary file; sets
440 // "cleanup_msu" and "inst_msu" properties to the full name of the extracted temporary file. These
441 // properties will become "CustomActionData" property inside relevant deferred actions.
442 extern "C" __declspec(dllexport) UINT __stdcall UnpackMSUForInstall(MSIHANDLE hInstall)
446 sLogPrefix = "UnpackMSUForInstall:";
447 WriteLog(hInstall, "started");
449 WriteLog(hInstall, "Checking value of InstMSUBinary");
450 wchar_t sInstMSUBinary[MAX_PATH + 10];
451 DWORD nCCh = sizeof(sInstMSUBinary) / sizeof(*sInstMSUBinary);
452 CheckWin32Error("MsiGetPropertyW",
453 MsiGetPropertyW(hInstall, L"InstMSUBinary", sInstMSUBinary, &nCCh));
454 WriteLog(hInstall, "Got InstMSUBinary value:",
455 sInstMSUBinary); // KB2999226|Windows61-KB2999226-x64msu
456 const wchar_t* sBinaryName = wcschr(sInstMSUBinary, L'|');
457 if (!sBinaryName)
458 throw std::exception("No KB number in InstMSUBinary!");
459 // "KB2999226"
460 const std::wstring sKBNo(sInstMSUBinary, sBinaryName - sInstMSUBinary);
461 ++sBinaryName;
463 PMSIHANDLE hDatabase = MsiGetActiveDatabase(hInstall);
464 if (!hDatabase)
465 ThrowLastError("MsiGetActiveDatabase");
466 WriteLog(hInstall, "MsiGetActiveDatabase succeeded");
468 std::wstringstream sQuery;
469 sQuery << "SELECT `Data` FROM `Binary` WHERE `Name`='" << sBinaryName << "'";
471 PMSIHANDLE hBinaryView;
472 CheckWin32Error("MsiDatabaseOpenViewW",
473 MsiDatabaseOpenViewW(hDatabase, sQuery.str().c_str(), &hBinaryView));
474 WriteLog(hInstall, "MsiDatabaseOpenViewW succeeded");
476 CheckWin32Error("MsiViewExecute", MsiViewExecute(hBinaryView, 0));
477 WriteLog(hInstall, "MsiViewExecute succeeded");
479 PMSIHANDLE hBinaryRecord;
480 CheckWin32Error("MsiViewFetch", MsiViewFetch(hBinaryView, &hBinaryRecord));
481 WriteLog(hInstall, "MsiViewFetch succeeded");
483 const std::wstring sBinary = GetTempFile();
484 auto aDeleteFileGuard(Guard(sBinary.c_str()));
485 WriteLog(hInstall, "Temp file path:", sBinary.c_str());
487 CheckWin32Error("MsiSetPropertyW",
488 MsiSetPropertyW(hInstall, L"cleanup_msu", sBinary.c_str()));
491 HANDLE hFile = CreateFileW(sBinary.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS,
492 FILE_ATTRIBUTE_NORMAL, nullptr);
493 if (hFile == INVALID_HANDLE_VALUE)
494 ThrowLastError("CreateFileW");
495 auto aFileHandleGuard(Guard(hFile));
497 const DWORD nBufSize = 1024 * 1024;
498 std::unique_ptr<char[]> buf(new char[nBufSize]);
499 DWORD nTotal = 0;
500 DWORD nRead;
503 nRead = nBufSize;
504 CheckWin32Error("MsiRecordReadStream",
505 MsiRecordReadStream(hBinaryRecord, 1, buf.get(), &nRead));
507 if (nRead > 0)
509 DWORD nWritten;
510 if (!WriteFile(hFile, buf.get(), nRead, &nWritten, nullptr))
511 ThrowLastError("WriteFile");
512 nTotal += nWritten;
514 } while (nRead == nBufSize);
516 WriteLog(hInstall, "Successfully wrote", Num2Dec(nTotal), "bytes");
518 const std::wstring s_inst_msu = sKBNo + L"|" + sBinary;
519 CheckWin32Error("MsiSetPropertyW",
520 MsiSetPropertyW(hInstall, L"inst_msu", s_inst_msu.c_str()));
522 // Don't delete the file: it will be done by following actions (inst_msu or cleanup_msu)
523 (void)aDeleteFileGuard.release();
524 return ERROR_SUCCESS;
526 catch (std::exception& e)
528 WriteLog(hInstall, e.what());
530 return ERROR_INSTALL_FAILURE;
533 // Deferred action "inst_msu" that must be run from system account. Receives the tempfile name from
534 // "CustomActionData" property, and runs wusa.exe to install it. Waits for it and checks exit code.
535 extern "C" __declspec(dllexport) UINT __stdcall InstallMSU(MSIHANDLE hInstall)
537 std::wstring sKBNo; // "KB2999226"
540 sLogPrefix = "InstallMSU:";
541 WriteLog(hInstall, "started");
543 WriteLog(hInstall, "Checking value of CustomActionData");
544 wchar_t sCustomActionData[MAX_PATH + 10]; // "KB2999226|C:\Temp\binary.tmp"
545 DWORD nCCh = sizeof(sCustomActionData) / sizeof(*sCustomActionData);
546 CheckWin32Error("MsiGetPropertyW",
547 MsiGetPropertyW(hInstall, L"CustomActionData", sCustomActionData, &nCCh));
548 WriteLog(hInstall, "Got CustomActionData value:", sCustomActionData);
549 const wchar_t* sBinaryName = wcschr(sCustomActionData, L'|');
550 if (!sBinaryName)
551 throw std::exception("No KB number in CustomActionData!");
552 sKBNo = std::wstring(sCustomActionData, sBinaryName - sCustomActionData);
553 ++sBinaryName;
554 auto aDeleteFileGuard(Guard(sBinaryName));
556 SetStatusText(hInstall, L"WU service state check",
557 L"Checking Windows Update service state");
559 // In case the Windows Update service is disabled, we temporarily enable it here. We also
560 // stop running WU service, to avoid wusa.exe freeze (see comment in EnableWUService).
561 WUServiceEnabler aWUServiceEnabler(hInstall);
563 SetStatusText(hInstall, sKBNo + L" installation", L"Installing " + sKBNo);
565 const bool bWow64Process = IsWow64Process();
566 WriteLog(hInstall, "Is Wow64 Process:", bWow64Process ? "YES" : "NO");
567 std::wstring sWUSAPath = bWow64Process ? GetKnownFolder(FOLDERID_Windows) + L"\\SysNative"
568 : GetKnownFolder(FOLDERID_System);
569 sWUSAPath += L"\\wusa.exe";
570 WriteLog(hInstall, "Prepared wusa path:", sWUSAPath);
572 std::wstring sWUSACmd
573 = L"\"" + sWUSAPath + L"\" \"" + sBinaryName + L"\" /quiet /norestart";
574 WriteLog(hInstall, "Prepared wusa command:", sWUSACmd);
576 STARTUPINFOW si{};
577 si.cb = sizeof(si);
578 PROCESS_INFORMATION pi{};
579 if (!CreateProcessW(sWUSAPath.c_str(), const_cast<LPWSTR>(sWUSACmd.c_str()), nullptr,
580 nullptr, FALSE, CREATE_NO_WINDOW, nullptr, nullptr, &si, &pi))
581 ThrowLastError("CreateProcessW");
582 CloseHandle(pi.hThread);
583 auto aCloseProcHandleGuard(Guard(pi.hProcess));
584 WriteLog(hInstall, "CreateProcessW succeeded");
587 // This block waits when the started wusa.exe process finishes. Since it's possible
588 // for wusa.exe in some circumstances to wait really long (indefinitely?), we check
589 // for user input here.
590 UserInputChecker aInputChecker(hInstall);
591 for (;;)
593 DWORD nWaitResult = WaitForSingleObject(pi.hProcess, 500);
594 if (nWaitResult == WAIT_OBJECT_0)
595 break; // wusa.exe finished
596 else if (nWaitResult == WAIT_TIMEOUT)
597 aInputChecker.ThrowIfUserCancelled();
598 else
599 ThrowWin32Error("WaitForSingleObject", nWaitResult);
603 DWORD nExitCode = 0;
604 if (!GetExitCodeProcess(pi.hProcess, &nExitCode))
605 ThrowLastError("GetExitCodeProcess");
607 HRESULT hr = static_cast<HRESULT>(nExitCode);
609 // HRESULT_FROM_WIN32 is defined as an inline function in SDK 8.1 without the constexpr
610 // And it won't work to place it inside the switch statement.
611 if (HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED) == hr)
613 hr = ERROR_SUCCESS_REBOOT_REQUIRED;
616 switch (hr)
618 case S_OK:
619 case WU_S_ALREADY_INSTALLED:
620 case WU_E_NOT_APPLICABLE: // Windows could lie us about its version, etc.
621 case ERROR_SUCCESS_REBOOT_REQUIRED:
622 case WU_S_REBOOT_REQUIRED:
623 WriteLog(hInstall, "wusa.exe succeeded with exit code", Num2Hex(nExitCode));
624 return ERROR_SUCCESS;
626 default:
627 ThrowHResult("Execution of wusa.exe", hr);
630 catch (const UserInputChecker::eUserCancelled&)
632 return ERROR_INSTALL_USEREXIT;
634 catch (std::exception& e)
636 WriteLog(hInstall, e.what());
637 ShowWarning(hInstall, sKBNo, e.what());
639 return ERROR_SUCCESS; // Do not break on MSU installation errors
642 // Rollback deferred action "cleanup_msu" that is executed on error or cancel.
643 // It removes the temporary file created by UnpackMSUForInstall action.
644 // MUST be placed IMMEDIATELY AFTER "unpack_msu" in execute sequence.
645 extern "C" __declspec(dllexport) UINT __stdcall CleanupMSU(MSIHANDLE hInstall)
649 sLogPrefix = "CleanupMSU:";
650 WriteLog(hInstall, "started");
652 WriteLog(hInstall, "Checking value of CustomActionData");
653 wchar_t sBinaryName[MAX_PATH + 1];
654 DWORD nCCh = sizeof(sBinaryName) / sizeof(*sBinaryName);
655 CheckWin32Error("MsiGetPropertyW",
656 MsiGetPropertyW(hInstall, L"CustomActionData", sBinaryName, &nCCh));
657 WriteLog(hInstall, "Got CustomActionData value:", sBinaryName);
659 if (!DeleteFileW(sBinaryName))
661 if (DWORD nError = GetLastError(); nError != ERROR_FILE_NOT_FOUND)
662 ThrowWin32Error("DeleteFileW", nError);
664 WriteLog(hInstall, "File successfully removed");
666 catch (std::exception& e)
668 WriteLog(hInstall, e.what());
670 // Always return success - we don't want rollback to fail.
671 return ERROR_SUCCESS;
674 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */