Bug 368587 - avoid the second UAC prompt for helper.exe on software update by launching it directly from the elevated updater.exe process. r=bsmedberg, a=mconnor
authorrob_strong@exchangecode.com
Mon, 30 Jul 2007 13:16:56 -0700
changeset 4092 81665ce10518cb9379a4ed352170bec459e6206a
parent 4091 995fb58ecc83a689019eb858acd228ca391c3760
child 4093 6b61e24b217e7cc56fd7b12bf9e6203848ab552f
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbsmedberg, mconnor
bugs368587
milestone1.9a7pre
Bug 368587 - avoid the second UAC prompt for helper.exe on software update by launching it directly from the elevated updater.exe process. r=bsmedberg, a=mconnor
browser/installer/windows/nsis/updater_append.ini
browser/locales/Makefile.in
toolkit/mozapps/installer/windows/nsis/common.nsh
toolkit/mozapps/update/src/Makefile.in
toolkit/mozapps/update/src/nsPostUpdateWin.js
toolkit/mozapps/update/src/updater/updater.cpp
new file mode 100644
--- /dev/null
+++ b/browser/installer/windows/nsis/updater_append.ini
@@ -0,0 +1,12 @@
+
+; IMPORTANT: This file should always start with a newline in case a locale
+; provided updater.ini does not end with a newline.
+; Application to launch after an update has been successfully applied. This
+; must be in the same directory or a sub-directory of the directory of the
+; application executable that initiated the software update.
+[PostUpdateWin]
+; ExeRelPath is the path to the PostUpdateWin executable relative to the
+; application executable.
+ExeRelPath=uninstall\helper.exe
+; ExeArg is the argument to pass to the PostUpdateWin exe
+ExeArg=/PostUpdate
--- a/browser/locales/Makefile.in
+++ b/browser/locales/Makefile.in
@@ -288,17 +288,18 @@ installers-%:
 ifeq (WINNT,$(OS_ARCH))
 	@$(MAKE) repackage-win32-installer-$*
 endif
 	@$(MAKE) repackage-zip-$*
 
 ifdef MOZ_UPDATER
 libs:: $(addprefix $(LOCALE_SRCDIR)/,updater/updater.ini)
 ifeq ($(OS_ARCH),WINNT)
-	iconv -f UTF-8 -t $(WIN_INSTALLER_CHARSET) $< > $(FINAL_TARGET)/updater.ini
+	cat $< $(srcdir)/../installer/windows/nsis/updater_append.ini | \
+	  iconv -f UTF-8 -t $(WIN_INSTALLER_CHARSET) > $(FINAL_TARGET)/updater.ini
 else
 ifneq (,$(filter mac cocoa,$(MOZ_WIDGET_TOOLKIT)))
 	$(SYSINSTALL) $(IFLAGS1) $^ $(FINAL_TARGET)/updater.app/Contents/MacOS
 else
 	$(SYSINSTALL) $(IFLAGS1) $^ $(FINAL_TARGET)
 endif
 endif
 endif
--- a/toolkit/mozapps/installer/windows/nsis/common.nsh
+++ b/toolkit/mozapps/installer/windows/nsis/common.nsh
@@ -384,21 +384,21 @@ Exch $R9 ; exchange the new $R9 value wi
   WriteINIStr "$PLUGINSDIR\options.ini" "Field 7" Text   "$(OPTION_CUSTOM_DESC)"
   WriteINIStr "$PLUGINSDIR\options.ini" "Field 7" Left   "30"
   WriteINIStr "$PLUGINSDIR\options.ini" "Field 7" Right  "-1"
   WriteINIStr "$PLUGINSDIR\options.ini" "Field 7" Top    "97"
   WriteINIStr "$PLUGINSDIR\options.ini" "Field 7" Bottom "117"
 !macroend
 
 !macro GetParentDir
-   Exch $R0
-   Push $R1
-   Push $R2
-   Push $R3
-   StrLen $R3 $R0
+  Exch $R0
+  Push $R1
+  Push $R2
+  Push $R3
+  StrLen $R3 $R0
   ${DoWhile} 1 > 0
     IntOp $R1 $R1 - 1
     ${If} $R1 <= -$R3
       ${Break}
     ${EndIf}
     StrCpy $R2 $R0 1 $R1
     ${If} $R2 == "\"
       ${Break}
@@ -1066,16 +1066,19 @@ Exch $R9 ; exchange the new $R9 value wi
   Push "${_PROMPT}"
   Call un.CloseApp
   !verbose pop
 !macroend
 
 /**
  * Writes a registry string using SHCTX and the supplied params and logs the
  * action to the install log and the uninstall log if _LOG_UNINSTALL equals 1.
+ *
+ * Define NO_LOG to prevent all logging when calling this from the uninstaller.
+ *
  * @param   _ROOT
  *          The registry key root as defined by NSIS (e.g. HKLM, HKCU, etc.).
  *          This will only be used for logging.
  * @param   _KEY
  *          The subkey in relation to the key root.
  * @param   _NAME
  *          The key value name to write to.
  * @param   _STR
@@ -1171,16 +1174,19 @@ Exch $R9 ; exchange the new $R9 value wi
     !define _MOZFUNC_UN
     !verbose pop
   !endif
 !macroend
 
 /**
  * Writes a registry dword using SHCTX and the supplied params and logs the
  * action to the install log and the uninstall log if _LOG_UNINSTALL equals 1.
+ *
+ * Define NO_LOG to prevent all logging when calling this from the uninstaller.
+ *
  * @param   _ROOT
  *          The registry key root as defined by NSIS (e.g. HKLM, HKCU, etc.).
  *          This will only be used for logging.
  * @param   _KEY
  *          The subkey in relation to the key root.
  * @param   _NAME
  *          The key value name to write to.
  * @param   _DWORD
@@ -1275,16 +1281,19 @@ Exch $R9 ; exchange the new $R9 value wi
     !define _MOZFUNC_UN
     !verbose pop
   !endif
 !macroend
 
 /**
  * Writes a registry string to HKCR using the supplied params and logs the
  * action to the install log and the uninstall log if _LOG_UNINSTALL equals 1.
+ *
+ * Define NO_LOG to prevent all logging when calling this from the uninstaller.
+ *
  * @param   _ROOT
  *          The registry key root as defined by NSIS (e.g. HKLM, HKCU, etc.).
  *          This will only be used for logging.
  * @param   _KEY
  *          The subkey in relation to the key root.
  * @param   _NAME
  *          The key value name to write to.
  * @param   _STR
@@ -1393,16 +1402,19 @@ Exch $R9 ; exchange the new $R9 value wi
  * see the NSIS documentation for additional information.
  */
 !define RegCreateKey "Advapi32::RegCreateKeyA(i, t, *i) i"
 
 /**
  * Creates a registry key. This will log the actions to the install and
  * uninstall logs. Alternatively you can set a registry value to create the key
  * and then delete the value.
+ *
+ * Define NO_LOG to prevent all logging when calling this from the uninstaller.
+ *
  * @param   _ROOT
  *          The registry key root as defined by NSIS (e.g. HKLM, HKCU, etc.).
  * @param   _KEY
  *          The subkey in relation to the key root.
  * @param   _LOG_UNINSTALL
  *          0 = don't add to uninstall log, 1 = add to uninstall log.
  *
  * $R4 = [out] handle to newly created registry key. If this is not a key
@@ -1894,113 +1906,114 @@ Exch $R9 ; exchange the new $R9 value wi
 *
 * $R6 = counter for the outer loop's EnumRegKey
 * $R7 = value returned from ReadRegStr
 * $R8 = _STRING
 * $R9 = _RESULT
 */
 !macro GetPathFromString
 
