VirtualBox

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

Last change on this file since 33540 was 33540, checked in by vboxsync, 14 years ago

*: spelling fixes, thanks Timeless!

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