VirtualBox

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

Last change on this file since 45317 was 45317, checked in by vboxsync, 12 years ago

ARG - installer stub exits successfully on failure. -> windows box

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