VirtualBox

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

Last change on this file since 40894 was 39613, checked in by vboxsync, 13 years ago

build fix

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 24.7 KB
Line 
1/* $Id: VBoxStub.cpp 39613 2011-12-14 15:16:15Z vboxsync $ */
2/** @file
3 * VBoxStub - VirtualBox's Windows installer stub.
4 */
5
6/*
7 * Copyright (C) 2010 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* Header Files *
20*******************************************************************************/
21#include <windows.h>
22#include <commctrl.h>
23#include <lmerr.h>
24#include <msiquery.h>
25#include <objbase.h>
26#include <shlobj.h>
27#include <stdlib.h>
28#include <stdio.h>
29#include <string.h>
30#include <strsafe.h>
31
32#include <VBox/version.h>
33
34#include <iprt/assert.h>
35#include <iprt/dir.h>
36#include <iprt/file.h>
37#include <iprt/initterm.h>
38#include <iprt/mem.h>
39#include <iprt/path.h>
40#include <iprt/param.h>
41#include <iprt/string.h>
42#include <iprt/thread.h>
43
44#include "VBoxStub.h"
45#include "../StubBld/VBoxStubBld.h"
46#include "resource.h"
47
48#ifndef _UNICODE
49#define _UNICODE
50#endif
51
52
53/**
54 * Shows a message box with a printf() style formatted string.
55 *
56 * @returns Message box result (IDOK, IDCANCEL, ...).
57 *
58 * @param uType Type of the message box (see MSDN).
59 * @param pszFmt Printf-style format string to show in the message box body.
60 *
61 */
62static int ShowInfo(const char *pszFmt, ...)
63{
64 char *pszMsg;
65 va_list va;
66
67 va_start(va, pszFmt);
68 RTStrAPrintfV(&pszMsg, pszFmt, va);
69 va_end(va);
70
71 int rc;
72 if (pszMsg)
73 rc = MessageBox(GetDesktopWindow(), pszMsg, VBOX_STUB_TITLE, MB_ICONINFORMATION);
74 else
75 rc = MessageBox(GetDesktopWindow(), pszFmt, VBOX_STUB_TITLE, MB_ICONINFORMATION);
76 RTStrFree(pszMsg);
77 return rc;
78}
79
80
81/**
82 * Shows an error message box with a printf() style formatted string.
83 *
84 * @returns Message box result (IDOK, IDCANCEL, ...).
85 *
86 * @param pszFmt Printf-style format string to show in the message box body.
87 *
88 */
89static int ShowError(const char *pszFmt, ...)
90{
91 char *pszMsg;
92 va_list va;
93 int rc;
94
95 va_start(va, pszFmt);
96 if (RTStrAPrintfV(&pszMsg, pszFmt, va))
97 {
98 rc = MessageBox(GetDesktopWindow(), pszMsg, VBOX_STUB_TITLE, MB_ICONERROR);
99 RTStrFree(pszMsg);
100 }
101 else /* Should never happen! */
102 AssertMsgFailed(("Failed to format error text of format string: %s!\n", pszFmt));
103 va_end(va);
104 return rc;
105}
106
107
108/**
109 * Reads data from a built-in resource.
110 *
111 * @returns iprt status code.
112 *
113 * @param hInst Instance to read the data from.
114 * @param pszDataName Name of resource to read.
115 * @param ppvResource Pointer to buffer which holds the read resource data.
116 * @param pdwSize Pointer which holds the read data size.
117 *
118 */
119static int ReadData(HINSTANCE hInst,
120 const char *pszDataName,
121 PVOID *ppvResource,
122 DWORD *pdwSize)
123{
124 do
125 {
126 AssertMsgBreak(pszDataName, ("Resource name is empty!\n"));
127
128 /* Find our resource. */
129 HRSRC hRsrc = FindResourceEx(hInst, RT_RCDATA, pszDataName, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));
130 AssertMsgBreak(hRsrc, ("Could not find resource!\n"));
131
132 /* Get resource size. */
133 *pdwSize = SizeofResource(hInst, hRsrc);
134 AssertMsgBreak(*pdwSize > 0, ("Size of resource is invalid!\n"));
135
136 /* Get pointer to resource. */
137 HGLOBAL hData = LoadResource(hInst, hRsrc);
138 AssertMsgBreak(hData, ("Could not load resource!\n"));
139
140 /* Lock resource. */
141 *ppvResource = LockResource(hData);
142 AssertMsgBreak(*ppvResource, ("Could not lock resource!\n"));
143 return VINF_SUCCESS;
144
145 } while (0);
146
147 return VERR_IO_GEN_FAILURE;
148}
149
150
151/**
152 * Constructs a full temporary file path from the given parameters.
153 *
154 * @returns iprt status code.
155 *
156 * @param pszTempPath The pure path to use for construction.
157 * @param pszTargetFileName The pure file name to use for construction.
158 * @param ppszTempFile Pointer to the constructed string. Must be freed
159 * using RTStrFree().
160 */
161static int GetTempFileAlloc(const char *pszTempPath,
162 const char *pszTargetFileName,
163 char **ppszTempFile)
164{
165 if (RTStrAPrintf(ppszTempFile, "%s\\%s", pszTempPath, pszTargetFileName) >= 0)
166 return VINF_SUCCESS;
167 return VERR_NO_STR_MEMORY;
168}
169
170
171/**
172 * Extracts a built-in resource to disk.
173 *
174 * @returns iprt status code.
175 *
176 * @param pszResourceName The resource name to extract.
177 * @param pszTempFile The full file path + name to extract the resource to.
178 *
179 */
180static int ExtractFile(const char *pszResourceName,
181 const char *pszTempFile)
182{
183 int rc;
184 RTFILE fh;
185 BOOL bCreatedFile = FALSE;
186
187 do
188 {
189 AssertMsgBreak(pszResourceName, ("Resource pointer invalid!\n"));
190 AssertMsgBreak(pszTempFile, ("Temp file pointer invalid!"));
191
192 /* Read the data of the built-in resource. */
193 PVOID pvData = NULL;
194 DWORD dwDataSize = 0;
195 rc = ReadData(NULL, pszResourceName, &pvData, &dwDataSize);
196 AssertMsgRCBreak(rc, ("Could not read resource data!\n"));
197
198 /* Create new (and replace an old) file. */
199 rc = RTFileOpen(&fh, pszTempFile,
200 RTFILE_O_CREATE_REPLACE
201 | RTFILE_O_WRITE
202 | RTFILE_O_DENY_NOT_DELETE
203 | RTFILE_O_DENY_WRITE);
204 AssertMsgRCBreak(rc, ("Could not open file for writing!\n"));
205 bCreatedFile = TRUE;
206
207 /* Write contents to new file. */
208 size_t cbWritten = 0;
209 rc = RTFileWrite(fh, pvData, dwDataSize, &cbWritten);
210 AssertMsgRCBreak(rc, ("Could not open file for writing!\n"));
211 AssertMsgBreak(dwDataSize == cbWritten, ("File was not extracted completely! Disk full?\n"));
212
213 } while (0);
214
215 if (RTFileIsValid(fh))
216 RTFileClose(fh);
217
218 if (RT_FAILURE(rc))
219 {
220 if (bCreatedFile)
221 RTFileDelete(pszTempFile);
222 }
223 return rc;
224}
225
226
227/**
228 * Extracts a built-in resource to disk.
229 *
230 * @returns iprt status code.
231 *
232 * @param pPackage Pointer to a VBOXSTUBPKG struct that contains the resource.
233 * @param pszTempFile The full file path + name to extract the resource to.
234 *
235 */
236static int Extract(const PVBOXSTUBPKG pPackage,
237 const char *pszTempFile)
238{
239 return ExtractFile(pPackage->szResourceName,
240 pszTempFile);
241}
242
243
244/**
245 * Detects whether we're running on a 32- or 64-bit platform and returns the result.
246 *
247 * @returns TRUE if we're running on a 64-bit OS, FALSE if not.
248 *
249 */
250static BOOL IsWow64(void)
251{
252 BOOL bIsWow64 = TRUE;
253 fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandle(TEXT("kernel32")), "IsWow64Process");
254 if (NULL != fnIsWow64Process)
255 {
256 if (!fnIsWow64Process(GetCurrentProcess(), &bIsWow64))
257 {
258 /* Error in retrieving process type - assume that we're running on 32bit. */
259 return FALSE;
260 }
261 }
262 return bIsWow64;
263}
264
265
266/**
267 * Decides whether we need a specified package to handle or not.
268 *
269 * @returns TRUE if we need to handle the specified package, FALSE if not.
270 *
271 * @param pPackage Pointer to a VBOXSTUBPKG struct that contains the resource.
272 *
273 */
274static BOOL PackageIsNeeded(PVBOXSTUBPKG pPackage)
275{
276 BOOL bIsWow64 = IsWow64();
277 if ((bIsWow64 && pPackage->byArch == VBOXSTUBPKGARCH_AMD64)) /* 64bit Windows. */
278 {
279 return TRUE;
280 }
281 else if ((!bIsWow64 && pPackage->byArch == VBOXSTUBPKGARCH_X86)) /* 32bit. */
282 {
283 return TRUE;
284 }
285 else if (pPackage->byArch == VBOXSTUBPKGARCH_ALL)
286 {
287 return TRUE;
288 }
289 return FALSE;
290}
291
292
293/**
294 * Recursively copies a directory to another location.
295 *
296 * @returns iprt status code.
297 *
298 * @param pszDestDir Location to copy the source directory to.
299 * @param pszSourceDir The source directory to copy.
300 *
301 */
302int CopyDir(const char *pszDestDir, const char *pszSourceDir)
303{
304 char szDest[RTPATH_MAX + 1];
305 char szSource[RTPATH_MAX + 1];
306
307 AssertStmt(pszDestDir, "Destination directory invalid!");
308 AssertStmt(pszSourceDir, "Source directory invalid!");
309
310 SHFILEOPSTRUCT s = {0};
311 if ( RTStrPrintf(szDest, _MAX_PATH, "%s%c", pszDestDir, '\0') > 0
312 && RTStrPrintf(szSource, _MAX_PATH, "%s%c", pszSourceDir, '\0') > 0)
313 {
314 s.hwnd = NULL;
315 s.wFunc = FO_COPY;
316 s.pTo = szDest;
317 s.pFrom = szSource;
318 s.fFlags = FOF_SILENT |
319 FOF_NOCONFIRMATION |
320 FOF_NOCONFIRMMKDIR |
321 FOF_NOERRORUI;
322 }
323 return RTErrConvertFromWin32(SHFileOperation(&s));
324}
325
326
327int WINAPI WinMain(HINSTANCE hInstance,
328 HINSTANCE hPrevInstance,
329 char *lpCmdLine,
330 int nCmdShow)
331{
332 char **argv = __argv;
333 int argc = __argc;
334
335 /* Check if we're already running and jump out if so. */
336 /* Do not use a global namespace ("Global\\") for mutex name here, will blow up NT4 compatibility! */
337 HANDLE hMutexAppRunning = CreateMutex (NULL, FALSE, "VBoxStubInstaller");
338 if ( (hMutexAppRunning != NULL)
339 && (GetLastError() == ERROR_ALREADY_EXISTS))
340 {
341 /* Close the mutex for this application instance. */
342 CloseHandle(hMutexAppRunning);
343 hMutexAppRunning = NULL;
344 return 1;
345 }
346
347 /* Init IPRT. */
348 int vrc = RTR3InitExe(argc, &argv, 0);
349 if (RT_FAILURE(vrc))
350 return vrc;
351
352 BOOL fExtractOnly = FALSE;
353 BOOL fSilent = FALSE;
354 BOOL fEnableLogging = FALSE;
355 BOOL fExit = FALSE;
356
357 /* Temp variables for arguments. */
358 char szExtractPath[RTPATH_MAX] = {0};
359 char szMSIArgs[RTPATH_MAX] = {0};
360
361 /* Process arguments. */
362 for (int i = 0; i < argc; i++)
363 {
364 if ( (0 == RTStrICmp(argv[i], "-x"))
365 || (0 == RTStrICmp(argv[i], "-extract"))
366 || (0 == RTStrICmp(argv[i], "/extract")))
367 {
368 fExtractOnly = TRUE;
369 }
370
371 else if ( (0 == RTStrICmp(argv[i], "-s"))
372 || (0 == RTStrICmp(argv[i], "-silent"))
373 || (0 == RTStrICmp(argv[i], "/silent")))
374 {
375 fSilent = TRUE;
376 }
377
378 else if ( (0 == RTStrICmp(argv[i], "-l"))
379 || (0 == RTStrICmp(argv[i], "-logging"))
380 || (0 == RTStrICmp(argv[i], "/logging")))
381 {
382 fEnableLogging = TRUE;
383 }
384
385 else if (( (0 == RTStrICmp(argv[i], "-p"))
386 || (0 == RTStrICmp(argv[i], "-path"))
387 || (0 == RTStrICmp(argv[i], "/path")))
388 )
389 {
390 if (argc > i)
391 {
392 vrc = ::StringCbCat(szExtractPath, sizeof(szExtractPath), argv[i+1]);
393 i++; /* Avoid the specified path from being parsed. */
394 }
395 else
396 {
397 ShowError("No path for extraction specified!");
398 fExit = TRUE;
399 }
400 }
401
402 else if (( (0 == RTStrICmp(argv[i], "-msiparams"))
403 || (0 == RTStrICmp(argv[i], "/msiparams")))
404 && (argc > i))
405 {
406 for (int a = i + 1; a < argc; a++)
407 {
408 if (a > i+1) /* Insert a space. */
409 vrc = ::StringCbCat(szMSIArgs, sizeof(szMSIArgs), " ");
410
411 vrc = ::StringCbCat(szMSIArgs, sizeof(szMSIArgs), argv[a]);
412 }
413 }
414
415 else if ( (0 == RTStrICmp(argv[i], "-v"))
416 || (0 == RTStrICmp(argv[i], "-version"))
417 || (0 == RTStrICmp(argv[i], "/version")))
418 {
419 ShowInfo("Version: %d.%d.%d.%d",
420 VBOX_VERSION_MAJOR, VBOX_VERSION_MINOR, VBOX_VERSION_BUILD, VBOX_SVN_REV);
421 fExit = TRUE;
422 }
423
424 else if ( (0 == RTStrICmp(argv[i], "-help"))
425 || (0 == RTStrICmp(argv[i], "/help"))
426 || (0 == RTStrICmp(argv[i], "/?")))
427 {
428 ShowInfo("-- %s v%d.%d.%d.%d --\n"
429 "Command Line Parameters:\n\n"
430 "-extract | -x - Extract file contents to temporary directory\n"
431 "-silent | -s - Enables silent mode installation\n"
432 "-path | -p - Sets the path of the extraction directory\n"
433 "-help | /? - Print this help and exit\n"
434 "-msiparams <parameters> - Specifies extra parameters for the MSI installers\n"
435 "-logging | -l - Enables installer logging\n"
436 "-version | -v - Print version number and exit\n\n"
437 "Examples:\n"
438 "%s -msiparams INSTALLDIR=C:\\VBox\n"
439 "%s -extract -path C:\\VBox\n",
440 VBOX_STUB_TITLE, VBOX_VERSION_MAJOR, VBOX_VERSION_MINOR, VBOX_VERSION_BUILD, VBOX_SVN_REV,
441 argv[0], argv[0]);
442 fExit = TRUE;
443 }
444 else
445 {
446 if (i > 0)
447 {
448 ShowError("Unknown option \"%s\"!\n"
449 "Please refer to the command line help by specifying \"/?\"\n"
450 "to get more information.", argv[i]);
451 fExit = TRUE;
452 }
453 }
454 }
455
456 if (fExit)
457 return 0;
458
459 HRESULT hr = S_OK;
460
461 do /* break loop */
462 {
463 /* Get/create our temp path (only if not already set). */
464 if (szExtractPath[0] == '\0')
465 {
466 vrc = RTPathTemp(szExtractPath, sizeof(szExtractPath));
467 AssertMsgRCBreak(vrc, ("Could not retrieve temp directory!\n"));
468
469 vrc = RTPathAppend(szExtractPath, sizeof(szExtractPath), "VirtualBox");
470 AssertMsgRCBreak(vrc, ("Could not construct temp directory!\n"));
471
472 /* Convert slahes; this is necessary for MSI routines later! */
473 RTPathChangeToDosSlashes(szExtractPath, true /* Force conversion. */);
474 }
475 if (!RTDirExists(szExtractPath))
476 {
477 vrc = RTDirCreate(szExtractPath, 0700, 0);
478 AssertMsgRCBreak(vrc, ("Could not create temp directory!\n"));
479 }
480
481 /* Get our executable path */
482 char szPathExe[_MAX_PATH];
483 vrc = RTPathExecDir(szPathExe, sizeof(szPathExe));
484 /** @todo error checking */
485
486 /* Read our manifest. */
487 PVBOXSTUBPKGHEADER pHeader = NULL;
488 DWORD cbHeader = 0;
489 vrc = ReadData(NULL, "MANIFEST", (LPVOID*)&pHeader, &cbHeader);
490 AssertMsgRCBreak(vrc, ("Manifest not found!\n"));
491
492 /* Extract files. */
493 for (BYTE k = 0; k < pHeader->byCntPkgs; k++)
494 {
495 PVBOXSTUBPKG pPackage = NULL;
496 DWORD cbPackage = 0;
497 char szHeaderName[RTPATH_MAX + 1] = {0};
498
499 hr = ::StringCchPrintf(szHeaderName, RTPATH_MAX, "HDR_%02d", k);
500 vrc = ReadData(NULL, szHeaderName, (LPVOID*)&pPackage, &cbPackage);
501 AssertMsgRCBreak(vrc, ("Header not found!\n")); /** @todo include header name, how? */
502
503 if (PackageIsNeeded(pPackage) || fExtractOnly)
504 {
505 char *pszTempFile = NULL;
506 vrc = GetTempFileAlloc(szExtractPath, pPackage->szFileName, &pszTempFile);
507 AssertMsgRCBreak(vrc, ("Could not create name for temporary extracted file!\n"));
508 vrc = Extract(pPackage, pszTempFile);
509 AssertMsgRCBreak(vrc, ("Could not extract file!\n"));
510 RTStrFree(pszTempFile);
511 }
512 }
513
514 if (FALSE == fExtractOnly && !RT_FAILURE(vrc))
515 {
516 /*
517 * Copy ".custom" directory into temp directory so that the extracted .MSI
518 * file(s) can use it.
519 */
520 char *pszPathCustomDir = RTPathJoinA(szPathExe, ".custom");
521 pszPathCustomDir = RTPathChangeToDosSlashes(pszPathCustomDir, true /* Force conversion. */);
522 if (pszPathCustomDir && RTDirExists(pszPathCustomDir))
523 {
524 vrc = CopyDir(szExtractPath, pszPathCustomDir);
525 if (RT_FAILURE(vrc)) /* Don't fail if it's missing! */
526 vrc = VINF_SUCCESS;
527
528 RTStrFree(pszPathCustomDir);
529 }
530
531 /* Do actions on files. */
532 for (BYTE k = 0; k < pHeader->byCntPkgs; k++)
533 {
534 PVBOXSTUBPKG pPackage = NULL;
535 DWORD cbPackage = 0;
536 char szHeaderName[RTPATH_MAX] = {0};
537
538 hr = StringCchPrintf(szHeaderName, RTPATH_MAX, "HDR_%02d", k);
539 vrc = ReadData(NULL, szHeaderName, (LPVOID*)&pPackage, &cbPackage);
540 AssertMsgRCBreak(vrc, ("Package not found!\n"));
541
542 if (PackageIsNeeded(pPackage))
543 {
544 char *pszTempFile = NULL;
545
546 vrc = GetTempFileAlloc(szExtractPath, pPackage->szFileName, &pszTempFile);
547 AssertMsgRCBreak(vrc, ("Could not create name for temporary action file!\n"));
548
549 /* Handle MSI files. */
550 if (RTStrICmp(RTPathExt(pszTempFile), ".msi") == 0)
551 {
552 /* Set UI level. */
553 INSTALLUILEVEL UILevel = MsiSetInternalUI( fSilent
554 ? INSTALLUILEVEL_NONE
555 : INSTALLUILEVEL_FULL,
556 NULL);
557 AssertMsgBreak(UILevel != INSTALLUILEVEL_NOCHANGE, ("Could not set installer UI level!\n"));
558
559 /* Enable logging? */
560 if (fEnableLogging)
561 {
562 char *pszLog = RTPathJoinA(szExtractPath, "VBoxInstallLog.txt");
563 /* Convert slahes; this is necessary for MSI routines! */
564 pszLog = RTPathChangeToDosSlashes(pszLog, true /* Force conversion. */);
565 AssertMsgBreak(pszLog, ("Could not construct path for log file!\n"));
566 UINT uLogLevel = MsiEnableLog(INSTALLLOGMODE_VERBOSE,
567 pszLog, INSTALLLOGATTRIBUTES_FLUSHEACHLINE);
568 RTStrFree(pszLog);
569 AssertMsgBreak(uLogLevel == ERROR_SUCCESS, ("Could not set installer logging level!\n"));
570 }
571
572 /* Initialize the common controls (extended version). This is necessary to
573 * run the actual .MSI installers with the new fancy visual control
574 * styles (XP+). Also, an integrated manifest is required. */
575 INITCOMMONCONTROLSEX ccEx;
576 ccEx.dwSize = sizeof(INITCOMMONCONTROLSEX);
577 ccEx.dwICC = ICC_LINK_CLASS | ICC_LISTVIEW_CLASSES | ICC_PAGESCROLLER_CLASS |
578 ICC_PROGRESS_CLASS | ICC_STANDARD_CLASSES | ICC_TAB_CLASSES | ICC_TREEVIEW_CLASSES |
579 ICC_UPDOWN_CLASS | ICC_USEREX_CLASSES | ICC_WIN95_CLASSES;
580 InitCommonControlsEx(&ccEx); /* Ignore failure. */
581
582 UINT uStatus = ::MsiInstallProductA(pszTempFile, szMSIArgs);
583 if ( (uStatus != ERROR_SUCCESS)
584 && (uStatus != ERROR_SUCCESS_REBOOT_REQUIRED)
585 && (uStatus != ERROR_INSTALL_USEREXIT))
586 {
587 if (!fSilent)
588 {
589 switch (uStatus)
590 {
591 case ERROR_INSTALL_PACKAGE_VERSION:
592
593 ShowError("This installation package cannot be installed by the Windows Installer service.\n"
594 "You must install a Windows service pack that contains a newer version of the Windows Installer service.");
595 break;
596
597 case ERROR_INSTALL_PLATFORM_UNSUPPORTED:
598
599 ShowError("This installation package is not supported on this platform.");
600 break;
601
602 default:
603 {
604 DWORD dwFormatFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER
605 | FORMAT_MESSAGE_IGNORE_INSERTS
606 | FORMAT_MESSAGE_FROM_SYSTEM;
607 HMODULE hModule = NULL;
608 if (uStatus >= NERR_BASE && uStatus <= MAX_NERR)
609 {
610 hModule = LoadLibraryEx(TEXT("netmsg.dll"),
611 NULL,
612 LOAD_LIBRARY_AS_DATAFILE);
613 if (hModule != NULL)
614 dwFormatFlags |= FORMAT_MESSAGE_FROM_HMODULE;
615 }
616
617 DWORD dwBufferLength;
618 LPSTR szMessageBuffer;
619 if (dwBufferLength = FormatMessageA(dwFormatFlags,
620 hModule, /* If NULL, load system stuff. */
621 uStatus,
622 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
623 (LPSTR)&szMessageBuffer,
624 0,
625 NULL))
626 {
627 ShowError("Installation failed! Error: %s", szMessageBuffer);
628 LocalFree(szMessageBuffer);
629 }
630 else /* If text lookup failed, show at least the error number. */
631 ShowError("Installation failed! Error: %u", uStatus);
632 if (hModule)
633 FreeLibrary(hModule);
634 break;
635 }
636 }
637 }
638
639 vrc = VERR_NO_CHANGE; /* No change done to the system. */
640 }
641 }
642 RTStrFree(pszTempFile);
643 } /* Package needed? */
644 } /* For all packages */
645 }
646
647 /* Clean up (only on success - prevent deleting the log). */
648 if ( !fExtractOnly
649 && RT_SUCCESS(vrc))
650 {
651 for (int i=0; i<5; i++)
652 {
653 vrc = RTDirRemoveRecursive(szExtractPath, 0 /*fFlags*/);
654 if (RT_SUCCESS(vrc))
655 break;
656 RTThreadSleep(3000 /* Wait 3 seconds.*/);
657 }
658 }
659
660 } while (0);
661
662 if (RT_SUCCESS(vrc))
663 {
664 if ( fExtractOnly
665 && !fSilent)
666 {
667 ShowInfo("Files were extracted to: %s", szExtractPath);
668 }
669
670 /** @todo Add more post installation stuff here if required. */
671 }
672
673 /* Release instance mutex. */
674 if (hMutexAppRunning != NULL)
675 {
676 CloseHandle(hMutexAppRunning);
677 hMutexAppRunning = NULL;
678 }
679
680 /* Set final exit (return) code (error level). */
681 if (RT_FAILURE(vrc))
682 {
683 switch(vrc)
684 {
685 case VERR_NO_CHANGE:
686 default:
687 vrc = 1;
688 }
689 }
690 else /* Always set to (VINF_SUCCESS), even if we got something else (like a VWRN etc). */
691 vrc = VINF_SUCCESS;
692 return vrc;
693}
694
Note: See TracBrowser for help on using the repository browser.

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