- !ifndef ${_MOZFUNC_UN}GetPathFromString
-   !verbose push
-   !verbose ${_MOZFUNC_VERBOSE}
-   !define ${_MOZFUNC_UN}GetPathFromString "!insertmacro ${_MOZFUNC_UN}GetPathFromStringCall"
+  !ifndef ${_MOZFUNC_UN}GetPathFromString
+    !verbose push
+    !verbose ${_MOZFUNC_VERBOSE}
+    !define ${_MOZFUNC_UN}GetPathFromString "!insertmacro ${_MOZFUNC_UN}GetPathFromStringCall"
 
-   Function ${_MOZFUNC_UN}GetPathFromString
-     Exch $R8
-     Push $R7
-     Push $R6
-     ClearErrors
+    Function ${_MOZFUNC_UN}GetPathFromString
+      Exch $R8
+      Push $R7
+      Push $R6
+      ClearErrors
 
-     StrCpy $R9 $R8
-     StrCpy $R6 0          ; Set the counter to 0.
+      StrCpy $R9 $R8
+      StrCpy $R6 0          ; Set the counter to 0.
 
-     ClearErrors
-     ; Handle quoted paths with arguments.
-     StrCpy $R7 $R9 1      ; Copy the first char.
-     StrCmp $R7 '"' +2 +1  ; Is it a "?
-     StrCmp $R7 "'" +1 +9  ; Is it a '?
-     StrCpy $R9 $R9 "" 1   ; Remove the first char.
-     IntOp $R6 $R6 + 1     ; Increment the counter.
-     StrCpy $R7 $R9 1 $R6  ; Starting from the counter copy the next char.
-     StrCmp $R7 "" end     ; Are there no more chars?
-     StrCmp $R7 '"' +2 +1  ; Is it a " char?
-     StrCmp $R7 "'" +1 -4  ; Is it a ' char?
-     StrCpy $R9 $R9 $R6    ; Copy chars up to the counter.
-     GoTo end
+      ClearErrors
+      ; Handle quoted paths with arguments.
+      StrCpy $R7 $R9 1      ; Copy the first char.
+      StrCmp $R7 '"' +2 +1  ; Is it a "?
+      StrCmp $R7 "'" +1 +9  ; Is it a '?
+      StrCpy $R9 $R9 "" 1   ; Remove the first char.
+      IntOp $R6 $R6 + 1     ; Increment the counter.
+      StrCpy $R7 $R9 1 $R6  ; Starting from the counter copy the next char.
+      StrCmp $R7 "" end     ; Are there no more chars?
+      StrCmp $R7 '"' +2 +1  ; Is it a " char?
+      StrCmp $R7 "'" +1 -4  ; Is it a ' char?
+      StrCpy $R9 $R9 $R6    ; Copy chars up to the counter.
+      GoTo end
 
-     ; Handle DefaultIcon paths. DefaultIcon paths are not quoted and end with
-     ; a , and a number.
-     IntOp $R6 $R6 - 1     ; Decrement the counter.
-     StrCpy $R7 $R9 1 $R6  ; Copy one char from the end minus the counter.
-     StrCmp $R7 '' +4      ; Are there no more chars?
-     StrCmp $R7 ',' +1 -3  ; Is it a , char?
-     StrCpy $R9 $R9 $R6    ; Copy chars up to the end minus the counter.
-     GoTo end
+      ; Handle DefaultIcon paths. DefaultIcon paths are not quoted and end with
+      ; a , and a number.
+      IntOp $R6 $R6 - 1     ; Decrement the counter.
+      StrCpy $R7 $R9 1 $R6  ; Copy one char from the end minus the counter.
+      StrCmp $R7 '' +4      ; Are there no more chars?
+      StrCmp $R7 ',' +1 -3  ; Is it a , char?
+      StrCpy $R9 $R9 $R6    ; Copy chars up to the end minus the counter.
+      GoTo end
 
-     ; Handle unquoted paths with arguments. An unquoted path with arguments
-     ; must be an 8dot3 path.
-     StrCpy $R6 -1          ; Set the counter to -1 so it will start at 0.
-     IntOp $R6 $R6 + 1      ; Increment the counter.
-     StrCpy $R7 $R9 1 $R6   ; Starting from the counter copy the next char.
-     StrCmp $R7 "" end      ; Are there no more chars?
-     StrCmp $R7 " " +1 -3   ; Is it a space char?
-     StrCpy $R9 $R9 $R6     ; Copy chars up to the counter.
+      ; Handle unquoted paths with arguments. An unquoted path with arguments
+      ; must be an 8dot3 path.
+      StrCpy $R6 -1          ; Set the counter to -1 so it will start at 0.
+      IntOp $R6 $R6 + 1      ; Increment the counter.
+      StrCpy $R7 $R9 1 $R6   ; Starting from the counter copy the next char.
+      StrCmp $R7 "" end      ; Are there no more chars?
+      StrCmp $R7 " " +1 -3   ; Is it a space char?
+      StrCpy $R9 $R9 $R6     ; Copy chars up to the counter.
 
-     end:
+      end:
 
-     Pop $R6
-     Pop $R7
-     Exch $R8
-     Push $R9
-   FunctionEnd
+      Pop $R6
+      Pop $R7
+      Exch $R8
+      Push $R9
+    FunctionEnd
 
-   !verbose pop
- !endif
+    !verbose pop
+  !endif
 !macroend
 
 !macro GetPathFromStringCall _STRING _RESULT
- !verbose push
- !verbose ${_MOZFUNC_VERBOSE}
- Push "${_STRING}"
- Call GetPathFromString
- Pop ${_RESULT}
- !verbose pop
+  !verbose push
+  !verbose ${_MOZFUNC_VERBOSE}
+  Push "${_STRING}"
+  Call GetPathFromString
+  Pop ${_RESULT}
+  !verbose pop
 !macroend
 
 !macro un.GetPathFromStringCall _STRING _RESULT
- !verbose push
- !verbose ${_MOZFUNC_VERBOSE}
- Push "${_STRING}"
- Call un.GetPathFromString
- Pop ${_RESULT}
- !verbose pop
+  !verbose push
+  !verbose ${_MOZFUNC_VERBOSE}
+  Push "${_STRING}"
+  Call un.GetPathFromString
+  Pop ${_RESULT}
+  !verbose pop
 !macroend
 
 !macro un.GetPathFromString
- !ifndef un.GetPathFromString
-   !verbose push
-   !verbose ${_MOZFUNC_VERBOSE}
-   !undef _MOZFUNC_UN
-   !define _MOZFUNC_UN "un."
+  !ifndef un.GetPathFromString
+    !verbose push
+    !verbose ${_MOZFUNC_VERBOSE}
+    !undef _MOZFUNC_UN
+    !define _MOZFUNC_UN "un."
 
-   !insertmacro GetPathFromString
+    !insertmacro GetPathFromString
 
-   !undef _MOZFUNC_UN
-   !define _MOZFUNC_UN
-   !verbose pop
- !endif
+    !undef _MOZFUNC_UN
+    !define _MOZFUNC_UN
+    !verbose pop
+  !endif
 !macroend
 
 /**
  * If present removes the VirtualStore directory for this installation. Uses the
  * program files directory path and the current install location to determine
  * the sub-directory in the VirtualStore directory.
-*/
+ */
 !macro CleanVirtualStore
+
   !ifndef ${_MOZFUNC_UN}CleanVirtualStore
     !verbose push
     !verbose ${_MOZFUNC_VERBOSE}
     !define ${_MOZFUNC_UN}CleanVirtualStore "!insertmacro ${_MOZFUNC_UN}CleanVirtualStoreCall"
 
     Function ${_MOZFUNC_UN}CleanVirtualStore
       Push $R9
       Push $R8
@@ -2041,35 +2054,160 @@ Exch $R9 ; exchange the new $R9 value wi
       Pop $R9
     FunctionEnd
 
     !verbose pop
   !endif
 !macroend
 
 !macro CleanVirtualStoreCall
- !verbose push
- !verbose ${_MOZFUNC_VERBOSE}
- Call CleanVirtualStore
- !verbose pop
+  !verbose push
+  !verbose ${_MOZFUNC_VERBOSE}
+  Call CleanVirtualStore
+  !verbose pop
 !macroend
 
 !macro un.CleanVirtualStoreCall
- !verbose push
- !verbose ${_MOZFUNC_VERBOSE}
- Call un.CleanVirtualStore
- !verbose pop
+  !verbose push
+  !verbose ${_MOZFUNC_VERBOSE}
+  Call un.CleanVirtualStore
+  !verbose pop
 !macroend
 
 !macro un.CleanVirtualStore
- !ifndef un.CleanVirtualStore
-   !verbose push
-   !verbose ${_MOZFUNC_VERBOSE}
-   !undef _MOZFUNC_UN
-   !define _MOZFUNC_UN "un."
+  !ifndef un.CleanVirtualStore
+    !verbose push
+    !verbose ${_MOZFUNC_VERBOSE}
+    !undef _MOZFUNC_UN
+    !define _MOZFUNC_UN "un."
+
+    !insertmacro CleanVirtualStore
+
+    !undef _MOZFUNC_UN
+    !define _MOZFUNC_UN
+    !verbose pop
+  !endif
+!macroend
+
+/**
+ * Updates the uninstall.log with new files added by software update.
+ *
+ * Requires FileJoin, LineFind, TextCompare, and TrimNewLines.
+ *
+ * IMPORTANT! The LineFind docs claim that it uses all registers except $R0-$R3.
+ *            Though it appears that this is not true all registers besides
+ *            $R0-$R3 may be overwritten so protect yourself!
+ */
+!macro UpdateUninstallLog
+
+  !ifndef ${_MOZFUNC_UN}UpdateUninstallLog
+    !verbose push
+    !verbose ${_MOZFUNC_VERBOSE}
+    !define ${_MOZFUNC_UN}UpdateUninstallLog "!insertmacro ${_MOZFUNC_UN}UpdateUninstallLogCall"
+
+    Function ${_MOZFUNC_UN}UpdateUninstallLog
+      Push $R3
+      Push $R2
+      Push $R1
+      Push $R0
+
+      ClearErrors
+
+      GetFullPathName $R3 "$INSTDIR\uninstall"
+      IfFileExists "$R3\uninstall.update" +2 0
+      Return
+
+      ${${_MOZFUNC_UN}LineFind} "$R3\uninstall.update" "" "1:-1" "${_MOZFUNC_UN}CleanupUpdateLog"
+
+      GetTempFileName $R2 "$R3"
+      FileOpen $R1 $R2 w
+      ${${_MOZFUNC_UN}TextCompare} "$R3\uninstall.update" "$R3\uninstall.log" "SlowDiff" "${_MOZFUNC_UN}CreateUpdateDiff"
+      FileClose $R1
+
+      IfErrors +2 0
+      ${${_MOZFUNC_UN}FileJoin} "$R3\uninstall.log" "$R2" "$R3\uninstall.log"
+
+      ${DeleteFile} "$R2"
+
+      ClearErrors
+
+      Pop $R0
+      Pop $R1
+      Pop $R2
+      Pop $R3
+    FunctionEnd
+
+    ; This callback MUST use labels vs. relative line numbers.
+    Function ${_MOZFUNC_UN}CleanupUpdateLog
+      StrCpy $R2 "$R9" 12
+      StrCmp "$R2" "EXECUTE ADD " 0 skip
+      StrCpy $R9 "$R9" "" 12
 
-   !insertmacro CleanVirtualStore
+      Push $R6
+      Push $R5
+      Push $R4
+      StrCpy $R4 ""         ; Initialize to an empty string.
+      StrCpy $R6 -1         ; Set the counter to -1 so it will start at 0.
+
+      loop:
+      IntOp $R6 $R6 + 1     ; Increment the counter.
+      StrCpy $R5 $R9 1 $R6  ; Starting from the counter copy the next char.
+      StrCmp $R5 "" copy    ; Are there no more chars?
+      StrCmp $R5 "/" 0 +2   ; Is the char a /?
+      StrCpy $R5 "\"        ; Replace the char with a \.
+
+      StrCpy $R4 "$R4$R5"
+      GoTo loop
+
+      copy:
+      StrCpy $R9 "File: \$R4"
+      Pop $R6
+      Pop $R5
+      Pop $R4
+      GoTo end
+
+      skip:
+      StrCpy $0 "SkipWrite"
+
+      end:
+      Push $0
+    FunctionEnd
+
+    Function ${_MOZFUNC_UN}CreateUpdateDiff
+      ${${_MOZFUNC_UN}TrimNewLines} "$9" "$9"
+      StrCmp $9 "" +2 0
+      FileWrite $R1 "$9$\r$\n"
 
-   !undef _MOZFUNC_UN
-   !define _MOZFUNC_UN
-   !verbose pop
- !endif
+      Push 0
+    FunctionEnd
+
+    !verbose pop
+  !endif
+!macroend
+
+!macro UpdateUninstallLogCall
+  !verbose push
+  !verbose ${_MOZFUNC_VERBOSE}
+  Call UpdateUninstallLog
+  !verbose pop
 !macroend
+
+!macro un.UpdateUninstallLogCall
+  !verbose push
+  !verbose ${_MOZFUNC_VERBOSE}
+  Call un.UpdateUninstallLog
+  !verbose pop
+!macroend
+
+!macro un.UpdateUninstallLog
+  !ifndef un.UpdateUninstallLog
+    !verbose push
+    !verbose ${_MOZFUNC_VERBOSE}
+    !undef _MOZFUNC_UN
+    !define _MOZFUNC_UN "un."
+
+    !insertmacro UpdateUninstallLog
+
+    !undef _MOZFUNC_UN
+    !define _MOZFUNC_UN
+    !verbose pop
+  !endif
+!macroend
--- a/toolkit/mozapps/update/src/Makefile.in
+++ b/toolkit/mozapps/update/src/Makefile.in
@@ -37,17 +37,17 @@
 
 DEPTH   = ../../../..
 topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH   = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
-MODULE = extensions
+MODULE = update
 
 ifdef MOZ_UPDATER
 DIRS = updater
 endif
 
 EXTRA_COMPONENTS = nsUpdateService.js
 GARBAGE += nsUpdateService.js
 
