Index: /trunk/src/VBox/Installer/win/Stub/VBoxStub.cpp
===================================================================
--- /trunk/src/VBox/Installer/win/Stub/VBoxStub.cpp	(revision 45331)
+++ /trunk/src/VBox/Installer/win/Stub/VBoxStub.cpp	(revision 45332)
@@ -19,9 +19,10 @@
 *   Header Files                                                               *
 *******************************************************************************/
-#include <windows.h>
+#include <Windows.h>
 #include <commctrl.h>
 #include <lmerr.h>
 #include <msiquery.h>
 #include <objbase.h>
+
 #include <shlobj.h>
 #include <stdlib.h>
@@ -35,10 +36,11 @@
 #include <iprt/dir.h>
 #include <iprt/file.h>
+#include <iprt/getopt.h>
 #include <iprt/initterm.h>
-#include <iprt/getopt.h>
+#include <iprt/list.h>
 #include <iprt/mem.h>
 #include <iprt/message.h>
+#include <iprt/param.h>
 #include <iprt/path.h>
-#include <iprt/param.h>
 #include <iprt/stream.h>
 #include <iprt/string.h>
@@ -54,7 +56,29 @@
 #endif
 
-#ifndef  _UNICODE /* Isn't this a little late? */
-#define  _UNICODE
-#endif
+
+/*******************************************************************************
+*   Defined Constants And Macros                                               *
+*******************************************************************************/
+#define MY_UNICODE_SUB(str) L ##str
+#define MY_UNICODE(str)     MY_UNICODE_SUB(str)
+
+
+/*******************************************************************************
+*   Structures and Typedefs                                                    *
+*******************************************************************************/
+/**
+ * Cleanup record.
+ */
+typedef struct STUBCLEANUPREC
+{
+    /** List entry. */
+    RTLISTNODE  ListEntry;
+    /** True if file, false if directory. */
+    bool        fFile;
+    /** The path to the file or directory to clean up. */
+    char        szPath[1];
+} STUBCLEANUPREC;
+/** Pointer to a cleanup record. */
+typedef STUBCLEANUPREC *PSTUBCLEANUPREC;
 
 
@@ -62,5 +86,9 @@
 *   Global Variables                                                           *
 *******************************************************************************/
-static bool g_fSilent = false;
+/** Whether it's a silent or interactive GUI driven install. */
+static bool             g_fSilent = false;
+/** List of temporary files. */
+static RTLISTANCHOR     g_TmpFiles;
+
 
 
@@ -83,5 +111,15 @@
             RTMsgError("%s", pszMsg);
         else
-            MessageBox(GetDesktopWindow(), pszMsg, VBOX_STUB_TITLE, MB_ICONERROR);
+        {
+            PRTUTF16 pwszMsg;
+            int rc = RTStrToUtf16(pszMsg, &pwszMsg);
+            if (RT_SUCCESS(rc))
+            {
+                MessageBoxW(GetDesktopWindow(), pwszMsg, MY_UNICODE(VBOX_STUB_TITLE), MB_ICONERROR);
+                RTUtf16Free(pwszMsg);
+            }
+            else
+                MessageBoxA(GetDesktopWindow(), pszMsg, VBOX_STUB_TITLE, MB_ICONERROR);
+        }
         RTStrFree(pszMsg);
     }
@@ -112,5 +150,15 @@
             RTPrintf("%s\n", pszMsg);
         else
-            MessageBox(GetDesktopWindow(), pszMsg, VBOX_STUB_TITLE, MB_ICONINFORMATION);
+        {
+            PRTUTF16 pwszMsg;
+            int rc = RTStrToUtf16(pszMsg, &pwszMsg);
+            if (RT_SUCCESS(rc))
+            {
+                MessageBoxW(GetDesktopWindow(), pwszMsg, MY_UNICODE(VBOX_STUB_TITLE), MB_ICONINFORMATION);
+                RTUtf16Free(pwszMsg);
+            }
+            else
+                MessageBoxA(GetDesktopWindow(), pszMsg, VBOX_STUB_TITLE, MB_ICONINFORMATION);
+        }
     }
     else /* Should never happen! */
@@ -129,15 +177,19 @@
  * @param   pdwSize             Where to return the size of the data (if found).
  *                              Optional.
- *
- */
-static int FindData(const char *pszDataName,
-                    PVOID      *ppvResource,
-                    DWORD      *pdwSize)
+ */
+static int FindData(const char *pszDataName, PVOID *ppvResource, DWORD *pdwSize)
 {
     AssertReturn(pszDataName, VERR_INVALID_PARAMETER);
-    HINSTANCE hInst = NULL;
+    HINSTANCE hInst = NULL;             /* indicates the executable image */
 
     /* Find our resource. */
-    HRSRC hRsrc = FindResourceEx(hInst, RT_RCDATA, pszDataName, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));
+    PRTUTF16 pwszDataName;
+    int rc = RTStrToUtf16(pszDataName, &pwszDataName);
+    AssertRCReturn(rc, rc);
+    HRSRC hRsrc = FindResourceExW(hInst,
+                                  (LPWSTR)RT_RCDATA,
+                                  pwszDataName,
+                                  MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));
+    RTUtf16Free(pwszDataName);
     AssertReturn(hRsrc, VERR_IO_GEN_FAILURE);
 
@@ -303,25 +355,96 @@
  * Decides whether we need a specified package to handle or not.
  *
- * @returns TRUE if we need to handle the specified package, FALSE if not.
+ * @returns @c true if we need to handle the specified package, @c false if not.
  *
  * @param   pPackage            Pointer to a VBOXSTUBPKG struct that contains the resource.
  *
  */
-static BOOL PackageIsNeeded(PVBOXSTUBPKG pPackage)
-{
-    BOOL bIsWow64 = IsWow64();
-    if ((bIsWow64 && pPackage->byArch == VBOXSTUBPKGARCH_AMD64)) /* 64bit Windows. */
-    {
-        return TRUE;
-    }
-    else if ((!bIsWow64 && pPackage->byArch == VBOXSTUBPKGARCH_X86)) /* 32bit. */
-    {
-        return TRUE;
-    }
-    else if (pPackage->byArch == VBOXSTUBPKGARCH_ALL)
-    {
-        return TRUE;
-    }
-    return FALSE;
+static bool PackageIsNeeded(PVBOXSTUBPKG pPackage)
+{
+    if (pPackage->byArch == VBOXSTUBPKGARCH_ALL)
+        return true;
+    VBOXSTUBPKGARCH enmArch = IsWow64() ? VBOXSTUBPKGARCH_AMD64 : VBOXSTUBPKGARCH_X86;
+    return pPackage->byArch == enmArch;
+}
+
+
+/**
+ * Adds a cleanup record.
+ *
+ * @returns Fully complained boolean success indicator.
+ * @param   pszPath             The path to the file or directory to clean up.
+ * @param   fFile               @c true if file, @c false if directory.
+ */
+static bool AddCleanupRec(const char *pszPath, bool fFile)
+{
+    size_t cchPath = strlen(pszPath); Assert(cchPath > 0);
+    PSTUBCLEANUPREC pRec = (PSTUBCLEANUPREC)RTMemAlloc(RT_OFFSETOF(STUBCLEANUPREC, szPath[cchPath + 1]));
+    if (!pRec)
+    {
+        ShowError("Out of memory!");
+        return false;
+    }
+    pRec->fFile = fFile;
+    memcpy(pRec->szPath, pszPath, cchPath + 1);
+
+    RTListPrepend(&g_TmpFiles, &pRec->ListEntry);
+    return true;
+}
+
+
+/**
+ * Cleans up all the extracted files and optionally removes the package
+ * directory.
+ *
+ * @param   cPackages           The number of packages.
+ * @param   pszPkgDir           The package directory, NULL if it shouldn't be
+ *                              removed.
+ */
+static void CleanUp(unsigned cPackages, const char *pszPkgDir)
+{
+    for (int i = 0; i < 5; i++)
+    {
+        int rc;
+        bool fFinalTry = i == 4;
+
+        PSTUBCLEANUPREC pCur, pNext;
+        RTListForEachSafe(&g_TmpFiles, pCur, pNext, STUBCLEANUPREC, ListEntry)
+        {
+            if (pCur->fFile)
+                rc = RTFileDelete(pCur->szPath);
+            else
+            {
+                rc = RTDirRemoveRecursive(pCur->szPath, RTDIRRMREC_F_CONTENT_AND_DIR);
+                if (rc == VERR_DIR_NOT_EMPTY && fFinalTry)
+                    rc = VINF_SUCCESS;
+            }
+            if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND)
+                rc = VINF_SUCCESS;
+            if (RT_SUCCESS(rc))
+            {
+                RTListNodeRemove(&pCur->ListEntry);
+                RTMemFree(pCur);
+            }
+            else if (fFinalTry)
+            {
+                if (pCur->fFile)
+                    ShowError("Failed to delete temporary file '%s': %Rrc", pCur->szPath, rc);
+                else
+                    ShowError("Failed to delete temporary directory '%s': %Rrc", pCur->szPath, rc);
+            }
+        }
+
+        if (RTListIsEmpty(&g_TmpFiles) || fFinalTry)
+        {
+            if (!pszPkgDir)
+                return;
+            rc = RTDirRemove(pszPkgDir);
+            if (RT_SUCCESS(rc) || rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND || fFinalTry)
+                return;
+        }
+
+        /* Delay a little and try again. */
+        RTThreadSleep(i == 0 ? 100 : 3000);
+    }
 }
 
@@ -331,10 +454,9 @@
  *
  * @returns Fully complained exit code.
- * @param   iPackage            The package number.
  * @param   pszMsi              The path to the MSI to process.
  * @param   pszMsiArgs          Any additional installer (MSI) argument
  * @param   fLogging            Whether to enable installer logging.
  */
-static RTEXITCODE ProcessMsiPackage(unsigned iPackage, const char *pszMsi, const char *pszMsiArgs, bool fLogging)
+static RTEXITCODE ProcessMsiPackage(const char *pszMsi, const char *pszMsiArgs, bool fLogging)
 {
     int rc;
@@ -370,5 +492,5 @@
         UINT uLogLevel = MsiEnableLogW(INSTALLLOGMODE_VERBOSE,
                                        pwszLogFile,
-                                       INSTALLLOGATTRIBUTES_FLUSHEACHLINE | (iPackage > 0 ? INSTALLLOGATTRIBUTES_APPEND : 0));
+                                       INSTALLLOGATTRIBUTES_FLUSHEACHLINE);
         RTUtf16Free(pwszLogFile);
         if (uLogLevel != ERROR_SUCCESS)
@@ -441,26 +563,22 @@
             if (uStatus >= NERR_BASE && uStatus <= MAX_NERR)
             {
-                hModule = LoadLibraryEx(TEXT("netmsg.dll"),
-                                        NULL,
-                                        LOAD_LIBRARY_AS_DATAFILE);
+                hModule = LoadLibraryExW(L"netmsg.dll",
+                                         NULL,
+                                         LOAD_LIBRARY_AS_DATAFILE);
                 if (hModule != NULL)
                     dwFormatFlags |= FORMAT_MESSAGE_FROM_HMODULE;
             }
 
-            /** @todo this is totally WRONG wrt to string code pages. We expect UTF-8
-             *        while the ANSI code page might be one of the special Chinese ones,
-             *        IPRT is going to be so angry with us (and so will the users). */
-            DWORD dwBufferLength;
-            LPSTR szMessageBuffer;
-            if (dwBufferLength = FormatMessageA(dwFormatFlags,
-                                                hModule, /* If NULL, load system stuff. */
-                                                uStatus,
-                                                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
-                                                (LPSTR)&szMessageBuffer,
-                                                0,
-                                                NULL))
+            PWSTR pwszMsg;
+            if (FormatMessageW(dwFormatFlags,
+                               hModule, /* If NULL, load system stuff. */
+                               uStatus,
+                               MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+                               (PWSTR)&pwszMsg,
+                               0,
+                               NULL) > 0)
             {
-                ShowError("Installation failed! Error: %s", szMessageBuffer);
-                LocalFree(szMessageBuffer);
+                ShowError("Installation failed! Error: %ls", pwszMsg);
+                LocalFree(pwszMsg);
             }
             else /* If text lookup failed, show at least the error number. */
@@ -501,17 +619,19 @@
      * Deal with the file based on it's extension.
      */
-    char *pszPkgFile = RTPathJoinA(pszPkgDir, pPackage->szFileName);
-    if (!pszPkgFile)
-        return ShowError("Out of memory on line #%u!", __LINE__);
-    RTPathChangeToDosSlashes(pszPkgFile, true /* Force conversion. */); /* paranoia */
+    char szPkgFile[RTPATH_MAX];
+    int rc = RTPathJoin(szPkgFile, sizeof(szPkgFile), pszPkgDir, pPackage->szFileName);
+    if (RT_FAILURE(rc))
+        return ShowError("Internal error: RTPathJoin failed: %Rrc", rc);
+    RTPathChangeToDosSlashes(szPkgFile, true /* Force conversion. */); /* paranoia */
 
     RTEXITCODE rcExit;
-    const char *pszExt = RTPathExt(pszPkgFile);
+    const char *pszExt = RTPathExt(szPkgFile);
     if (RTStrICmp(pszExt, ".msi") == 0)
-        rcExit = ProcessMsiPackage(iPackage, pszPkgFile, pszMsiArgs, fLogging);
+        rcExit = ProcessMsiPackage(szPkgFile, pszMsiArgs, fLogging);
+    else if (RTStrICmp(pszExt, ".cab") == 0)
+        rcExit = RTEXITCODE_SUCCESS; /* Ignore .cab files, they're generally referenced by other files. */
     else
         rcExit = ShowError("Internal error: Do not know how to handle file '%s'.", pPackage->szFileName);
 
-    RTStrFree(pszPkgFile);
     return rcExit;
 }
@@ -548,7 +668,7 @@
 {
     char szSrcDir[RTPATH_MAX];
-    int rc = RTPathExecDir(szSrcDir, sizeof(szSrcDir) - 1);
+    int rc = RTPathExecDir(szSrcDir, sizeof(szSrcDir));
     if (RT_SUCCESS(rc))
-        rc = RTPathAppend(szSrcDir, sizeof(szSrcDir) - 1, ".custom");
+        rc = RTPathAppend(szSrcDir, sizeof(szSrcDir), ".custom");
     if (RT_FAILURE(rc))
         return ShowError("Failed to construct '.custom' dir path: %Rrc", rc);
@@ -558,9 +678,8 @@
         /*
          * Use SHFileOperation w/ FO_COPY to do the job.  This API requires an
-         * extra zero at the end of both source and destination paths.  Thus
-         * the -1 above and below.
+         * extra zero at the end of both source and destination paths.
          */
         size_t   cwc;
-        RTUTF16  wszSrcDir[RTPATH_MAX + 2];
+        RTUTF16  wszSrcDir[RTPATH_MAX + 1];
         PRTUTF16 pwszSrcDir = wszSrcDir;
         rc = RTStrToUtf16Ex(szSrcDir, RTSTR_MAX, &pwszSrcDir, RTPATH_MAX, &cwc);
@@ -569,5 +688,5 @@
         wszSrcDir[cwc] = '\0';
 
-        RTUTF16  wszDstDir[RTPATH_MAX + 2];
+        RTUTF16  wszDstDir[RTPATH_MAX + 1];
         PRTUTF16 pwszDstDir = wszSrcDir;
         rc = RTStrToUtf16Ex(pszDstDir, RTSTR_MAX, &pwszDstDir, RTPATH_MAX, &cwc);
@@ -593,4 +712,17 @@
         if (rc != 0)    /* Not a Win32 status code! */
             return ShowError("Copying the '.custom' dir failed: %#x", rc);
+
+        /*
+         * Add a cleanup record for recursively deleting the destination
+         * .custom directory.  We should actually add this prior to calling
+         * SHFileOperationW since it may partially succeed...
+         */
+        char *pszDstSubDir = RTPathJoinA(pszDstDir, ".custom");
+        if (!pszDstSubDir)
+            return ShowError("Out of memory!");
+        bool fRc = AddCleanupRec(pszDstSubDir, false /*fFile*/);
+        RTStrFree(pszDstSubDir);
+        if (!fRc)
+            return RTEXITCODE_FAILURE;
     }
 
@@ -599,5 +731,5 @@
 
 
-static RTEXITCODE ExtractFiles(unsigned cPackages, const char *pszDstDir, bool fExtractOnly)
+static RTEXITCODE ExtractFiles(unsigned cPackages, const char *pszDstDir, bool fExtractOnly, bool *pfCreatedExtractDir)
 {
     int rc;
@@ -606,4 +738,5 @@
      * Make sure the directory exists.
      */
+    *pfCreatedExtractDir = false;
     if (!RTDirExists(pszDstDir))
     {
@@ -611,4 +744,5 @@
         if (RT_FAILURE(rc))
             return ShowError("Failed to create extraction path '%s': %Rrc", pszDstDir, rc);
+        *pfCreatedExtractDir = true;
     }
 
@@ -624,12 +758,18 @@
         if (fExtractOnly || PackageIsNeeded(pPackage))
         {
-            char *pszDstFile = RTPathJoinA(pszDstDir, pPackage->szFileName);
-            if (!pszDstFile)
-                return ShowError("Out of memory on line %u!", __LINE__);
-
-            rc = Extract(pPackage, pszDstFile);
-            RTStrFree(pszDstFile);
+            char szDstFile[RTPATH_MAX];
+            rc = RTPathJoin(szDstFile, sizeof(szDstFile), pszDstDir, pPackage->szFileName);
+            if (RT_FAILURE(rc))
+                return ShowError("Internal error: RTPathJoin failed: %Rrc", rc);
+
+            rc = Extract(pPackage, szDstFile);
             if (RT_FAILURE(rc))
                 return ShowError("Error extracting package #%u: %Rrc", k, rc);
+
+            if (!fExtractOnly && !AddCleanupRec(szDstFile, true /*fFile*/))
+            {
+                RTFileDelete(szDstFile);
+                return RTEXITCODE_FAILURE;
+            }
         }
     }
@@ -758,4 +898,5 @@
             case 'h':
                 ShowInfo("-- %s v%d.%d.%d.%d --\n"
+                         "\n"
                          "Command Line Parameters:\n\n"
                          "--extract                - Extract file contents to temporary directory\n"
@@ -803,4 +944,8 @@
 
     }
+    else
+    {
+        /** @todo should check if there is a .custom subdirectory there or not. */
+    }
     RTPathChangeToDosSlashes(szExtractPath, true /* Force conversion. */); /* MSI requirement. */
 
@@ -813,16 +958,16 @@
      *        commonly defined, nor the version number... */
 
+    RTListInit(&g_TmpFiles);
+
     /*
      * Up to this point, we haven't done anything that requires any cleanup.
      * From here on, we do everything in function so we can counter clean up.
      */
-    RTEXITCODE rcExit = ExtractFiles(pHeader->byCntPkgs, szExtractPath, fExtractOnly);
+    bool fCreatedExtractDir;
+    RTEXITCODE rcExit = ExtractFiles(pHeader->byCntPkgs, szExtractPath, fExtractOnly, &fCreatedExtractDir);
     if (rcExit == RTEXITCODE_SUCCESS)
     {
         if (fExtractOnly)
-        {
-            if (!g_fSilent)
-                ShowInfo("Files were extracted to: %s", szExtractPath);
-        }
+            ShowInfo("Files were extracted to: %s", szExtractPath);
         else
         {
@@ -830,5 +975,5 @@
 #ifdef VBOX_WITH_CODE_SIGNING
             if (rcExit == RTEXITCODE_SUCCESS && fEnableSilentCert && g_fSilent)
-                InstallCertificate();
+                rcExit = InstallCertificate();
 #endif
             unsigned iPackage = 0;
@@ -838,28 +983,21 @@
                 iPackage++;
             }
+
+            /* Don't fail if cleanup fail. At least for now. */
+            CleanUp(pHeader->byCntPkgs, !fEnableLogging && fCreatedExtractDir ? szExtractPath : NULL);
         }
     }
 
-
-
-    do /* break loop */
-    {
-
-        /* Clean up (only on success - prevent deleting the log). */
-        if (   !fExtractOnly
-            && RT_SUCCESS(vrc))
-        {
-            for (int i = 0; i < 5; i++)
-            {
-                vrc = RTDirRemoveRecursive(szExtractPath, 0 /*fFlags*/);
-                if (RT_SUCCESS(vrc))
-                    break;
-                RTThreadSleep(3000 /* Wait 3 seconds.*/);
-            }
-        }
-
-    } while (0);
-
-    /* Release instance mutex. */
+    /* Free any left behind cleanup records (not strictly needed). */
+    PSTUBCLEANUPREC pCur, pNext;
+    RTListForEachSafe(&g_TmpFiles, pCur, pNext, STUBCLEANUPREC, ListEntry)
+    {
+        RTListNodeRemove(&pCur->ListEntry);
+        RTMemFree(pCur);
+    }
+
+    /*
+     * Release instance mutex.
+     */
     if (hMutexAppRunning != NULL)
     {
