1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
15 #define WIN32_LEAN_AND_MEAN
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
31 template <typename IntType
> std::string
Num2Dec(IntType n
)
33 std::stringstream sMsg
;
38 std::string
Win32ErrorMessage(const char* sFunc
, DWORD nWin32Error
)
40 std::stringstream sMsg
;
41 sMsg
<< sFunc
<< " failed with Win32 error code " << Num2Hex(nWin32Error
) << "!";
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
)
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
);
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 S1
, class... SOther
>
100 void WriteLogElem(MSIHANDLE hInst
, MSIHANDLE hRec
, std::ostringstream
& sTmpl
, UINT nField
,
101 const S1
& elem
, const SOther
&... others
);
103 template <class Ch
, class... SOther
>
104 void 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
...);
112 template <class S1
, class... SOther
>
113 void 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
...);
119 std::string sLogPrefix
;
121 template <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
...);
132 // Show a warning message box. This will be automatically suppressed in unattended installation.
133 void ShowWarning(MSIHANDLE hInst
, const std::wstring
& sKBNo
, const char* sMessage
)
135 // Error table's message #25000: "Installing a pre-requisite [2] failed.
136 // You might need to manually install it from Microsoft site to be able to run the product.[3]"
137 PMSIHANDLE hRec
= MsiCreateRecord(3);
138 // To show a message from Error table, record's Field 0 must be null
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
);
147 // Set custom action description visible in progress dialog
148 void SetStatusText(MSIHANDLE hInst
, const std::wstring
& actName
, const std::wstring
& actDesc
)
150 PMSIHANDLE hRec
= MsiCreateRecord(3);
151 // For INSTALLMESSAGE_ACTIONSTART, record's Field 0 must be null
152 // Field 1: Action name - must be non-null
153 MsiRecordSetStringW(hRec
, 1, actName
.c_str());
154 // Field 2: Action description - displayed in dialog
155 MsiRecordSetStringW(hRec
, 2, actDesc
.c_str());
156 // Let Field 3 stay null - no action template
157 MsiProcessMessage(hInst
, INSTALLMESSAGE_ACTIONSTART
, hRec
);
160 typedef std::unique_ptr
<void, decltype(&CloseHandle
)> CloseHandleGuard
;
161 CloseHandleGuard
Guard(HANDLE h
) { return CloseHandleGuard(h
, CloseHandle
); }
163 typedef std::unique_ptr
<const wchar_t, decltype(&DeleteFileW
)> DeleteFileGuard
;
164 DeleteFileGuard
Guard(const wchar_t* sFileName
) { return DeleteFileGuard(sFileName
, DeleteFileW
); }
166 typedef std::unique_ptr
<SC_HANDLE__
, decltype(&CloseServiceHandle
)> CloseServiceHandleGuard
;
167 CloseServiceHandleGuard
Guard(SC_HANDLE h
)
169 return CloseServiceHandleGuard(h
, CloseServiceHandle
);
172 std::wstring
GetTempFile()
174 wchar_t sPath
[MAX_PATH
+ 1];
175 DWORD nResult
= GetTempPathW(sizeof(sPath
) / sizeof(*sPath
), sPath
);
177 ThrowLastError("GetTempPathW");
179 wchar_t sFile
[MAX_PATH
+ 1];
180 nResult
= GetTempFileNameW(sPath
, L
"TMP", 0, sFile
);
182 ThrowLastError("GetTempFileNameW");
186 bool IsWow64Process()
189 BOOL bResult
= FALSE
;
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
))
197 ThrowLastError("IsWow64Process");
206 // This class uses MsiProcessMessage to check for user input: it returns IDCANCEL when user cancels
207 // installation. It throws a special exception, to be intercepted in main action function to return
208 // corresponding exit code.
209 class UserInputChecker
216 UserInputChecker(MSIHANDLE hInstall
)
217 : m_hInstall(hInstall
)
218 , m_hProgressRec(MsiCreateRecord(3))
220 // Use explicit progress messages
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();
227 // Prepare the record to following progress update calls
228 MsiRecordSetInteger(m_hProgressRec
, 1, 2);
229 MsiRecordSetInteger(m_hProgressRec
, 2, 0); // step by 0 - don't move progress
230 MsiRecordSetInteger(m_hProgressRec
, 3, 0);
233 void ThrowIfUserCancelled()
235 // Check if user has cancelled
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
;
246 // Checks if Windows Update service is disabled, and if it is, enables it temporarily.
247 // Also stops the service if it's currently running, because it seems that wusa.exe
248 // does not freeze when it starts the service itself.
249 class WUServiceEnabler
252 WUServiceEnabler(MSIHANDLE hInstall
)
253 : mhInstall(hInstall
)
254 , mhService(EnableWUService(hInstall
))
264 EnsureServiceEnabled(mhInstall
, mhService
.get(), false);
265 StopService(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
));
279 ThrowLastError("OpenSCManagerW");
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
));
286 ThrowLastError("OpenServiceW");
287 WriteLog(hInstall
, "Obtained WU service handle");
289 const DWORD nCurrentStatus
= ServiceStatus(hInstall
, hService
.get());
290 // Stop currently running service to prevent wusa.exe from hanging trying to detect if the
291 // update is applicable (sometimes this freezes it ~indefinitely; it seems that it doesn't
292 // happen if wusa.exe starts the service itself: https://superuser.com/questions/1044528/).
293 // tdf#124794: Wait for service to stop.
294 if (nCurrentStatus
== SERVICE_RUNNING
)
295 StopService(hInstall
, hService
.get(), true);
297 if (nCurrentStatus
== SERVICE_RUNNING
298 || !EnsureServiceEnabled(hInstall
, hService
.get(), true))
300 // No need to restore anything back, since we didn't change config
302 WriteLog(hInstall
, "Service configuration is unchanged");
308 // Returns if the service configuration was actually changed
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
)
317 ThrowWin32Error("QueryServiceConfigW", nError
);
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
))
322 ThrowLastError("QueryServiceConfigW");
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,
344 ThrowLastError("ChangeServiceConfigW");
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
))
357 ThrowLastError("QueryServiceStatus");
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
))
399 ThrowLastError("ControlService");
401 "Successfully sent SERVICE_CONTROL_STOP code to Windows Update service");
402 if (aServiceStatus
.dwCurrentState
!= SERVICE_STOPPED
&& bWait
)
404 // Let user cancel too long wait
405 UserInputChecker
aInputChecker(hInstall
);
406 // aServiceStatus.dwWaitHint is unreasonably high for Windows Update (30000),
407 // so don't use it, but simply poll service status each second
408 for (int nWait
= 0; nWait
< 30; ++nWait
) // arbitrary limit of 30 s
410 for (int i
= 0; i
< 2; ++i
) // check user input twice a second
413 aInputChecker
.ThrowIfUserCancelled();
416 if (!QueryServiceStatus(hService
, &aServiceStatus
))
417 ThrowLastError("QueryServiceStatus");
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
;
442 // Immediate action "unpack_msu" that has access to installation database and properties; checks
443 // "InstMSUBinary" property and unpacks the binary with that name to a temporary file; sets
444 // "cleanup_msu" and "inst_msu" properties to the full name of the extracted temporary file. These
445 // properties will become "CustomActionData" property inside relevant deferred actions.
446 extern "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:",
459 sInstMSUBinary
); // KB2999226|Windows61-KB2999226-x64msu
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
);
469 ThrowLastError("MsiGetActiveDatabase");
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
)
498 ThrowLastError("CreateFileW");
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))
515 ThrowLastError("WriteFile");
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()));
526 // Don't delete the file: it will be done by following actions (inst_msu or cleanup_msu)
527 (void)aDeleteFileGuard
.release();
528 return ERROR_SUCCESS
;
530 catch (std::exception
& e
)
532 WriteLog(hInstall
, e
.what());
534 return ERROR_INSTALL_FAILURE
;
537 // Deferred action "inst_msu" that must be run from system account. Receives the tempfile name from
538 // "CustomActionData" property, and runs wusa.exe to install it. Waits for it and checks exit code.
539 extern "C" __declspec(dllexport
) UINT __stdcall
InstallMSU(MSIHANDLE hInstall
)
541 std::wstring sKBNo
; // "KB2999226"
544 sLogPrefix
= "InstallMSU:";
545 WriteLog(hInstall
, "started");
547 WriteLog(hInstall
, "Checking value of CustomActionData");
548 wchar_t sCustomActionData
[MAX_PATH
+ 10]; // "KB2999226|C:\Temp\binary.tmp"
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");
563 // In case the Windows Update service is disabled, we temporarily enable it here. We also
564 // stop running WU service, to avoid wusa.exe freeze (see comment in EnableWUService).
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
))
585 ThrowLastError("CreateProcessW");
586 CloseHandle(pi
.hThread
);
587 auto aCloseProcHandleGuard(Guard(pi
.hProcess
));
588 WriteLog(hInstall
, "CreateProcessW succeeded");
591 // This block waits when the started wusa.exe process finishes. Since it's possible
592 // for wusa.exe in some circumstances to wait really long (indefinitely?), we check
593 // for user input here.
594 UserInputChecker
aInputChecker(hInstall
);
597 DWORD nWaitResult
= WaitForSingleObject(pi
.hProcess
, 500);
598 if (nWaitResult
== WAIT_OBJECT_0
)
599 break; // wusa.exe finished
600 else if (nWaitResult
== WAIT_TIMEOUT
)
601 aInputChecker
.ThrowIfUserCancelled();
603 ThrowWin32Error("WaitForSingleObject", nWaitResult
);
608 if (!GetExitCodeProcess(pi
.hProcess
, &nExitCode
))
609 ThrowLastError("GetExitCodeProcess");
611 HRESULT hr
= static_cast<HRESULT
>(nExitCode
);
613 // HRESULT_FROM_WIN32 is defined as an inline function in SDK 8.1 without the constexpr
614 // And it won't work to place it inside the switch statement.
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
: // Windows could lie us about its version, etc.
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
; // Do not break on MSU installation errors
646 // Rollback deferred action "cleanup_msu" that is executed on error or cancel.
647 // It removes the temporary file created by UnpackMSUForInstall action.
648 // MUST be placed IMMEDIATELY AFTER "unpack_msu" in execute sequence.
649 extern "C" __declspec(dllexport
) UINT __stdcall
CleanupMSU(MSIHANDLE hInstall
)
653 sLogPrefix
= "CleanupMSU:";
654 WriteLog(hInstall
, "started");
656 WriteLog(hInstall
, "Checking value of CustomActionData");
657 wchar_t sBinaryName
[MAX_PATH
+ 1];
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
)
666 ThrowWin32Error("DeleteFileW", nError
);
668 WriteLog(hInstall
, "File successfully removed");
670 catch (std::exception
& e
)
672 WriteLog(hInstall
, e
.what());
674 // Always return success - we don't want rollback to fail.
675 return ERROR_SUCCESS
;
678 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */