VirtualBox

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

Last change on this file since 31659 was 31659, checked in by vboxsync, 15 years ago

export Windows installer to OSE

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