--- a/toolkit/mozapps/update/src/nsPostUpdateWin.js
+++ b/toolkit/mozapps/update/src/nsPostUpdateWin.js
@@ -584,16 +584,26 @@ nsPostUpdateWin.prototype = {
   QueryInterface: function(iid) {
     if (iid.equals(Components.interfaces.nsIRunnable) ||
         iid.equals(Components.interfaces.nsISupports))
       return this;
     throw Components.results.NS_ERROR_NO_INTERFACE;
   },
 
   run: function() {
+    // When uninstall/uninstall.update exists the uninstaller has already
+    // updated the uninstall.log with the files added by software update.
+    var updateUninstallFile = getFile(KEY_APPDIR); 
+    updateUninstallFile.append("uninstall");
+    updateUninstallFile.append("uninstall.update");
+    if (updateUninstallFile.exists()) {
+      LOG("nothing to do, uninstall.log has already been updated"); 
+      return;
+    }
+
     try {
       installLogWriter = new InstallLogWriter();
       try {
         installLogWriter.begin();
       } finally {
         installLogWriter.end();
         installLogWriter = null;
       }
--- a/toolkit/mozapps/update/src/updater/updater.cpp
+++ b/toolkit/mozapps/update/src/updater/updater.cpp
@@ -258,16 +258,17 @@ private:
 #else
 #error "Unsupported platform"
 #endif
 
 //-----------------------------------------------------------------------------
 
 static char* gSourcePath;
 static ArchiveReader gArchiveReader;
+static bool gSucceeded = FALSE;
 
 static const char kWhitespace[] = " \t";
 static const char kNL[] = "\r\n";
 static const char kQuote[] = "\"";
 
 //-----------------------------------------------------------------------------
 // LOGGING
 
@@ -1001,16 +1002,71 @@ PatchIfFile::Finish(int status)
 
   PatchFile::Finish(status);
 }
 
 //-----------------------------------------------------------------------------
 
 #ifdef XP_WIN
 #include "nsWindowsRestart.cpp"
+
+static void
+LaunchWinPostProcess(const char *appExe)
+{
+  // Launch helper.exe to perform post processing (e.g. registry and log file
+  // modifications) for the update.
+  char inifile[MAXPATHLEN];
+  strcpy(inifile, appExe);
+
+  char *slash = strrchr(inifile, '\\');
+  if (!slash)
+    return;
+
+  strcpy(slash + 1, "updater.ini");
+
+  char exefile[MAXPATHLEN];
+  char exearg[MAXPATHLEN];
+  if (!GetPrivateProfileString("PostUpdateWin", "ExeRelPath", NULL, exefile,
+      sizeof(exefile), inifile))
+    return;
+
+  if (!GetPrivateProfileString("PostUpdateWin", "ExeArg", NULL, exearg,
+      sizeof(exearg), inifile))
+    return;
+
+  char exefullpath[MAXPATHLEN];
+  strcpy(exefullpath, appExe);
+
+  slash = strrchr(exefullpath, '\\');
+  strcpy(slash + 1, exefile);
+
+  char dlogFile[MAXPATHLEN];
+  strcpy(dlogFile, exefullpath);
+
+  slash = strrchr(dlogFile, '\\');
+  strcpy(slash + 1, "uninstall.update");
+
+  char slogFile[MAXPATHLEN];
+  snprintf(slogFile, MAXPATHLEN, "%s/update.log", gSourcePath);
+
+  // We want to launch the post update helper app to update the Windows
+  // registry even if there is a failure with removing the uninstall.update
+  // file or copying the update.log file.
+  ensure_remove(dlogFile);
+  copy_file(slogFile, dlogFile);
+
+  static int    argc = 2;
+  static char **argv = (char**) malloc(sizeof(char*) * (argc + 1));
+  argv[0] = "argv0ignoredbywinlaunchchild";
+  argv[1] = exearg;
+  argv[2] = "\0";
+
+  WinLaunchChild(exefullpath, argc, argv, 1);
+  free(argv);
+}
 #endif
 
 static void
 LaunchCallbackApp(const char *workingDir, int argc, char **argv)
 {
   putenv("NO_EM_RESTART=");
   putenv("MOZ_LAUNCHED_CHILD=1");
 
@@ -1139,16 +1195,19 @@ int main(int argc, char **argv)
     ShowProgressUI();
   t.Join();
 
   LogFinish();
 
 #ifdef XP_WIN
   if (exefile)
     CloseHandle(exefile);
+
+  if (gSucceeded)
+    LaunchWinPostProcess(argv[4]);
 #endif
 
   // The callback to execute is given as the last N arguments of our command
   // line.  The first of those arguments specifies the working directory for
   // the callback.
   if (argc > 4)
     LaunchCallbackApp(argv[3], argc - 4, argv + 4);
 
@@ -1246,16 +1305,19 @@ void
 ActionList::Finish(int status)
 {
   Action *a = mFirst;
   while (a) {
     a->Finish(status);
     a = a->mNext;
   }
 
+  if (status == OK)
+    gSucceeded = TRUE;
+
   UpdateProgressUI(100.0f);
 }
 
 int DoUpdate()
 {
   char manifest[MAXPATHLEN];
   snprintf(manifest, MAXPATHLEN, "%s/update.manifest", gSourcePath);