VirtualBox

source: vbox/trunk/src/VBox/Installer/win/Stub/VBoxStub.cpp@ 93390

Last change on this file since 93390 was 93390, checked in by vboxsync, 3 years ago

Installer/win: Documented proper quoting of --msiparams, adding a --msi-props <prop> <value> option for simplifying this. Changed to exit with RTEXITCODE_SYNTAX on syntax error. bugref:10164

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 43.6 KB
Line 
1/* $Id: VBoxStub.cpp 93390 2022-01-21 09:26:43Z vboxsync $ */
2/** @file
3 * VBoxStub - VirtualBox's Windows installer stub.
4 */
5
6/*
7 * Copyright (C) 2010-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0501
23# undef _WIN32_WINNT
24# define _WIN32_WINNT 0x0501 /* AttachConsole() / FreeConsole(). */
25#endif
26
27#include <iprt/win/windows.h>
28#include <iprt/win/commctrl.h>
29#include <fcntl.h>
30#include <io.h>
31#include <lmerr.h>
32#include <msiquery.h>
33#include <iprt/win/objbase.h>
34
35#include <iprt/win/shlobj.h>
36#include <stdlib.h>
37#include <stdio.h>
38#include <string.h>
39#include <strsafe.h>
40
41#include <VBox/version.h>
42
43#include <iprt/assert.h>
44#include <iprt/dir.h>
45#include <iprt/err.h>
46#include <iprt/file.h>
47#include <iprt/getopt.h>
48#include <iprt/initterm.h>
49#include <iprt/list.h>
50#include <iprt/mem.h>
51#include <iprt/message.h>
52#include <iprt/param.h>
53#include <iprt/path.h>
54#include <iprt/stream.h>
55#include <iprt/string.h>
56#include <iprt/thread.h>
57#include <iprt/utf16.h>
58
59#include "VBoxStub.h"
60#include "../StubBld/VBoxStubBld.h"
61#include "resource.h"
62
63#ifdef VBOX_WITH_CODE_SIGNING
64# include "VBoxStubCertUtil.h"
65# include "VBoxStubPublicCert.h"
66#endif
67
68#ifndef TARGET_NT4
69/* Use an own console window if run in verbose mode. */
70# define VBOX_STUB_WITH_OWN_CONSOLE
71#endif
72
73
74/*********************************************************************************************************************************
75* Defined Constants And Macros *
76*********************************************************************************************************************************/
77#define MY_UNICODE_SUB(str) L ##str
78#define MY_UNICODE(str) MY_UNICODE_SUB(str)
79
80
81/*********************************************************************************************************************************
82* Structures and Typedefs *
83*********************************************************************************************************************************/
84/**
85 * Cleanup record.
86 */
87typedef struct STUBCLEANUPREC
88{
89 /** List entry. */
90 RTLISTNODE ListEntry;
91 /** True if file, false if directory. */
92 bool fFile;
93 /** The path to the file or directory to clean up. */
94 char szPath[1];
95} STUBCLEANUPREC;
96/** Pointer to a cleanup record. */
97typedef STUBCLEANUPREC *PSTUBCLEANUPREC;
98
99
100/*********************************************************************************************************************************
101* Global Variables *
102*********************************************************************************************************************************/
103/** Whether it's a silent or interactive GUI driven install. */
104static bool g_fSilent = false;
105/** List of temporary files. */
106static RTLISTANCHOR g_TmpFiles;
107/** Verbosity flag. */
108static int g_iVerbosity = 0;
109
110
111
112/**
113 * Shows an error message box with a printf() style formatted string.
114 *
115 * @returns RTEXITCODE_FAILURE
116 * @param pszFmt Printf-style format string to show in the message box body.
117 *
118 */
119static RTEXITCODE ShowError(const char *pszFmt, ...)
120{
121 char *pszMsg;
122 va_list va;
123
124 va_start(va, pszFmt);
125 if (RTStrAPrintfV(&pszMsg, pszFmt, va))
126 {
127 if (g_fSilent)
128 RTMsgError("%s", pszMsg);
129 else
130 {
131 PRTUTF16 pwszMsg;
132 int rc = RTStrToUtf16(pszMsg, &pwszMsg);
133 if (RT_SUCCESS(rc))
134 {
135 MessageBoxW(GetDesktopWindow(), pwszMsg, MY_UNICODE(VBOX_STUB_TITLE), MB_ICONERROR);
136 RTUtf16Free(pwszMsg);
137 }
138 else
139 MessageBoxA(GetDesktopWindow(), pszMsg, VBOX_STUB_TITLE, MB_ICONERROR);
140 }
141 RTStrFree(pszMsg);
142 }
143 else /* Should never happen! */
144 AssertMsgFailed(("Failed to format error text of format string: %s!\n", pszFmt));
145 va_end(va);
146 return RTEXITCODE_FAILURE;
147}
148
149
150/**
151 * Same as ShowError, only it returns RTEXITCODE_SYNTAX.
152 */
153static RTEXITCODE ShowSyntaxError(const char *pszFmt, ...)
154{
155 va_list va;
156 va_start(va, pszFmt);
157 ShowError("%N", pszFmt, &va);
158 va_end(va);
159 return RTEXITCODE_SYNTAX;
160}
161
162
163/**
164 * Shows a message box with a printf() style formatted string.
165 *
166 * @param uType Type of the message box (see MSDN).
167 * @param pszFmt Printf-style format string to show in the message box body.
168 *
169 */
170static void ShowInfo(const char *pszFmt, ...)
171{
172 char *pszMsg;
173 va_list va;
174 va_start(va, pszFmt);
175 int rc = RTStrAPrintfV(&pszMsg, pszFmt, va);
176 va_end(va);
177 if (rc >= 0)
178 {
179 if (g_fSilent)
180 RTPrintf("%s\n", pszMsg);
181 else
182 {
183 PRTUTF16 pwszMsg;
184 rc = RTStrToUtf16(pszMsg, &pwszMsg);
185 if (RT_SUCCESS(rc))
186 {
187 MessageBoxW(GetDesktopWindow(), pwszMsg, MY_UNICODE(VBOX_STUB_TITLE), MB_ICONINFORMATION);
188 RTUtf16Free(pwszMsg);
189 }
190 else
191 MessageBoxA(GetDesktopWindow(), pszMsg, VBOX_STUB_TITLE, MB_ICONINFORMATION);
192 }
193 }
194 else /* Should never happen! */
195 AssertMsgFailed(("Failed to format error text of format string: %s!\n", pszFmt));
196 RTStrFree(pszMsg);
197}
198
199
200/**
201 * Finds the specified in the resource section of the executable.
202 *
203 * @returns IPRT status code.
204 *
205 * @param pszDataName Name of resource to read.
206 * @param ppvResource Where to return the pointer to the data.
207 * @param pdwSize Where to return the size of the data (if found).
208 * Optional.
209 */
210static int FindData(const char *pszDataName, PVOID *ppvResource, DWORD *pdwSize)
211{
212 AssertReturn(pszDataName, VERR_INVALID_PARAMETER);
213 HINSTANCE hInst = NULL; /* indicates the executable image */
214
215 /* Find our resource. */
216 PRTUTF16 pwszDataName;
217 int rc = RTStrToUtf16(pszDataName, &pwszDataName);
218 AssertRCReturn(rc, rc);
219 HRSRC hRsrc = FindResourceExW(hInst,
220 (LPWSTR)RT_RCDATA,
221 pwszDataName,
222 MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));
223 RTUtf16Free(pwszDataName);
224 AssertReturn(hRsrc, VERR_IO_GEN_FAILURE);
225
226 /* Get resource size. */
227 DWORD cb = SizeofResource(hInst, hRsrc);
228 AssertReturn(cb > 0, VERR_NO_DATA);
229 if (pdwSize)
230 *pdwSize = cb;
231
232 /* Get pointer to resource. */
233 HGLOBAL hData = LoadResource(hInst, hRsrc);
234 AssertReturn(hData, VERR_IO_GEN_FAILURE);
235
236 /* Lock resource. */
237 *ppvResource = LockResource(hData);
238 AssertReturn(*ppvResource, VERR_IO_GEN_FAILURE);
239 return VINF_SUCCESS;
240}
241
242
243/**
244 * Finds the header for the given package.
245 *
246 * @returns Pointer to the package header on success. On failure NULL is
247 * returned after ShowError has been invoked.
248 * @param iPackage The package number.
249 */
250static PVBOXSTUBPKG FindPackageHeader(unsigned iPackage)
251{
252 char szHeaderName[32];
253 RTStrPrintf(szHeaderName, sizeof(szHeaderName), "HDR_%02d", iPackage);
254
255 PVBOXSTUBPKG pPackage;
256 int rc = FindData(szHeaderName, (PVOID *)&pPackage, NULL);
257 if (RT_FAILURE(rc))
258 {
259 ShowError("Internal error: Could not find package header #%u: %Rrc", iPackage, rc);
260 return NULL;
261 }
262
263 /** @todo validate it. */
264 return pPackage;
265}
266
267
268
269/**
270 * Constructs a full temporary file path from the given parameters.
271 *
272 * @returns iprt status code.
273 *
274 * @param pszTempPath The pure path to use for construction.
275 * @param pszTargetFileName The pure file name to use for construction.
276 * @param ppszTempFile Pointer to the constructed string. Must be freed
277 * using RTStrFree().
278 */
279static int GetTempFileAlloc(const char *pszTempPath,
280 const char *pszTargetFileName,
281 char **ppszTempFile)
282{
283 if (RTStrAPrintf(ppszTempFile, "%s\\%s", pszTempPath, pszTargetFileName) >= 0)
284 return VINF_SUCCESS;
285 return VERR_NO_STR_MEMORY;
286}
287
288
289/**
290 * Extracts a built-in resource to disk.
291 *
292 * @returns iprt status code.
293 *
294 * @param pszResourceName The resource name to extract.
295 * @param pszTempFile The full file path + name to extract the resource to.
296 *
297 */
298static int ExtractFile(const char *pszResourceName,
299 const char *pszTempFile)
300{
301#if 0 /* Another example of how unnecessarily complicated things get with
302 do-break-while-false and you end up with buggy code using uninitialized
303 variables. */
304 int rc;
305 RTFILE fh;
306 BOOL bCreatedFile = FALSE;
307
308 do
309 {
310 AssertMsgBreak(pszResourceName, ("Resource pointer invalid!\n")); /* rc is not initialized here, we'll return garbage. */
311 AssertMsgBreak(pszTempFile, ("Temp file pointer invalid!")); /* Ditto. */
312
313 /* Read the data of the built-in resource. */
314 PVOID pvData = NULL;
315 DWORD dwDataSize = 0;
316 rc = FindData(pszResourceName, &pvData, &dwDataSize);
317 AssertMsgRCBreak(rc, ("Could not read resource data!\n"));
318
319 /* Create new (and replace an old) file. */
320 rc = RTFileOpen(&fh, pszTempFile,
321 RTFILE_O_CREATE_REPLACE
322 | RTFILE_O_WRITE
323 | RTFILE_O_DENY_NOT_DELETE
324 | RTFILE_O_DENY_WRITE);
325 AssertMsgRCBreak(rc, ("Could not open file for writing!\n"));
326 bCreatedFile = TRUE;
327
328 /* Write contents to new file. */
329 size_t cbWritten = 0;
330 rc = RTFileWrite(fh, pvData, dwDataSize, &cbWritten);
331 AssertMsgRCBreak(rc, ("Could not open file for writing!\n"));
332 AssertMsgBreak(dwDataSize == cbWritten, ("File was not extracted completely! Disk full?\n"));
333
334 } while (0);
335
336 if (RTFileIsValid(fh)) /* fh is unused uninitalized (MSC agrees) */
337 RTFileClose(fh);
338
339 if (RT_FAILURE(rc))
340 {
341 if (bCreatedFile)
342 RTFileDelete(pszTempFile);
343 }
344
345#else /* This is exactly the same as above, except no bug and better assertion
346 message. Note only the return-success statment is indented, indicating
347 that the whole do-break-while-false approach was totally unnecessary. */
348
349 AssertPtrReturn(pszResourceName, VERR_INVALID_POINTER);
350 AssertPtrReturn(pszTempFile, VERR_INVALID_POINTER);
351
352 /* Read the data of the built-in resource. */
353 PVOID pvData = NULL;
354 DWORD dwDataSize = 0;
355 int rc = FindData(pszResourceName, &pvData, &dwDataSize);
356 AssertMsgRCReturn(rc, ("Could not read resource data: %Rrc\n", rc), rc);
357
358 /* Create new (and replace an old) file. */
359 RTFILE hFile;
360 rc = RTFileOpen(&hFile, pszTempFile,
361 RTFILE_O_CREATE_REPLACE
362 | RTFILE_O_WRITE
363 | RTFILE_O_DENY_NOT_DELETE
364 | RTFILE_O_DENY_WRITE);
365 AssertMsgRCReturn(rc, ("Could not open '%s' for writing: %Rrc\n", pszTempFile, rc), rc);
366
367 /* Write contents to new file. */
368 size_t cbWritten = 0;
369 rc = RTFileWrite(hFile, pvData, dwDataSize, &cbWritten);
370 AssertMsgStmt(cbWritten == dwDataSize || RT_FAILURE_NP(rc), ("%#zx vs %#x\n", cbWritten, dwDataSize), rc = VERR_WRITE_ERROR);
371
372 int rc2 = RTFileClose(hFile);
373 AssertRC(rc2);
374
375 if (RT_SUCCESS(rc))
376 return VINF_SUCCESS;
377
378 RTFileDelete(pszTempFile);
379
380#endif
381 return rc;
382}
383
384
385/**
386 * Extracts a built-in resource to disk.
387 *
388 * @returns iprt status code.
389 *
390 * @param pPackage Pointer to a VBOXSTUBPKG struct that contains the resource.
391 * @param pszTempFile The full file path + name to extract the resource to.
392 */
393static int Extract(const PVBOXSTUBPKG pPackage,
394 const char *pszTempFile)
395{
396 return ExtractFile(pPackage->szResourceName, pszTempFile);
397}
398
399
400/**
401 * Detects whether we're running on a 32- or 64-bit platform and returns the result.
402 *
403 * @returns TRUE if we're running on a 64-bit OS, FALSE if not.
404 */
405static BOOL IsWow64(void)
406{
407 BOOL fIsWow64 = TRUE;
408 fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandle(TEXT("kernel32")), "IsWow64Process");
409 if (NULL != fnIsWow64Process)
410 {
411 if (!fnIsWow64Process(GetCurrentProcess(), &fIsWow64))
412 {
413 /* Error in retrieving process type - assume that we're running on 32bit. */
414 return FALSE;
415 }
416 }
417 return fIsWow64;
418}
419
420
421/**
422 * Decides whether we need a specified package to handle or not.
423 *
424 * @returns @c true if we need to handle the specified package, @c false if not.
425 *
426 * @param pPackage Pointer to a VBOXSTUBPKG struct that contains the resource.
427 */
428static bool PackageIsNeeded(PVBOXSTUBPKG pPackage)
429{
430 if (pPackage->byArch == VBOXSTUBPKGARCH_ALL)
431 return true;
432 VBOXSTUBPKGARCH enmArch = IsWow64() ? VBOXSTUBPKGARCH_AMD64 : VBOXSTUBPKGARCH_X86;
433 return pPackage->byArch == enmArch;
434}
435
436
437/**
438 * Adds a cleanup record.
439 *
440 * @returns Fully complained boolean success indicator.
441 * @param pszPath The path to the file or directory to clean up.
442 * @param fFile @c true if file, @c false if directory.
443 */
444static bool AddCleanupRec(const char *pszPath, bool fFile)
445{
446 size_t cchPath = strlen(pszPath); Assert(cchPath > 0);
447 PSTUBCLEANUPREC pRec = (PSTUBCLEANUPREC)RTMemAlloc(RT_UOFFSETOF_DYN(STUBCLEANUPREC, szPath[cchPath + 1]));
448 if (!pRec)
449 {
450 ShowError("Out of memory!");
451 return false;
452 }
453 pRec->fFile = fFile;
454 memcpy(pRec->szPath, pszPath, cchPath + 1);
455
456 RTListPrepend(&g_TmpFiles, &pRec->ListEntry);
457 return true;
458}
459
460
461/**
462 * Cleans up all the extracted files and optionally removes the package
463 * directory.
464 *
465 * @param pszPkgDir The package directory, NULL if it shouldn't be
466 * removed.
467 */
468static void CleanUp(const char *pszPkgDir)
469{
470 for (int i = 0; i < 5; i++)
471 {
472 int rc;
473 bool fFinalTry = i == 4;
474
475 PSTUBCLEANUPREC pCur, pNext;
476 RTListForEachSafe(&g_TmpFiles, pCur, pNext, STUBCLEANUPREC, ListEntry)
477 {
478 if (pCur->fFile)
479 rc = RTFileDelete(pCur->szPath);
480 else
481 {
482 rc = RTDirRemoveRecursive(pCur->szPath, RTDIRRMREC_F_CONTENT_AND_DIR);
483 if (rc == VERR_DIR_NOT_EMPTY && fFinalTry)
484 rc = VINF_SUCCESS;
485 }
486 if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND)
487 rc = VINF_SUCCESS;
488 if (RT_SUCCESS(rc))
489 {
490 RTListNodeRemove(&pCur->ListEntry);
491 RTMemFree(pCur);
492 }
493 else if (fFinalTry)
494 {
495 if (pCur->fFile)
496 ShowError("Failed to delete temporary file '%s': %Rrc", pCur->szPath, rc);
497 else
498 ShowError("Failed to delete temporary directory '%s': %Rrc", pCur->szPath, rc);
499 }
500 }
501
502 if (RTListIsEmpty(&g_TmpFiles) || fFinalTry)
503 {
504 if (!pszPkgDir)
505 return;
506 rc = RTDirRemove(pszPkgDir);
507 if (RT_SUCCESS(rc) || rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND || fFinalTry)
508 return;
509 }
510
511 /* Delay a little and try again. */
512 RTThreadSleep(i == 0 ? 100 : 3000);
513 }
514}
515
516
517/**
518 * Processes an MSI package.
519 *
520 * @returns Fully complained exit code.
521 * @param pszMsi The path to the MSI to process.
522 * @param pszMsiArgs Any additional installer (MSI) argument
523 * @param fLogging Whether to enable installer logging.
524 */
525static RTEXITCODE ProcessMsiPackage(const char *pszMsi, const char *pszMsiArgs, bool fLogging)
526{
527 int rc;
528
529 /*
530 * Set UI level.
531 */
532 INSTALLUILEVEL enmDesiredUiLevel = g_fSilent ? INSTALLUILEVEL_NONE : INSTALLUILEVEL_FULL;
533 INSTALLUILEVEL enmRet = MsiSetInternalUI(enmDesiredUiLevel, NULL);
534 if (enmRet == INSTALLUILEVEL_NOCHANGE /* means error */)
535 return ShowError("Internal error: MsiSetInternalUI failed.");
536
537 /*
538 * Enable logging?
539 */
540 if (fLogging)
541 {
542 char szLogFile[RTPATH_MAX];
543 rc = RTStrCopy(szLogFile, sizeof(szLogFile), pszMsi);
544 if (RT_SUCCESS(rc))
545 {
546 RTPathStripFilename(szLogFile);
547 rc = RTPathAppend(szLogFile, sizeof(szLogFile), "VBoxInstallLog.txt");
548 }
549 if (RT_FAILURE(rc))
550 return ShowError("Internal error: Filename path too long.");
551
552 PRTUTF16 pwszLogFile;
553 rc = RTStrToUtf16(szLogFile, &pwszLogFile);
554 if (RT_FAILURE(rc))
555 return ShowError("RTStrToUtf16 failed on '%s': %Rrc", szLogFile, rc);
556
557 UINT uLogLevel = MsiEnableLogW(INSTALLLOGMODE_VERBOSE,
558 pwszLogFile,
559 INSTALLLOGATTRIBUTES_FLUSHEACHLINE);
560 RTUtf16Free(pwszLogFile);
561 if (uLogLevel != ERROR_SUCCESS)
562 return ShowError("MsiEnableLogW failed");
563 }
564
565 /*
566 * Initialize the common controls (extended version). This is necessary to
567 * run the actual .MSI installers with the new fancy visual control
568 * styles (XP+). Also, an integrated manifest is required.
569 */
570 INITCOMMONCONTROLSEX ccEx;
571 ccEx.dwSize = sizeof(INITCOMMONCONTROLSEX);
572 ccEx.dwICC = ICC_LINK_CLASS | ICC_LISTVIEW_CLASSES | ICC_PAGESCROLLER_CLASS |
573 ICC_PROGRESS_CLASS | ICC_STANDARD_CLASSES | ICC_TAB_CLASSES | ICC_TREEVIEW_CLASSES |
574 ICC_UPDOWN_CLASS | ICC_USEREX_CLASSES | ICC_WIN95_CLASSES;
575 InitCommonControlsEx(&ccEx); /* Ignore failure. */
576
577 /*
578 * Convert both strings to UTF-16 and start the installation.
579 */
580 PRTUTF16 pwszMsi;
581 rc = RTStrToUtf16(pszMsi, &pwszMsi);
582 if (RT_FAILURE(rc))
583 return ShowError("RTStrToUtf16 failed on '%s': %Rrc", pszMsi, rc);
584 PRTUTF16 pwszMsiArgs;
585 rc = RTStrToUtf16(pszMsiArgs, &pwszMsiArgs);
586 if (RT_FAILURE(rc))
587 {
588 RTUtf16Free(pwszMsi);
589 return ShowError("RTStrToUtf16 failed on '%s': %Rrc", pszMsi, rc);
590 }
591
592 UINT uStatus = MsiInstallProductW(pwszMsi, pwszMsiArgs);
593 RTUtf16Free(pwszMsi);
594 RTUtf16Free(pwszMsiArgs);
595
596 if (uStatus == ERROR_SUCCESS)
597 return RTEXITCODE_SUCCESS;
598 if (uStatus == ERROR_SUCCESS_REBOOT_REQUIRED)
599 {
600 if (g_fSilent)
601 RTMsgInfo("Reboot required (by %s)\n", pszMsi);
602 return (RTEXITCODE)uStatus;
603 }
604
605 /*
606 * Installation failed. Figure out what to say.
607 */
608 switch (uStatus)
609 {
610 case ERROR_INSTALL_USEREXIT:
611 /* Don't say anything? */
612 break;
613
614 case ERROR_INSTALL_PACKAGE_VERSION:
615 ShowError("This installation package cannot be installed by the Windows Installer service.\n"
616 "You must install a Windows service pack that contains a newer version of the Windows Installer service.");
617 break;
618
619 case ERROR_INSTALL_PLATFORM_UNSUPPORTED:
620 ShowError("This installation package is not supported on this platform.");
621 break;
622
623 default:
624 {
625 /*
626 * Try get windows to format the message.
627 */
628 DWORD dwFormatFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER
629 | FORMAT_MESSAGE_IGNORE_INSERTS
630 | FORMAT_MESSAGE_FROM_SYSTEM;
631 HMODULE hModule = NULL;
632 if (uStatus >= NERR_BASE && uStatus <= MAX_NERR)
633 {
634 hModule = LoadLibraryExW(L"netmsg.dll",
635 NULL,
636 LOAD_LIBRARY_AS_DATAFILE);
637 if (hModule != NULL)
638 dwFormatFlags |= FORMAT_MESSAGE_FROM_HMODULE;
639 }
640
641 PWSTR pwszMsg;
642 if (FormatMessageW(dwFormatFlags,
643 hModule, /* If NULL, load system stuff. */
644 uStatus,
645 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
646 (PWSTR)&pwszMsg,
647 0,
648 NULL) > 0)
649 {
650 ShowError("Installation failed! Error: %ls", pwszMsg);
651 LocalFree(pwszMsg);
652 }
653 else /* If text lookup failed, show at least the error number. */
654 ShowError("Installation failed! Error: %u", uStatus);
655
656 if (hModule)
657 FreeLibrary(hModule);
658 break;
659 }
660 }
661
662 return RTEXITCODE_FAILURE;
663}
664
665
666/**
667 * Processes a package.
668 *
669 * @returns Fully complained exit code.
670 * @param iPackage The package number.
671 * @param pszPkgDir The package directory (aka extraction dir).
672 * @param pszMsiArgs Any additional installer (MSI) argument
673 * @param fLogging Whether to enable installer logging.
674 */
675static RTEXITCODE ProcessPackage(unsigned iPackage, const char *pszPkgDir, const char *pszMsiArgs, bool fLogging)
676{
677 /*
678 * Get the package header and check if it's needed.
679 */
680 PVBOXSTUBPKG pPackage = FindPackageHeader(iPackage);
681 if (pPackage == NULL)
682 return RTEXITCODE_FAILURE;
683
684 if (!PackageIsNeeded(pPackage))
685 return RTEXITCODE_SUCCESS;
686
687 /*
688 * Deal with the file based on it's extension.
689 */
690 char szPkgFile[RTPATH_MAX];
691 int rc = RTPathJoin(szPkgFile, sizeof(szPkgFile), pszPkgDir, pPackage->szFileName);
692 if (RT_FAILURE(rc))
693 return ShowError("Internal error: RTPathJoin failed: %Rrc", rc);
694 RTPathChangeToDosSlashes(szPkgFile, true /* Force conversion. */); /* paranoia */
695
696 RTEXITCODE rcExit;
697 const char *pszSuff = RTPathSuffix(szPkgFile);
698 if (RTStrICmp(pszSuff, ".msi") == 0)
699 rcExit = ProcessMsiPackage(szPkgFile, pszMsiArgs, fLogging);
700 else if (RTStrICmp(pszSuff, ".cab") == 0)
701 rcExit = RTEXITCODE_SUCCESS; /* Ignore .cab files, they're generally referenced by other files. */
702 else
703 rcExit = ShowError("Internal error: Do not know how to handle file '%s'.", pPackage->szFileName);
704
705 return rcExit;
706}
707
708
709#ifdef VBOX_WITH_CODE_SIGNING
710/**
711 * Install the public certificate into TrustedPublishers so the installer won't
712 * prompt the user during silent installs.
713 *
714 * @returns Fully complained exit code.
715 */
716static RTEXITCODE InstallCertificates(void)
717{
718 for (uint32_t i = 0; i < RT_ELEMENTS(g_aVBoxStubTrustedCerts); i++)
719 {
720 if (!addCertToStore(CERT_SYSTEM_STORE_LOCAL_MACHINE,
721 "TrustedPublisher",
722 g_aVBoxStubTrustedCerts[i].pab,
723 g_aVBoxStubTrustedCerts[i].cb))
724 return ShowError("Failed to construct install certificate.");
725 }
726 return RTEXITCODE_SUCCESS;
727}
728#endif /* VBOX_WITH_CODE_SIGNING */
729
730
731/**
732 * Copies the "<exepath>.custom" directory to the extraction path if it exists.
733 *
734 * This is used by the MSI packages from the resource section.
735 *
736 * @returns Fully complained exit code.
737 * @param pszDstDir The destination directory.
738 */
739static RTEXITCODE CopyCustomDir(const char *pszDstDir)
740{
741 char szSrcDir[RTPATH_MAX];
742 int rc = RTPathExecDir(szSrcDir, sizeof(szSrcDir));
743 if (RT_SUCCESS(rc))
744 rc = RTPathAppend(szSrcDir, sizeof(szSrcDir), ".custom");
745 if (RT_FAILURE(rc))
746 return ShowError("Failed to construct '.custom' dir path: %Rrc", rc);
747
748 if (RTDirExists(szSrcDir))
749 {
750 /*
751 * Use SHFileOperation w/ FO_COPY to do the job. This API requires an
752 * extra zero at the end of both source and destination paths.
753 */
754 size_t cwc;
755 RTUTF16 wszSrcDir[RTPATH_MAX + 1];
756 PRTUTF16 pwszSrcDir = wszSrcDir;
757 rc = RTStrToUtf16Ex(szSrcDir, RTSTR_MAX, &pwszSrcDir, RTPATH_MAX, &cwc);
758 if (RT_FAILURE(rc))
759 return ShowError("RTStrToUtf16Ex failed on '%s': %Rrc", szSrcDir, rc);
760 wszSrcDir[cwc] = '\0';
761
762 RTUTF16 wszDstDir[RTPATH_MAX + 1];
763 PRTUTF16 pwszDstDir = wszSrcDir;
764 rc = RTStrToUtf16Ex(pszDstDir, RTSTR_MAX, &pwszDstDir, RTPATH_MAX, &cwc);
765 if (RT_FAILURE(rc))
766 return ShowError("RTStrToUtf16Ex failed on '%s': %Rrc", pszDstDir, rc);
767 wszDstDir[cwc] = '\0';
768
769 SHFILEOPSTRUCTW FileOp;
770 RT_ZERO(FileOp); /* paranoia */
771 FileOp.hwnd = NULL;
772 FileOp.wFunc = FO_COPY;
773 FileOp.pFrom = wszSrcDir;
774 FileOp.pTo = wszDstDir;
775 FileOp.fFlags = FOF_SILENT
776 | FOF_NOCONFIRMATION
777 | FOF_NOCONFIRMMKDIR
778 | FOF_NOERRORUI;
779 FileOp.fAnyOperationsAborted = FALSE;
780 FileOp.hNameMappings = NULL;
781 FileOp.lpszProgressTitle = NULL;
782
783 rc = SHFileOperationW(&FileOp);
784 if (rc != 0) /* Not a Win32 status code! */
785 return ShowError("Copying the '.custom' dir failed: %#x", rc);
786
787 /*
788 * Add a cleanup record for recursively deleting the destination
789 * .custom directory. We should actually add this prior to calling
790 * SHFileOperationW since it may partially succeed...
791 */
792 char *pszDstSubDir = RTPathJoinA(pszDstDir, ".custom");
793 if (!pszDstSubDir)
794 return ShowError("Out of memory!");
795 bool fRc = AddCleanupRec(pszDstSubDir, false /*fFile*/);
796 RTStrFree(pszDstSubDir);
797 if (!fRc)
798 return RTEXITCODE_FAILURE;
799 }
800
801 return RTEXITCODE_SUCCESS;
802}
803
804
805static RTEXITCODE ExtractFiles(unsigned cPackages, const char *pszDstDir, bool fExtractOnly, bool *pfCreatedExtractDir)
806{
807 int rc;
808
809 /*
810 * Make sure the directory exists.
811 */
812 *pfCreatedExtractDir = false;
813 if (!RTDirExists(pszDstDir))
814 {
815 rc = RTDirCreate(pszDstDir, 0700, 0);
816 if (RT_FAILURE(rc))
817 return ShowError("Failed to create extraction path '%s': %Rrc", pszDstDir, rc);
818 *pfCreatedExtractDir = true;
819 }
820
821 /*
822 * Extract files.
823 */
824 for (unsigned k = 0; k < cPackages; k++)
825 {
826 PVBOXSTUBPKG pPackage = FindPackageHeader(k);
827 if (!pPackage)
828 return RTEXITCODE_FAILURE; /* Done complaining already. */
829
830 if (fExtractOnly || PackageIsNeeded(pPackage))
831 {
832 char szDstFile[RTPATH_MAX];
833 rc = RTPathJoin(szDstFile, sizeof(szDstFile), pszDstDir, pPackage->szFileName);
834 if (RT_FAILURE(rc))
835 return ShowError("Internal error: RTPathJoin failed: %Rrc", rc);
836
837 rc = Extract(pPackage, szDstFile);
838 if (RT_FAILURE(rc))
839 return ShowError("Error extracting package #%u: %Rrc", k, rc);
840
841 if (!fExtractOnly && !AddCleanupRec(szDstFile, true /*fFile*/))
842 {
843 RTFileDelete(szDstFile);
844 return RTEXITCODE_FAILURE;
845 }
846 }
847 }
848
849 return RTEXITCODE_SUCCESS;
850}
851
852
853int WINAPI WinMain(HINSTANCE hInstance,
854 HINSTANCE hPrevInstance,
855 char *lpCmdLine,
856 int nCmdShow)
857{
858 RT_NOREF(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
859 char **argv = __argv;
860 int argc = __argc;
861
862 /*
863 * Init IPRT. This is _always_ the very first thing we do.
864 */
865 int vrc = RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_STANDALONE_APP);
866 if (RT_FAILURE(vrc))
867 return RTMsgInitFailure(vrc);
868
869 /*
870 * Check if we're already running and jump out if so.
871 *
872 * Note! Do not use a global namespace ("Global\\") for mutex name here,
873 * will blow up NT4 compatibility!
874 */
875 HANDLE hMutexAppRunning = CreateMutex(NULL, FALSE, "VBoxStubInstaller");
876 if ( hMutexAppRunning != NULL
877 && GetLastError() == ERROR_ALREADY_EXISTS)
878 {
879 /* Close the mutex for this application instance. */
880 CloseHandle(hMutexAppRunning);
881 hMutexAppRunning = NULL;
882 return RTEXITCODE_FAILURE;
883 }
884
885 /*
886 * Parse arguments.
887 */
888
889 /* Parameter variables. */
890 bool fExtractOnly = false;
891 bool fEnableLogging = false;
892#ifdef VBOX_WITH_CODE_SIGNING
893 bool fEnableSilentCert = true;
894#endif
895 bool fIgnoreReboot = false;
896 char szExtractPath[RTPATH_MAX] = {0};
897 char szMSIArgs[_4K] = {0};
898
899 /* Parameter definitions. */
900 static const RTGETOPTDEF s_aOptions[] =
901 {
902 /** @todo Replace short parameters with enums since they're not
903 * used (and not documented to the public). */
904 { "--extract", 'x', RTGETOPT_REQ_NOTHING },
905 { "-extract", 'x', RTGETOPT_REQ_NOTHING },
906 { "/extract", 'x', RTGETOPT_REQ_NOTHING },
907 { "--silent", 's', RTGETOPT_REQ_NOTHING },
908 { "-silent", 's', RTGETOPT_REQ_NOTHING },
909 { "/silent", 's', RTGETOPT_REQ_NOTHING },
910#ifdef VBOX_WITH_CODE_SIGNING
911 { "--no-silent-cert", 'c', RTGETOPT_REQ_NOTHING },
912 { "-no-silent-cert", 'c', RTGETOPT_REQ_NOTHING },
913 { "/no-silent-cert", 'c', RTGETOPT_REQ_NOTHING },
914#endif
915 { "--logging", 'l', RTGETOPT_REQ_NOTHING },
916 { "-logging", 'l', RTGETOPT_REQ_NOTHING },
917 { "/logging", 'l', RTGETOPT_REQ_NOTHING },
918 { "--path", 'p', RTGETOPT_REQ_STRING },
919 { "-path", 'p', RTGETOPT_REQ_STRING },
920 { "/path", 'p', RTGETOPT_REQ_STRING },
921 { "--msiparams", 'm', RTGETOPT_REQ_STRING },
922 { "-msiparams", 'm', RTGETOPT_REQ_STRING },
923 { "--msi-prop", 'P', RTGETOPT_REQ_STRING },
924 { "--reinstall", 'f', RTGETOPT_REQ_NOTHING },
925 { "-reinstall", 'f', RTGETOPT_REQ_NOTHING },
926 { "/reinstall", 'f', RTGETOPT_REQ_NOTHING },
927 { "--ignore-reboot", 'r', RTGETOPT_REQ_NOTHING },
928 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
929 { "-verbose", 'v', RTGETOPT_REQ_NOTHING },
930 { "/verbose", 'v', RTGETOPT_REQ_NOTHING },
931 { "--version", 'V', RTGETOPT_REQ_NOTHING },
932 { "-version", 'V', RTGETOPT_REQ_NOTHING },
933 { "/version", 'V', RTGETOPT_REQ_NOTHING },
934 { "--help", 'h', RTGETOPT_REQ_NOTHING },
935 { "-help", 'h', RTGETOPT_REQ_NOTHING },
936 { "/help", 'h', RTGETOPT_REQ_NOTHING },
937 { "/?", 'h', RTGETOPT_REQ_NOTHING },
938 };
939
940 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
941
942 /* Parse the parameters. */
943 int ch;
944 bool fExitEarly = false;
945 RTGETOPTUNION ValueUnion;
946 RTGETOPTSTATE GetState;
947 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
948 while ( (ch = RTGetOpt(&GetState, &ValueUnion))
949 && rcExit == RTEXITCODE_SUCCESS
950 && !fExitEarly)
951 {
952 switch (ch)
953 {
954 case 'f': /* Force re-installation. */
955 if (szMSIArgs[0])
956 vrc = RTStrCat(szMSIArgs, sizeof(szMSIArgs), " ");
957 if (RT_SUCCESS(vrc))
958 vrc = RTStrCat(szMSIArgs, sizeof(szMSIArgs),
959 "REINSTALLMODE=vomus REINSTALL=ALL");
960 if (RT_FAILURE(vrc))
961 rcExit = ShowSyntaxError("Out of space for MSI parameters and properties");
962 break;
963
964 case 'x':
965 fExtractOnly = true;
966 break;
967
968 case 's':
969 g_fSilent = true;
970 break;
971
972#ifdef VBOX_WITH_CODE_SIGNING
973 case 'c':
974 fEnableSilentCert = false;
975 break;
976#endif
977 case 'l':
978 fEnableLogging = true;
979 break;
980
981 case 'p':
982 vrc = RTStrCopy(szExtractPath, sizeof(szExtractPath), ValueUnion.psz);
983 if (RT_FAILURE(vrc))
984 rcExit = ShowSyntaxError("Extraction path is too long.");
985 break;
986
987 case 'm':
988 if (szMSIArgs[0])
989 vrc = RTStrCat(szMSIArgs, sizeof(szMSIArgs), " ");
990 if (RT_SUCCESS(vrc))
991 vrc = RTStrCat(szMSIArgs, sizeof(szMSIArgs), ValueUnion.psz);
992 if (RT_FAILURE(vrc))
993 rcExit = ShowSyntaxError("Out of space for MSI parameters and properties");
994 break;
995
996 case 'P':
997 {
998 const char *pszProp = ValueUnion.psz;
999 if (strpbrk(pszProp, " \t\n\r") == NULL)
1000 {
1001 vrc = RTGetOptFetchValue(&GetState, &ValueUnion, RTGETOPT_REQ_STRING);
1002 if (RT_SUCCESS(vrc))
1003 {
1004 size_t cchMsiArgs = strlen(szMSIArgs);
1005 if (RTStrPrintf2(&szMSIArgs[cchMsiArgs], sizeof(szMSIArgs) - cchMsiArgs,
1006 strpbrk(ValueUnion.psz, " \t\n\r") == NULL ? "%s%s=%s" : "%s%s=\"%s\"",
1007 cchMsiArgs ? " " : "", pszProp, ValueUnion.psz) <= 1)
1008 rcExit = ShowSyntaxError("Out of space for MSI parameters and properties");
1009 }
1010 else if (vrc == VERR_GETOPT_REQUIRED_ARGUMENT_MISSING)
1011 rcExit = ShowSyntaxError("--msi-prop takes two arguments, the 2nd is missing");
1012 else
1013 rcExit = ShowSyntaxError("Failed to get 2nd --msi-prop argument: %Rrc", vrc);
1014 }
1015 else
1016 rcExit = ShowSyntaxError("The first argument to --msi-prop must not contain spaces: %s", pszProp);
1017 break;
1018 }
1019
1020 case 'r':
1021 fIgnoreReboot = true;
1022 break;
1023
1024 case 'V':
1025 ShowInfo("Version: %u.%u.%ur%u", VBOX_VERSION_MAJOR, VBOX_VERSION_MINOR, VBOX_VERSION_BUILD, VBOX_SVN_REV);
1026 fExitEarly = true;
1027 break;
1028
1029 case 'v':
1030 g_iVerbosity++;
1031 break;
1032
1033 case 'h':
1034 ShowInfo("-- %s v%u.%u.%ur%u --\n"
1035 "\n"
1036 "Command Line Parameters:\n\n"
1037 "--extract\n"
1038 " Extract file contents to temporary directory\n"
1039 "--logging\n"
1040 " Enables installer logging\n"
1041 "--msiparams <parameters>\n"
1042 " Specifies extra parameters for the MSI installers\n"
1043 " double quoted arguments must be doubled and put\n"
1044 " in quotes: --msiparams \"PROP=\"\"a b c\"\"\"\n"
1045 "--msi-prop <prop> <value>\n"
1046 " Adds <prop>=<value> to the MSI parameters,\n"
1047 " quoting the property value if necessary\n"
1048 "--no-silent-cert\n"
1049 " Do not install VirtualBox Certificate automatically\n"
1050 " when --silent option is specified\n"
1051 "--path\n"
1052 " Sets the path of the extraction directory\n"
1053 "--reinstall\n"
1054 " Forces VirtualBox to get re-installed\n"
1055 "--ignore-reboot\n"
1056 " Do not set exit code to 3010 if a reboot is required\n"
1057 "--silent\n"
1058 " Enables silent mode installation\n"
1059 "--version\n"
1060 " Displays version number and exit\n"
1061 "-?, -h, --help\n"
1062 " Displays this help text and exit\n"
1063 "\n"
1064 "Examples:\n"
1065 " %s --msiparams \"INSTALLDIR=\"\"C:\\Program Files\\VirtualBox\"\"\"\n"
1066 " %s --extract -path C:\\VBox",
1067 VBOX_STUB_TITLE, VBOX_VERSION_MAJOR, VBOX_VERSION_MINOR, VBOX_VERSION_BUILD, VBOX_SVN_REV,
1068 argv[0], argv[0]);
1069 fExitEarly = true;
1070 break;
1071
1072 case VINF_GETOPT_NOT_OPTION:
1073 /* Are (optional) MSI parameters specified and this is the last
1074 * parameter? Append everything to the MSI parameter list then. */
1075 /** @todo r=bird: this makes zero sense */
1076 if (szMSIArgs[0])
1077 {
1078 vrc = RTStrCat(szMSIArgs, sizeof(szMSIArgs), " ");
1079 if (RT_SUCCESS(vrc))
1080 vrc = RTStrCat(szMSIArgs, sizeof(szMSIArgs), ValueUnion.psz);
1081 if (RT_FAILURE(vrc))
1082 rcExit = ShowSyntaxError("Out of space for MSI parameters and properties");
1083 continue;
1084 }
1085 /* Fall through is intentional. */
1086
1087 default:
1088 if (g_fSilent)
1089 rcExit = RTGetOptPrintError(ch, &ValueUnion);
1090 if (ch == VERR_GETOPT_UNKNOWN_OPTION)
1091 rcExit = ShowSyntaxError("Unknown option \"%s\"\n"
1092 "Please refer to the command line help by specifying \"-?\"\n"
1093 "to get more information.", ValueUnion.psz);
1094 else
1095 rcExit = ShowSyntaxError("Parameter parsing error: %Rrc\n"
1096 "Please refer to the command line help by specifying \"-?\"\n"
1097 "to get more information.", ch);
1098 break;
1099 }
1100 }
1101
1102 /* Check if we can bail out early. */
1103 if (fExitEarly)
1104 return rcExit;
1105
1106 if (rcExit != RTEXITCODE_SUCCESS)
1107 vrc = VERR_PARSE_ERROR;
1108
1109/** @todo
1110 *
1111 * Split the remainder up in functions and simplify the code flow!!
1112 *
1113 * */
1114
1115#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0501
1116# ifdef VBOX_STUB_WITH_OWN_CONSOLE /* Use an own console window if run in debug mode. */
1117 if ( RT_SUCCESS(vrc)
1118 && g_iVerbosity)
1119 {
1120 if (!AllocConsole())
1121 {
1122 DWORD dwErr = GetLastError();
1123 ShowError("Unable to allocate console, error = %ld\n",
1124 dwErr);
1125
1126 /* Close the mutex for this application instance. */
1127 CloseHandle(hMutexAppRunning);
1128 hMutexAppRunning = NULL;
1129 return RTEXITCODE_FAILURE;
1130 }
1131
1132 freopen("CONOUT$", "w", stdout);
1133 setvbuf(stdout, NULL, _IONBF, 0);
1134
1135 freopen("CONOUT$", "w", stderr);
1136 }
1137# endif /* VBOX_STUB_WITH_OWN_CONSOLE */
1138#endif
1139
1140 if ( RT_SUCCESS(vrc)
1141 && g_iVerbosity)
1142 {
1143 RTPrintf("Silent installation : %RTbool\n", g_fSilent);
1144 RTPrintf("Logging enabled : %RTbool\n", fEnableLogging);
1145#ifdef VBOX_WITH_CODE_SIGNING
1146 RTPrintf("Certificate installation : %RTbool\n", fEnableSilentCert);
1147#endif
1148 RTPrintf("Additional MSI parameters: %s\n",
1149 szMSIArgs[0] ? szMSIArgs : "<None>");
1150 }
1151
1152 /*
1153 * 32-bit is not officially supported any more.
1154 */
1155 if ( !fExtractOnly
1156 && !g_fSilent
1157 && !IsWow64())
1158 {
1159 rcExit = ShowError("32-bit Windows hosts are not supported by this VirtualBox release.");
1160 vrc = VERR_NOT_SUPPORTED;
1161 }
1162
1163 if (RT_SUCCESS(vrc))
1164 {
1165 /*
1166 * Determine the extration path if not given by the user, and gather some
1167 * other bits we'll be needing later.
1168 */
1169 if (szExtractPath[0] == '\0')
1170 {
1171 vrc = RTPathTemp(szExtractPath, sizeof(szExtractPath));
1172 if (RT_SUCCESS(vrc))
1173 vrc = RTPathAppend(szExtractPath, sizeof(szExtractPath), "VirtualBox");
1174 if (RT_FAILURE(vrc))
1175 ShowError("Failed to determine extraction path (%Rrc)", vrc);
1176
1177 }
1178 else
1179 {
1180 /** @todo should check if there is a .custom subdirectory there or not. */
1181 }
1182 RTPathChangeToDosSlashes(szExtractPath,
1183 true /* Force conversion. */); /* MSI requirement. */
1184 }
1185
1186 /* Read our manifest. */
1187 if (RT_SUCCESS(vrc))
1188 {
1189 PVBOXSTUBPKGHEADER pHeader;
1190 vrc = FindData("MANIFEST", (PVOID *)&pHeader, NULL);
1191 if (RT_SUCCESS(vrc))
1192 {
1193 /** @todo If we could, we should validate the header. Only the magic isn't
1194 * commonly defined, nor the version number... */
1195
1196 RTListInit(&g_TmpFiles);
1197
1198 /*
1199 * Up to this point, we haven't done anything that requires any cleanup.
1200 * From here on, we do everything in function so we can counter clean up.
1201 */
1202 bool fCreatedExtractDir;
1203 rcExit = ExtractFiles(pHeader->byCntPkgs, szExtractPath,
1204 fExtractOnly, &fCreatedExtractDir);
1205 if (rcExit == RTEXITCODE_SUCCESS)
1206 {
1207 if (fExtractOnly)
1208 ShowInfo("Files were extracted to: %s", szExtractPath);
1209 else
1210 {
1211 rcExit = CopyCustomDir(szExtractPath);
1212#ifdef VBOX_WITH_CODE_SIGNING
1213 if (rcExit == RTEXITCODE_SUCCESS && fEnableSilentCert && g_fSilent)
1214 rcExit = InstallCertificates();
1215#endif
1216 unsigned iPackage = 0;
1217 while ( iPackage < pHeader->byCntPkgs
1218 && (rcExit == RTEXITCODE_SUCCESS || rcExit == (RTEXITCODE)ERROR_SUCCESS_REBOOT_REQUIRED))
1219 {
1220 RTEXITCODE rcExit2 = ProcessPackage(iPackage, szExtractPath, szMSIArgs, fEnableLogging);
1221 if (rcExit2 != RTEXITCODE_SUCCESS)
1222 rcExit = rcExit2;
1223 iPackage++;
1224 }
1225
1226 /* Don't fail if cleanup fail. At least for now. */
1227 CleanUp( !fEnableLogging
1228 && fCreatedExtractDir ? szExtractPath : NULL);
1229 }
1230 }
1231
1232 /* Free any left behind cleanup records (not strictly needed). */
1233 PSTUBCLEANUPREC pCur, pNext;
1234 RTListForEachSafe(&g_TmpFiles, pCur, pNext, STUBCLEANUPREC, ListEntry)
1235 {
1236 RTListNodeRemove(&pCur->ListEntry);
1237 RTMemFree(pCur);
1238 }
1239 }
1240 else
1241 rcExit = ShowError("Internal package error: Manifest not found (%Rrc)", vrc);
1242 }
1243
1244#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0501
1245# ifdef VBOX_STUB_WITH_OWN_CONSOLE
1246 if (g_iVerbosity)
1247 FreeConsole();
1248# endif /* VBOX_STUB_WITH_OWN_CONSOLE */
1249#endif
1250
1251 /*
1252 * Release instance mutex.
1253 */
1254 if (hMutexAppRunning != NULL)
1255 {
1256 CloseHandle(hMutexAppRunning);
1257 hMutexAppRunning = NULL;
1258 }
1259
1260 return rcExit != (RTEXITCODE)ERROR_SUCCESS_REBOOT_REQUIRED || !fIgnoreReboot ? rcExit : RTEXITCODE_SUCCESS;
1261}
1262
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette