Changeset 45332 in vbox for trunk/src/VBox/Installer/win/Stub
- Timestamp:
- Apr 4, 2013 9:43:25 AM (12 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Installer/win/Stub/VBoxStub.cpp
r45320 r45332 19 19 * Header Files * 20 20 *******************************************************************************/ 21 #include < windows.h>21 #include <Windows.h> 22 22 #include <commctrl.h> 23 23 #include <lmerr.h> 24 24 #include <msiquery.h> 25 25 #include <objbase.h> 26 26 27 #include <shlobj.h> 27 28 #include <stdlib.h> … … 35 36 #include <iprt/dir.h> 36 37 #include <iprt/file.h> 38 #include <iprt/getopt.h> 37 39 #include <iprt/initterm.h> 38 #include <iprt/ getopt.h>40 #include <iprt/list.h> 39 41 #include <iprt/mem.h> 40 42 #include <iprt/message.h> 43 #include <iprt/param.h> 41 44 #include <iprt/path.h> 42 #include <iprt/param.h>43 45 #include <iprt/stream.h> 44 46 #include <iprt/string.h> … … 54 56 #endif 55 57 56 #ifndef _UNICODE /* Isn't this a little late? */ 57 #define _UNICODE 58 #endif 58 59 /******************************************************************************* 60 * Defined Constants And Macros * 61 *******************************************************************************/ 62 #define MY_UNICODE_SUB(str) L ##str 63 #define MY_UNICODE(str) MY_UNICODE_SUB(str) 64 65 66 /******************************************************************************* 67 * Structures and Typedefs * 68 *******************************************************************************/ 69 /** 70 * Cleanup record. 71 */ 72 typedef struct STUBCLEANUPREC 73 { 74 /** List entry. */ 75 RTLISTNODE ListEntry; 76 /** True if file, false if directory. */ 77 bool fFile; 78 /** The path to the file or directory to clean up. */ 79 char szPath[1]; 80 } STUBCLEANUPREC; 81 /** Pointer to a cleanup record. */ 82 typedef STUBCLEANUPREC *PSTUBCLEANUPREC; 59 83 60 84 … … 62 86 * Global Variables * 63 87 *******************************************************************************/ 64 static bool g_fSilent = false; 88 /** Whether it's a silent or interactive GUI driven install. */ 89 static bool g_fSilent = false; 90 /** List of temporary files. */ 91 static RTLISTANCHOR g_TmpFiles; 92 65 93 66 94 … … 83 111 RTMsgError("%s", pszMsg); 84 112 else 85 MessageBox(GetDesktopWindow(), pszMsg, VBOX_STUB_TITLE, MB_ICONERROR); 113 { 114 PRTUTF16 pwszMsg; 115 int rc = RTStrToUtf16(pszMsg, &pwszMsg); 116 if (RT_SUCCESS(rc)) 117 { 118 MessageBoxW(GetDesktopWindow(), pwszMsg, MY_UNICODE(VBOX_STUB_TITLE), MB_ICONERROR); 119 RTUtf16Free(pwszMsg); 120 } 121 else 122 MessageBoxA(GetDesktopWindow(), pszMsg, VBOX_STUB_TITLE, MB_ICONERROR); 123 } 86 124 RTStrFree(pszMsg); 87 125 } … … 112 150 RTPrintf("%s\n", pszMsg); 113 151 else 114 MessageBox(GetDesktopWindow(), pszMsg, VBOX_STUB_TITLE, MB_ICONINFORMATION); 152 { 153 PRTUTF16 pwszMsg; 154 int rc = RTStrToUtf16(pszMsg, &pwszMsg); 155 if (RT_SUCCESS(rc)) 156 { 157 MessageBoxW(GetDesktopWindow(), pwszMsg, MY_UNICODE(VBOX_STUB_TITLE), MB_ICONINFORMATION); 158 RTUtf16Free(pwszMsg); 159 } 160 else 161 MessageBoxA(GetDesktopWindow(), pszMsg, VBOX_STUB_TITLE, MB_ICONINFORMATION); 162 } 115 163 } 116 164 else /* Should never happen! */ … … 129 177 * @param pdwSize Where to return the size of the data (if found). 130 178 * Optional. 131 * 132 */ 133 static int FindData(const char *pszDataName, 134 PVOID *ppvResource, 135 DWORD *pdwSize) 179 */ 180 static int FindData(const char *pszDataName, PVOID *ppvResource, DWORD *pdwSize) 136 181 { 137 182 AssertReturn(pszDataName, VERR_INVALID_PARAMETER); 138 HINSTANCE hInst = NULL; 183 HINSTANCE hInst = NULL; /* indicates the executable image */ 139 184 140 185 /* Find our resource. */ 141 HRSRC hRsrc = FindResourceEx(hInst, RT_RCDATA, pszDataName, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)); 186 PRTUTF16 pwszDataName; 187 int rc = RTStrToUtf16(pszDataName, &pwszDataName); 188 AssertRCReturn(rc, rc); 189 HRSRC hRsrc = FindResourceExW(hInst, 190 (LPWSTR)RT_RCDATA, 191 pwszDataName, 192 MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)); 193 RTUtf16Free(pwszDataName); 142 194 AssertReturn(hRsrc, VERR_IO_GEN_FAILURE); 143 195 … … 303 355 * Decides whether we need a specified package to handle or not. 304 356 * 305 * @returns TRUE if we need to handle the specified package, FALSEif not.357 * @returns @c true if we need to handle the specified package, @c false if not. 306 358 * 307 359 * @param pPackage Pointer to a VBOXSTUBPKG struct that contains the resource. 308 360 * 309 361 */ 310 static BOOL PackageIsNeeded(PVBOXSTUBPKG pPackage) 311 { 312 BOOL bIsWow64 = IsWow64(); 313 if ((bIsWow64 && pPackage->byArch == VBOXSTUBPKGARCH_AMD64)) /* 64bit Windows. */ 314 { 315 return TRUE; 316 } 317 else if ((!bIsWow64 && pPackage->byArch == VBOXSTUBPKGARCH_X86)) /* 32bit. */ 318 { 319 return TRUE; 320 } 321 else if (pPackage->byArch == VBOXSTUBPKGARCH_ALL) 322 { 323 return TRUE; 324 } 325 return FALSE; 362 static bool PackageIsNeeded(PVBOXSTUBPKG pPackage) 363 { 364 if (pPackage->byArch == VBOXSTUBPKGARCH_ALL) 365 return true; 366 VBOXSTUBPKGARCH enmArch = IsWow64() ? VBOXSTUBPKGARCH_AMD64 : VBOXSTUBPKGARCH_X86; 367 return pPackage->byArch == enmArch; 368 } 369 370 371 /** 372 * Adds a cleanup record. 373 * 374 * @returns Fully complained boolean success indicator. 375 * @param pszPath The path to the file or directory to clean up. 376 * @param fFile @c true if file, @c false if directory. 377 */ 378 static bool AddCleanupRec(const char *pszPath, bool fFile) 379 { 380 size_t cchPath = strlen(pszPath); Assert(cchPath > 0); 381 PSTUBCLEANUPREC pRec = (PSTUBCLEANUPREC)RTMemAlloc(RT_OFFSETOF(STUBCLEANUPREC, szPath[cchPath + 1])); 382 if (!pRec) 383 { 384 ShowError("Out of memory!"); 385 return false; 386 } 387 pRec->fFile = fFile; 388 memcpy(pRec->szPath, pszPath, cchPath + 1); 389 390 RTListPrepend(&g_TmpFiles, &pRec->ListEntry); 391 return true; 392 } 393 394 395 /** 396 * Cleans up all the extracted files and optionally removes the package 397 * directory. 398 * 399 * @param cPackages The number of packages. 400 * @param pszPkgDir The package directory, NULL if it shouldn't be 401 * removed. 402 */ 403 static void CleanUp(unsigned cPackages, const char *pszPkgDir) 404 { 405 for (int i = 0; i < 5; i++) 406 { 407 int rc; 408 bool fFinalTry = i == 4; 409 410 PSTUBCLEANUPREC pCur, pNext; 411 RTListForEachSafe(&g_TmpFiles, pCur, pNext, STUBCLEANUPREC, ListEntry) 412 { 413 if (pCur->fFile) 414 rc = RTFileDelete(pCur->szPath); 415 else 416 { 417 rc = RTDirRemoveRecursive(pCur->szPath, RTDIRRMREC_F_CONTENT_AND_DIR); 418 if (rc == VERR_DIR_NOT_EMPTY && fFinalTry) 419 rc = VINF_SUCCESS; 420 } 421 if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND) 422 rc = VINF_SUCCESS; 423 if (RT_SUCCESS(rc)) 424 { 425 RTListNodeRemove(&pCur->ListEntry); 426 RTMemFree(pCur); 427 } 428 else if (fFinalTry) 429 { 430 if (pCur->fFile) 431 ShowError("Failed to delete temporary file '%s': %Rrc", pCur->szPath, rc); 432 else 433 ShowError("Failed to delete temporary directory '%s': %Rrc", pCur->szPath, rc); 434 } 435 } 436 437 if (RTListIsEmpty(&g_TmpFiles) || fFinalTry) 438 { 439 if (!pszPkgDir) 440 return; 441 rc = RTDirRemove(pszPkgDir); 442 if (RT_SUCCESS(rc) || rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND || fFinalTry) 443 return; 444 } 445 446 /* Delay a little and try again. */ 447 RTThreadSleep(i == 0 ? 100 : 3000); 448 } 326 449 } 327 450 … … 331 454 * 332 455 * @returns Fully complained exit code. 333 * @param iPackage The package number.334 456 * @param pszMsi The path to the MSI to process. 335 457 * @param pszMsiArgs Any additional installer (MSI) argument 336 458 * @param fLogging Whether to enable installer logging. 337 459 */ 338 static RTEXITCODE ProcessMsiPackage( unsigned iPackage,const char *pszMsi, const char *pszMsiArgs, bool fLogging)460 static RTEXITCODE ProcessMsiPackage(const char *pszMsi, const char *pszMsiArgs, bool fLogging) 339 461 { 340 462 int rc; … … 370 492 UINT uLogLevel = MsiEnableLogW(INSTALLLOGMODE_VERBOSE, 371 493 pwszLogFile, 372 INSTALLLOGATTRIBUTES_FLUSHEACHLINE | (iPackage > 0 ? INSTALLLOGATTRIBUTES_APPEND : 0));494 INSTALLLOGATTRIBUTES_FLUSHEACHLINE); 373 495 RTUtf16Free(pwszLogFile); 374 496 if (uLogLevel != ERROR_SUCCESS) … … 441 563 if (uStatus >= NERR_BASE && uStatus <= MAX_NERR) 442 564 { 443 hModule = LoadLibraryEx (TEXT("netmsg.dll"),444 NULL,445 LOAD_LIBRARY_AS_DATAFILE);565 hModule = LoadLibraryExW(L"netmsg.dll", 566 NULL, 567 LOAD_LIBRARY_AS_DATAFILE); 446 568 if (hModule != NULL) 447 569 dwFormatFlags |= FORMAT_MESSAGE_FROM_HMODULE; 448 570 } 449 571 450 /** @todo this is totally WRONG wrt to string code pages. We expect UTF-8 451 * while the ANSI code page might be one of the special Chinese ones, 452 * IPRT is going to be so angry with us (and so will the users). */ 453 DWORD dwBufferLength; 454 LPSTR szMessageBuffer; 455 if (dwBufferLength = FormatMessageA(dwFormatFlags, 456 hModule, /* If NULL, load system stuff. */ 457 uStatus, 458 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 459 (LPSTR)&szMessageBuffer, 460 0, 461 NULL)) 572 PWSTR pwszMsg; 573 if (FormatMessageW(dwFormatFlags, 574 hModule, /* If NULL, load system stuff. */ 575 uStatus, 576 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 577 (PWSTR)&pwszMsg, 578 0, 579 NULL) > 0) 462 580 { 463 ShowError("Installation failed! Error: % s", szMessageBuffer);464 LocalFree( szMessageBuffer);581 ShowError("Installation failed! Error: %ls", pwszMsg); 582 LocalFree(pwszMsg); 465 583 } 466 584 else /* If text lookup failed, show at least the error number. */ … … 501 619 * Deal with the file based on it's extension. 502 620 */ 503 char *pszPkgFile = RTPathJoinA(pszPkgDir, pPackage->szFileName); 504 if (!pszPkgFile) 505 return ShowError("Out of memory on line #%u!", __LINE__); 506 RTPathChangeToDosSlashes(pszPkgFile, true /* Force conversion. */); /* paranoia */ 621 char szPkgFile[RTPATH_MAX]; 622 int rc = RTPathJoin(szPkgFile, sizeof(szPkgFile), pszPkgDir, pPackage->szFileName); 623 if (RT_FAILURE(rc)) 624 return ShowError("Internal error: RTPathJoin failed: %Rrc", rc); 625 RTPathChangeToDosSlashes(szPkgFile, true /* Force conversion. */); /* paranoia */ 507 626 508 627 RTEXITCODE rcExit; 509 const char *pszExt = RTPathExt( pszPkgFile);628 const char *pszExt = RTPathExt(szPkgFile); 510 629 if (RTStrICmp(pszExt, ".msi") == 0) 511 rcExit = ProcessMsiPackage(iPackage, pszPkgFile, pszMsiArgs, fLogging); 630 rcExit = ProcessMsiPackage(szPkgFile, pszMsiArgs, fLogging); 631 else if (RTStrICmp(pszExt, ".cab") == 0) 632 rcExit = RTEXITCODE_SUCCESS; /* Ignore .cab files, they're generally referenced by other files. */ 512 633 else 513 634 rcExit = ShowError("Internal error: Do not know how to handle file '%s'.", pPackage->szFileName); 514 635 515 RTStrFree(pszPkgFile);516 636 return rcExit; 517 637 } … … 548 668 { 549 669 char szSrcDir[RTPATH_MAX]; 550 int rc = RTPathExecDir(szSrcDir, sizeof(szSrcDir) - 1);670 int rc = RTPathExecDir(szSrcDir, sizeof(szSrcDir)); 551 671 if (RT_SUCCESS(rc)) 552 rc = RTPathAppend(szSrcDir, sizeof(szSrcDir) - 1, ".custom");672 rc = RTPathAppend(szSrcDir, sizeof(szSrcDir), ".custom"); 553 673 if (RT_FAILURE(rc)) 554 674 return ShowError("Failed to construct '.custom' dir path: %Rrc", rc); … … 558 678 /* 559 679 * Use SHFileOperation w/ FO_COPY to do the job. This API requires an 560 * extra zero at the end of both source and destination paths. Thus 561 * the -1 above and below. 680 * extra zero at the end of both source and destination paths. 562 681 */ 563 682 size_t cwc; 564 RTUTF16 wszSrcDir[RTPATH_MAX + 2];683 RTUTF16 wszSrcDir[RTPATH_MAX + 1]; 565 684 PRTUTF16 pwszSrcDir = wszSrcDir; 566 685 rc = RTStrToUtf16Ex(szSrcDir, RTSTR_MAX, &pwszSrcDir, RTPATH_MAX, &cwc); … … 569 688 wszSrcDir[cwc] = '\0'; 570 689 571 RTUTF16 wszDstDir[RTPATH_MAX + 2];690 RTUTF16 wszDstDir[RTPATH_MAX + 1]; 572 691 PRTUTF16 pwszDstDir = wszSrcDir; 573 692 rc = RTStrToUtf16Ex(pszDstDir, RTSTR_MAX, &pwszDstDir, RTPATH_MAX, &cwc); … … 593 712 if (rc != 0) /* Not a Win32 status code! */ 594 713 return ShowError("Copying the '.custom' dir failed: %#x", rc); 714 715 /* 716 * Add a cleanup record for recursively deleting the destination 717 * .custom directory. We should actually add this prior to calling 718 * SHFileOperationW since it may partially succeed... 719 */ 720 char *pszDstSubDir = RTPathJoinA(pszDstDir, ".custom"); 721 if (!pszDstSubDir) 722 return ShowError("Out of memory!"); 723 bool fRc = AddCleanupRec(pszDstSubDir, false /*fFile*/); 724 RTStrFree(pszDstSubDir); 725 if (!fRc) 726 return RTEXITCODE_FAILURE; 595 727 } 596 728 … … 599 731 600 732 601 static RTEXITCODE ExtractFiles(unsigned cPackages, const char *pszDstDir, bool fExtractOnly )733 static RTEXITCODE ExtractFiles(unsigned cPackages, const char *pszDstDir, bool fExtractOnly, bool *pfCreatedExtractDir) 602 734 { 603 735 int rc; … … 606 738 * Make sure the directory exists. 607 739 */ 740 *pfCreatedExtractDir = false; 608 741 if (!RTDirExists(pszDstDir)) 609 742 { … … 611 744 if (RT_FAILURE(rc)) 612 745 return ShowError("Failed to create extraction path '%s': %Rrc", pszDstDir, rc); 746 *pfCreatedExtractDir = true; 613 747 } 614 748 … … 624 758 if (fExtractOnly || PackageIsNeeded(pPackage)) 625 759 { 626 char *pszDstFile = RTPathJoinA(pszDstDir, pPackage->szFileName);627 if (!pszDstFile)628 return ShowError("Out of memory on line %u!", __LINE__);629 630 rc = Extract(pPackage, pszDstFile); 631 RTStrFree(pszDstFile);760 char szDstFile[RTPATH_MAX]; 761 rc = RTPathJoin(szDstFile, sizeof(szDstFile), pszDstDir, pPackage->szFileName); 762 if (RT_FAILURE(rc)) 763 return ShowError("Internal error: RTPathJoin failed: %Rrc", rc); 764 765 rc = Extract(pPackage, szDstFile); 632 766 if (RT_FAILURE(rc)) 633 767 return ShowError("Error extracting package #%u: %Rrc", k, rc); 768 769 if (!fExtractOnly && !AddCleanupRec(szDstFile, true /*fFile*/)) 770 { 771 RTFileDelete(szDstFile); 772 return RTEXITCODE_FAILURE; 773 } 634 774 } 635 775 } … … 758 898 case 'h': 759 899 ShowInfo("-- %s v%d.%d.%d.%d --\n" 900 "\n" 760 901 "Command Line Parameters:\n\n" 761 902 "--extract - Extract file contents to temporary directory\n" … … 803 944 804 945 } 946 else 947 { 948 /** @todo should check if there is a .custom subdirectory there or not. */ 949 } 805 950 RTPathChangeToDosSlashes(szExtractPath, true /* Force conversion. */); /* MSI requirement. */ 806 951 … … 813 958 * commonly defined, nor the version number... */ 814 959 960 RTListInit(&g_TmpFiles); 961 815 962 /* 816 963 * Up to this point, we haven't done anything that requires any cleanup. 817 964 * From here on, we do everything in function so we can counter clean up. 818 965 */ 819 RTEXITCODE rcExit = ExtractFiles(pHeader->byCntPkgs, szExtractPath, fExtractOnly); 966 bool fCreatedExtractDir; 967 RTEXITCODE rcExit = ExtractFiles(pHeader->byCntPkgs, szExtractPath, fExtractOnly, &fCreatedExtractDir); 820 968 if (rcExit == RTEXITCODE_SUCCESS) 821 969 { 822 970 if (fExtractOnly) 823 { 824 if (!g_fSilent) 825 ShowInfo("Files were extracted to: %s", szExtractPath); 826 } 971 ShowInfo("Files were extracted to: %s", szExtractPath); 827 972 else 828 973 { … … 830 975 #ifdef VBOX_WITH_CODE_SIGNING 831 976 if (rcExit == RTEXITCODE_SUCCESS && fEnableSilentCert && g_fSilent) 832 InstallCertificate();977 rcExit = InstallCertificate(); 833 978 #endif 834 979 unsigned iPackage = 0; … … 838 983 iPackage++; 839 984 } 985 986 /* Don't fail if cleanup fail. At least for now. */ 987 CleanUp(pHeader->byCntPkgs, !fEnableLogging && fCreatedExtractDir ? szExtractPath : NULL); 840 988 } 841 989 } 842 990 843 844 845 do /* break loop */ 846 { 847 848 /* Clean up (only on success - prevent deleting the log). */ 849 if ( !fExtractOnly 850 && RT_SUCCESS(vrc)) 851 { 852 for (int i = 0; i < 5; i++) 853 { 854 vrc = RTDirRemoveRecursive(szExtractPath, 0 /*fFlags*/); 855 if (RT_SUCCESS(vrc)) 856 break; 857 RTThreadSleep(3000 /* Wait 3 seconds.*/); 858 } 859 } 860 861 } while (0); 862 863 /* Release instance mutex. */ 991 /* Free any left behind cleanup records (not strictly needed). */ 992 PSTUBCLEANUPREC pCur, pNext; 993 RTListForEachSafe(&g_TmpFiles, pCur, pNext, STUBCLEANUPREC, ListEntry) 994 { 995 RTListNodeRemove(&pCur->ListEntry); 996 RTMemFree(pCur); 997 } 998 999 /* 1000 * Release instance mutex. 1001 */ 864 1002 if (hMutexAppRunning != NULL) 865 1003 {
Note:
See TracChangeset
for help on using the changeset viewer.