LibreOffice Module setup_native (master) 1
inst_msu.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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 */
9
10#include <memory>
11#include <string>
12#include <sstream>
13#include <iomanip>
14
15#define WIN32_LEAN_AND_MEAN
16#include <windows.h>
17#include <Shlobj.h>
18#include <Wuerror.h>
19#include <msiquery.h>
20
21namespace
22{
23template <typename IntType> std::string Num2Hex(IntType n)
24{
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();
29}
30
31template <typename IntType> std::string Num2Dec(IntType n)
32{
33 std::stringstream sMsg;
34 sMsg << n;
35 return sMsg.str();
36}
37
38std::string Win32ErrorMessage(const char* sFunc, DWORD nWin32Error)
39{
40 std::stringstream sMsg;
41 sMsg << sFunc << " failed with Win32 error code " << Num2Hex(nWin32Error) << "!";
42
43 return sMsg.str();
44}
45
46void ThrowHResult(const char* sFunc, HRESULT hr)
47{
48 std::stringstream sMsg;
49 sMsg << sFunc << " failed (HRESULT = " << Num2Hex(hr) << ")!";
50
51 throw std::exception(sMsg.str().c_str());
52}
53
54void CheckHResult(const char* sFunc, HRESULT hr)
55{
56 if (FAILED(hr))
57 ThrowHResult(sFunc, hr);
58}
59
60void ThrowWin32Error(const char* sFunc, DWORD nWin32Error)
61{
62 throw std::exception(Win32ErrorMessage(sFunc, nWin32Error).c_str());
63}
64
65void ThrowLastError(const char* sFunc) { ThrowWin32Error(sFunc, GetLastError()); }
66
67void CheckWin32Error(const char* sFunc, DWORD nWin32Error)
68{
69 if (nWin32Error != ERROR_SUCCESS)
70 ThrowWin32Error(sFunc, nWin32Error);
71}
72
73std::wstring GetKnownFolder(const KNOWNFOLDERID& rfid)
74{
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;
81}
82
83void WriteLogElem(MSIHANDLE hInst, MSIHANDLE hRecord, std::ostringstream& sTmpl, UINT)
84{
85 MsiRecordSetStringA(hRecord, 0, sTmpl.str().c_str());
86 MsiProcessMessage(hInst, INSTALLMESSAGE_INFO, hRecord);
87}
88
89void RecSetString(MSIHANDLE hRec, UINT nField, LPCSTR sVal)
90{
91 MsiRecordSetStringA(hRec, nField, sVal);
92}
93
94void RecSetString(MSIHANDLE hRec, UINT nField, LPCWSTR sVal)
95{
96 MsiRecordSetStringW(hRec, nField, sVal);
97}
98
99template <class S1, class... SOther>
100void WriteLogElem(MSIHANDLE hInst, MSIHANDLE hRec, std::ostringstream& sTmpl, UINT nField,
101 const S1& elem, const SOther&... others);
102
103template <class Ch, class... SOther>
104void WriteLogElem(MSIHANDLE hInst, MSIHANDLE hRec, std::ostringstream& sTmpl, UINT nField,
105 const Ch* elem, const SOther&... others)
106{
107 sTmpl << " [" << nField << "]";
108 RecSetString(hRec, nField, elem);
109 WriteLogElem(hInst, hRec, sTmpl, nField + 1, others...);
110}
111
112template <class S1, class... SOther>
113void WriteLogElem(MSIHANDLE hInst, MSIHANDLE hRec, std::ostringstream& sTmpl, UINT nField,
114 const S1& elem, const SOther&... others)
115{
116 WriteLogElem(hInst, hRec, sTmpl, nField, elem.c_str(), others...);
117}
118
119std::string sLogPrefix;
120
121template <class... StrType> void WriteLog(MSIHANDLE hInst, const StrType&... elements)
122{
123 PMSIHANDLE hRec = MsiCreateRecord(sizeof...(elements));
124 if (!hRec)
125 return;
126
127 std::ostringstream sTemplate;
128 sTemplate << sLogPrefix;
129 WriteLogElem(hInst, hRec, sTemplate, 1, elements...);
130}
131
132// Show a warning message box. This will be automatically suppressed in unattended installation.
133void ShowWarning(MSIHANDLE hInst, const std::wstring& sKBNo, const char* sMessage)
134{
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());
141 std::string s("\n");
142 s += sMessage;
143 MsiRecordSetStringA(hRec, 3, s.c_str());
144 MsiProcessMessage(hInst, INSTALLMESSAGE_WARNING, hRec);
145}
146
147// Set custom action description visible in progress dialog
148void SetStatusText(MSIHANDLE hInst, const std::wstring& actName, const std::wstring& actDesc)
149{
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);
158}
159
160typedef std::unique_ptr<void, decltype(&CloseHandle)> CloseHandleGuard;
161CloseHandleGuard Guard(HANDLE h) { return CloseHandleGuard(h, CloseHandle); }
162
163typedef std::unique_ptr<const wchar_t, decltype(&DeleteFileW)> DeleteFileGuard;
164DeleteFileGuard Guard(const wchar_t* sFileName) { return DeleteFileGuard(sFileName, DeleteFileW); }
165
166typedef std::unique_ptr<SC_HANDLE__, decltype(&CloseServiceHandle)> CloseServiceHandleGuard;
167CloseServiceHandleGuard Guard(SC_HANDLE h)
168{
169 return CloseServiceHandleGuard(h, CloseServiceHandle);
170}
171
172std::wstring GetTempFile()
173{
174 wchar_t sPath[MAX_PATH + 1];
175 DWORD nResult = GetTempPathW(sizeof(sPath) / sizeof(*sPath), sPath);
176 if (!nResult)
177 ThrowLastError("GetTempPathW");
178
179 wchar_t sFile[MAX_PATH + 1];
180 nResult = GetTempFileNameW(sPath, L"TMP", 0, sFile);
181 if (!nResult)
182 ThrowLastError("GetTempFileNameW");
183 return sFile;
184}
185
186bool IsWow64Process()
187{
188#if !defined _WIN64
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"));
193
194 if (fnIsWow64Process)
195 {
196 if (!fnIsWow64Process(GetCurrentProcess(), &bResult))
197 ThrowLastError("IsWow64Process");
198 }
199
200 return bResult;
201#else
202 return false;
203#endif
204}
205
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.
209class UserInputChecker
210{
211public:
212 class eUserCancelled
213 {
214 };
215
216 UserInputChecker(MSIHANDLE hInstall)
217 : m_hInstall(hInstall)
218 , m_hProgressRec(MsiCreateRecord(3))
219 {
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);
231 }
232
233 void ThrowIfUserCancelled()
234 {
235 // Check if user has cancelled
236 int nResult = MsiProcessMessage(m_hInstall, INSTALLMESSAGE_PROGRESS, m_hProgressRec);
237 if (nResult == IDCANCEL)
238 throw eUserCancelled();
239 }
240
241private:
242 MSIHANDLE m_hInstall;
243 PMSIHANDLE m_hProgressRec;
244};
245
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.
249class WUServiceEnabler
250{
251public:
252 WUServiceEnabler(MSIHANDLE hInstall)
253 : mhInstall(hInstall)
254 , mhService(EnableWUService(hInstall))
255 {
256 }
257
258 ~WUServiceEnabler()
259 {
260 try
261 {
262 if (mhService)
263 {
264 EnsureServiceEnabled(mhInstall, mhService.get(), false);
265 StopService(mhInstall, mhService.get(), false);
266 }
267 }
268 catch (std::exception& e)
269 {
270 WriteLog(mhInstall, e.what());
271 }
272 }
273
274private:
275 static CloseServiceHandleGuard EnableWUService(MSIHANDLE hInstall)
276 {
277 auto hSCM = Guard(OpenSCManagerW(nullptr, nullptr, SC_MANAGER_ALL_ACCESS));
278 if (!hSCM)
279 ThrowLastError("OpenSCManagerW");
280 WriteLog(hInstall, "Opened service control manager");
281
282 auto hService = Guard(OpenServiceW(hSCM.get(), L"wuauserv",
283 SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG
284 | SERVICE_QUERY_STATUS | SERVICE_STOP));
285 if (!hService)
286 ThrowLastError("OpenServiceW");
287 WriteLog(hInstall, "Obtained WU service handle");
288
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);
296
297 if (nCurrentStatus == SERVICE_RUNNING
298 || !EnsureServiceEnabled(hInstall, hService.get(), true))
299 {
300 // No need to restore anything back, since we didn't change config
301 hService.reset();
302 WriteLog(hInstall, "Service configuration is unchanged");
303 }
304
305 return hService;
306 }
307
308 // Returns if the service configuration was actually changed
309 static bool EnsureServiceEnabled(MSIHANDLE hInstall, SC_HANDLE hService, bool bEnabled)
310 {
311 bool bConfigChanged = false;
312
313 DWORD nCbRequired = 0;
314 if (!QueryServiceConfigW(hService, nullptr, 0, &nCbRequired))
315 {
316 if (DWORD nError = GetLastError(); nError != ERROR_INSUFFICIENT_BUFFER)
317 ThrowWin32Error("QueryServiceConfigW", nError);
318 }
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");
324
325 DWORD eNewStartType = 0;
326 if (bEnabled && pConfig->dwStartType == SERVICE_DISABLED)
327 {
328 bConfigChanged = true;
329 eNewStartType = SERVICE_DEMAND_START;
330 WriteLog(hInstall, "Service is disabled, and requested to enable");
331 }
332 else if (!bEnabled && pConfig->dwStartType != SERVICE_DISABLED)
333 {
334 bConfigChanged = true;
335 eNewStartType = SERVICE_DISABLED;
336 WriteLog(hInstall, "Service is enabled, and requested to disable");
337 }
338
339 if (bConfigChanged)
340 {
341 if (!ChangeServiceConfigW(hService, SERVICE_NO_CHANGE, eNewStartType, SERVICE_NO_CHANGE,
342 nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
343 nullptr))
344 ThrowLastError("ChangeServiceConfigW");
345 WriteLog(hInstall, "WU service config successfully changed");
346 }
347 else
348 WriteLog(hInstall, "No need to modify service config");
349
350 return bConfigChanged;
351 }
352
353 static DWORD ServiceStatus(MSIHANDLE hInstall, SC_HANDLE hService)
354 {
355 SERVICE_STATUS aServiceStatus{};
356 if (!QueryServiceStatus(hService, &aServiceStatus))
357 ThrowLastError("QueryServiceStatus");
358
359 std::string sStatus;
360 switch (aServiceStatus.dwCurrentState)
361 {
362 case SERVICE_STOPPED:
363 sStatus = "SERVICE_STOPPED";
364 break;
365 case SERVICE_START_PENDING:
366 sStatus = "SERVICE_START_PENDING";
367 break;
368 case SERVICE_STOP_PENDING:
369 sStatus = "SERVICE_STOP_PENDING";
370 break;
371 case SERVICE_RUNNING:
372 sStatus = "SERVICE_RUNNING";
373 break;
374 case SERVICE_CONTINUE_PENDING:
375 sStatus = "SERVICE_CONTINUE_PENDING";
376 break;
377 case SERVICE_PAUSE_PENDING:
378 sStatus = "SERVICE_PAUSE_PENDING";
379 break;
380 case SERVICE_PAUSED:
381 sStatus = "SERVICE_PAUSED";
382 break;
383 default:
384 sStatus = Num2Hex(aServiceStatus.dwCurrentState);
385 }
386 WriteLog(hInstall, "Service status is", sStatus);
387
388 return aServiceStatus.dwCurrentState;
389 }
390
391 static void StopService(MSIHANDLE hInstall, SC_HANDLE hService, bool bWait)
392 {
393 try
394 {
395 if (ServiceStatus(hInstall, hService) != SERVICE_STOPPED)
396 {
397 SERVICE_STATUS aServiceStatus{};
398 if (!ControlService(hService, SERVICE_CONTROL_STOP, &aServiceStatus))
399 ThrowLastError("ControlService");
400 WriteLog(hInstall,
401 "Successfully sent SERVICE_CONTROL_STOP code to Windows Update service");
402 if (aServiceStatus.dwCurrentState != SERVICE_STOPPED && bWait)
403 {
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
409 {
410 for (int i = 0; i < 2; ++i) // check user input twice a second
411 {
412 Sleep(500);
413 aInputChecker.ThrowIfUserCancelled();
414 }
415
416 if (!QueryServiceStatus(hService, &aServiceStatus))
417 ThrowLastError("QueryServiceStatus");
418
419 if (aServiceStatus.dwCurrentState == SERVICE_STOPPED)
420 break;
421 }
422 }
423 if (aServiceStatus.dwCurrentState == SERVICE_STOPPED)
424 WriteLog(hInstall, "Successfully stopped Windows Update service");
425 else if (bWait)
426 WriteLog(hInstall, "Wait for Windows Update stop timed out - proceeding");
427 }
428 else
429 WriteLog(hInstall, "Windows Update service is not running");
430 }
431 catch (std::exception& e)
432 {
433 WriteLog(hInstall, e.what());
434 }
435 }
436
437 MSIHANDLE mhInstall;
438 CloseServiceHandleGuard mhService;
439};
440}
441
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.
446extern "C" __declspec(dllexport) UINT __stdcall UnpackMSUForInstall(MSIHANDLE hInstall)
447{
448 try
449 {
450 sLogPrefix = "UnpackMSUForInstall:";
451 WriteLog(hInstall, "started");
452
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'|');
461 if (!sBinaryName)
462 throw std::exception("No KB number in InstMSUBinary!");
463 // "KB2999226"
464 const std::wstring sKBNo(sInstMSUBinary, sBinaryName - sInstMSUBinary);
465 ++sBinaryName;
466
467 PMSIHANDLE hDatabase = MsiGetActiveDatabase(hInstall);
468 if (!hDatabase)
469 ThrowLastError("MsiGetActiveDatabase");
470 WriteLog(hInstall, "MsiGetActiveDatabase succeeded");
471
472 std::wstringstream sQuery;
473 sQuery << "SELECT `Data` FROM `Binary` WHERE `Name`='" << sBinaryName << "'";
474
475 PMSIHANDLE hBinaryView;
476 CheckWin32Error("MsiDatabaseOpenViewW",
477 MsiDatabaseOpenViewW(hDatabase, sQuery.str().c_str(), &hBinaryView));
478 WriteLog(hInstall, "MsiDatabaseOpenViewW succeeded");
479
480 CheckWin32Error("MsiViewExecute", MsiViewExecute(hBinaryView, 0));
481 WriteLog(hInstall, "MsiViewExecute succeeded");
482
483 PMSIHANDLE hBinaryRecord;
484 CheckWin32Error("MsiViewFetch", MsiViewFetch(hBinaryView, &hBinaryRecord));
485 WriteLog(hInstall, "MsiViewFetch succeeded");
486
487 const std::wstring sBinary = GetTempFile();
488 auto aDeleteFileGuard(Guard(sBinary.c_str()));
489 WriteLog(hInstall, "Temp file path:", sBinary.c_str());
490
491 CheckWin32Error("MsiSetPropertyW",
492 MsiSetPropertyW(hInstall, L"cleanup_msu", sBinary.c_str()));
493
494 {
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));
500
501 const DWORD nBufSize = 1024 * 1024;
502 std::unique_ptr<char[]> buf(new char[nBufSize]);
503 DWORD nTotal = 0;
504 DWORD nRead;
505 do
506 {
507 nRead = nBufSize;
508 CheckWin32Error("MsiRecordReadStream",
509 MsiRecordReadStream(hBinaryRecord, 1, buf.get(), &nRead));
510
511 if (nRead > 0)
512 {
513 DWORD nWritten;
514 if (!WriteFile(hFile, buf.get(), nRead, &nWritten, nullptr))
515 ThrowLastError("WriteFile");
516 nTotal += nWritten;
517 }
518 } while (nRead == nBufSize);
519
520 WriteLog(hInstall, "Successfully wrote", Num2Dec(nTotal), "bytes");
521 }
522 const std::wstring s_inst_msu = sKBNo + L"|" + sBinary;
523 CheckWin32Error("MsiSetPropertyW",
524 MsiSetPropertyW(hInstall, L"inst_msu", s_inst_msu.c_str()));
525
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;
529 }
530 catch (std::exception& e)
531 {
532 WriteLog(hInstall, e.what());
533 }
534 return ERROR_INSTALL_FAILURE;
535}
536
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.
539extern "C" __declspec(dllexport) UINT __stdcall InstallMSU(MSIHANDLE hInstall)
540{
541 std::wstring sKBNo; // "KB2999226"
542 try
543 {
544 sLogPrefix = "InstallMSU:";
545 WriteLog(hInstall, "started");
546
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'|');
554 if (!sBinaryName)
555 throw std::exception("No KB number in CustomActionData!");
556 sKBNo = std::wstring(sCustomActionData, sBinaryName - sCustomActionData);
557 ++sBinaryName;
558 auto aDeleteFileGuard(Guard(sBinaryName));
559
560 SetStatusText(hInstall, L"WU service state check",
561 L"Checking Windows Update service state");
562
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);
566
567 SetStatusText(hInstall, sKBNo + L" installation", L"Installing " + sKBNo);
568
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);
575
576 std::wstring sWUSACmd
577 = L"\"" + sWUSAPath + L"\" \"" + sBinaryName + L"\" /quiet /norestart";
578 WriteLog(hInstall, "Prepared wusa command:", sWUSACmd);
579
580 STARTUPINFOW si{};
581 si.cb = sizeof(si);
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");
589
590 {
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);
595 for (;;)
596 {
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();
602 else
603 ThrowWin32Error("WaitForSingleObject", nWaitResult);
604 }
605 }
606
607 DWORD nExitCode = 0;
608 if (!GetExitCodeProcess(pi.hProcess, &nExitCode))
609 ThrowLastError("GetExitCodeProcess");
610
611 HRESULT hr = static_cast<HRESULT>(nExitCode);
612
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)
616 {
617 hr = ERROR_SUCCESS_REBOOT_REQUIRED;
618 }
619
620 switch (hr)
621 {
622 case S_OK:
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;
629
630 default:
631 ThrowHResult("Execution of wusa.exe", hr);
632 }
633 }
634 catch (const UserInputChecker::eUserCancelled&)
635 {
636 return ERROR_INSTALL_USEREXIT;
637 }
638 catch (std::exception& e)
639 {
640 WriteLog(hInstall, e.what());
641 ShowWarning(hInstall, sKBNo, e.what());
642 }
643 return ERROR_SUCCESS; // Do not break on MSU installation errors
644}
645
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.
649extern "C" __declspec(dllexport) UINT __stdcall CleanupMSU(MSIHANDLE hInstall)
650{
651 try
652 {
653 sLogPrefix = "CleanupMSU:";
654 WriteLog(hInstall, "started");
655
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);
662
663 if (!DeleteFileW(sBinaryName))
664 {
665 if (DWORD nError = GetLastError(); nError != ERROR_FILE_NOT_FOUND)
666 ThrowWin32Error("DeleteFileW", nError);
667 }
668 WriteLog(hInstall, "File successfully removed");
669 }
670 catch (std::exception& e)
671 {
672 WriteLog(hInstall, e.what());
673 }
674 // Always return success - we don't want rollback to fail.
675 return ERROR_SUCCESS;
676}
677
678/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
#define FALSE
#define MAX_PATH
__declspec(dllexport) UINT __stdcall UnpackMSUForInstall(MSIHANDLE hInstall)
Definition: inst_msu.cxx:446
sal_Int64 n
int i
sal_Int32 h
const wchar_t *typedef BOOL
Definition: regactivex.cxx:38
BOOL StopService()
return hr
OUString sMessage
std::string Num2Hex(IntType n)
void ThrowLastError(const char *sFunc)
void ThrowWin32Error(const char *sFunc, DWORD nWin32Error)