Bug 661363 - Port bug 562753 (On upgrade, old win7 taskbar entries should have their app model id upgraded, based on install path) to Thunderbird. r=Standard8, sr=bienvenu
authorSiddharth Agarwal <sid.bugzilla@gmail.com>
Thu, 09 Jun 2011 11:44:05 -0700
changeset 8133 995678d4c9111649bf1af9aaba82b0d771c1adb3
parent 8132 c0d4c89007cff6a8cd115d9817a8d8278c199084
child 8134 3d8db10ee378db5b3c5c93ccb827eb74a6aecbd1
push idunknown
push userunknown
push dateunknown
reviewersStandard8, bienvenu
bugs661363, 562753
Bug 661363 - Port bug 562753 (On upgrade, old win7 taskbar entries should have their app model id upgraded, based on install path) to Thunderbird. r=Standard8, sr=bienvenu
mail/components/shell/nsMailWinIntegration.cpp
mail/components/shell/nsMailWinIntegration.h
mail/components/shell/public/Makefile.in
mail/components/shell/public/nsIWindowsShellService.idl
mail/components/wintaskbar/windowsJumpLists.js
mail/installer/windows/nsis/defines.nsi.in
mail/installer/windows/nsis/installer.nsi
mail/installer/windows/nsis/shared.nsh
--- a/mail/components/shell/nsMailWinIntegration.cpp
+++ b/mail/components/shell/nsMailWinIntegration.cpp
@@ -41,16 +41,18 @@
 #include "nsICategoryManager.h"
 #include "nsNativeCharsetUtils.h"
 #include "nsIPrefService.h"
 #include "windows.h"
 #include "shellapi.h"
 #include "nsILocalFile.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsUnicharUtils.h"
+#include "nsIWinTaskbar.h"
+#include "nsISupportsPrimitives.h"
 
 #ifdef _WIN32_WINNT
 #undef _WIN32_WINNT
 #endif
 #define _WIN32_WINNT 0x0600
 #define INITGUID
 #include <shlobj.h>
 
@@ -58,17 +60,19 @@
 
 #ifndef MAX_BUF
 #define MAX_BUF 4096
 #endif
 
 #define REG_FAILED(val) \
   (val != ERROR_SUCCESS)
 
-NS_IMPL_ISUPPORTS1(nsWindowsShellService, nsIShellService)
+#define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1"
+
+NS_IMPL_ISUPPORTS2(nsWindowsShellService, nsIWindowsShellService, nsIShellService)
 
 static nsresult
 OpenKeyForReading(HKEY aKeyRoot, const nsAString& aKeyName, HKEY* aKey)
 {
   const nsString &flatName = PromiseFlatString(aKeyName);
 
   DWORD res = ::RegOpenKeyExW(aKeyRoot, flatName.get(), 0, KEY_READ, aKey);
   switch (res) {
@@ -134,16 +138,136 @@ static SETTING gNewsSettings[] = {
 static SETTING gFeedSettings[] = {
    // Protocol Handler Class - for Vista and above
   { MAKE_KEY_NAME1(CLS_FEEDURL, SOP), "", VAL_MAIL_OPEN, APP_PATH_SUBSTITUTION },
 
   // Protocol Handlers
   { MAKE_KEY_NAME1("feed", SOP), "", VAL_MAIL_OPEN, APP_PATH_SUBSTITUTION },
 };
 
+nsresult
+GetHelperPath(nsAutoString& aPath)
+{
+  nsresult rv;
+  nsCOMPtr<nsIProperties> directoryService = 
+    do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsILocalFile> appHelper;
+  rv = directoryService->Get(NS_XPCOM_CURRENT_PROCESS_DIR,
+                             NS_GET_IID(nsILocalFile),
+                             getter_AddRefs(appHelper));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = appHelper->Append(NS_LITERAL_STRING("uninstall"));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = appHelper->Append(NS_LITERAL_STRING("helper.exe"));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return appHelper->GetPath(aPath);
+}
+
+nsresult
+LaunchHelper(nsAutoString& aPath, nsAutoString& aParams)
+{
+  SHELLEXECUTEINFOW executeInfo = {0};
+
+  executeInfo.cbSize = sizeof(SHELLEXECUTEINFOW);
+  executeInfo.hwnd = NULL;
+  executeInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
+  executeInfo.lpDirectory = NULL;
+  executeInfo.lpFile = aPath.get();
+  executeInfo.lpParameters = aParams.get();
+  executeInfo.nShow = SW_SHOWNORMAL;
+
+  if (ShellExecuteExW(&executeInfo))
+    // Block until the program exits
+    WaitForSingleObject(executeInfo.hProcess, INFINITE);
+  else
+    return NS_ERROR_ABORT;
+
+  // We're going to ignore errors here since there's nothing we can do about
+  // them, and helper.exe seems to return non-zero ret on success.
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowsShellService::ShortcutMaintenance()
+{
+  nsresult rv;
+
+  // Launch helper.exe so it can update the application user model ids on
+  // shortcuts in the user's taskbar and start menu. This keeps older pinned
+  // shortcuts grouped correctly after major updates. Note, we also do this
+  // through the upgrade installer script, however, this is the only place we
+  // have a chance to trap links created by users who do control the install/
+  // update process of the browser.
+
+  nsCOMPtr<nsIWinTaskbar> taskbarInfo = do_GetService(NS_TASKBAR_CONTRACTID);
+  if (!taskbarInfo) // If we haven't built with win7 sdk features, this fails.
+    return NS_OK;
+
+  // Avoid if this isn't Win7+
+  PRBool isSupported = PR_FALSE;
+  taskbarInfo->GetAvailable(&isSupported);
+  if (!isSupported)
+    return NS_OK;
+
+  nsAutoString appId;
+  if (NS_FAILED(taskbarInfo->GetDefaultGroupId(appId)))
+    return NS_ERROR_UNEXPECTED;
+
+  NS_NAMED_LITERAL_CSTRING(prefName, "mail.taskbar.lastgroupid");
+  nsCOMPtr<nsIPrefService> prefs =
+    do_GetService(NS_PREFSERVICE_CONTRACTID);
+  if (!prefs)
+    return NS_ERROR_UNEXPECTED;
+
+  nsCOMPtr<nsIPrefBranch> prefBranch;
+  prefs->GetBranch(nsnull, getter_AddRefs(prefBranch));
+  if (!prefBranch)
+    return NS_ERROR_UNEXPECTED;
+
+  nsCOMPtr<nsISupportsString> prefString;
+  rv = prefBranch->GetComplexValue(prefName.get(),
+                                   NS_GET_IID(nsISupportsString),
+                                   getter_AddRefs(prefString));
+  if (NS_SUCCEEDED(rv)) {
+    nsAutoString version;
+    prefString->GetData(version);
+    if (!version.IsEmpty() && version.Equals(appId)) {
+      // We're all good, get out of here.
+      return NS_OK;
+    }
+  }
+  // Update the version in prefs
+  prefString =
+    do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
+  if (NS_FAILED(rv))
+    return rv;
+
+  prefString->SetData(appId);
+  rv = prefBranch->SetComplexValue(prefName.get(),
+                                   NS_GET_IID(nsISupportsString),
+                                   prefString);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Couldn't set last user model id!");
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  nsAutoString appHelperPath;
+  if (NS_FAILED(GetHelperPath(appHelperPath)))
+    return NS_ERROR_UNEXPECTED;
+
+  nsAutoString params;
+  params.AssignLiteral(" /UpdateShortcutAppUserModelIds");
+  return LaunchHelper(appHelperPath, params);
+}
+
 nsresult nsWindowsShellService::Init()
 {
   nsresult rv;
 
   PRUnichar appPath[MAX_BUF];
   if (!::GetModuleFileNameW(0, appPath, MAX_BUF))
     return NS_ERROR_FAILURE;
 
@@ -197,69 +321,36 @@ nsWindowsShellService::IsDefaultClient(P
 //    *aIsDefaultClient &= TestForDefault(gFeedSettings, sizeof(gFeedSettings)/sizeof(SETTING));
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsWindowsShellService::SetDefaultClient(PRBool aForAllUsers, PRUint16 aApps)
 {
-  nsresult rv;
-  nsCOMPtr<nsIProperties> directoryService = 
-    do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsILocalFile> appHelper;
-  rv = directoryService->Get(NS_XPCOM_CURRENT_PROCESS_DIR, NS_GET_IID(nsILocalFile), getter_AddRefs(appHelper));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = appHelper->AppendNative(NS_LITERAL_CSTRING("uninstall"));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = appHelper->AppendNative(NS_LITERAL_CSTRING("helper.exe"));
-  NS_ENSURE_SUCCESS(rv, rv);
-
   nsAutoString appHelperPath;
-  rv = appHelper->GetPath(appHelperPath);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_FAILED(GetHelperPath(appHelperPath)))
+    return NS_ERROR_FAILURE;
 
   nsAutoString params;
   if (aForAllUsers)
   {
     params.AppendLiteral(" /SetAsDefaultAppGlobal");
   }
   else
   {
     params.AppendLiteral(" /SetAsDefaultAppUser");
     if (aApps & nsIShellService::MAIL)
       params.AppendLiteral(" Mail");
 
     if (aApps & nsIShellService::NEWS)
       params.AppendLiteral(" News");
   }
 
-  SHELLEXECUTEINFOW executeInfo = {0};
-
-  executeInfo.cbSize = sizeof(SHELLEXECUTEINFOW);
-  executeInfo.hwnd = NULL;
-  executeInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
-  executeInfo.lpDirectory = NULL;
-  executeInfo.lpFile = appHelperPath.get();
-  executeInfo.lpParameters = params.get();
-  executeInfo.nShow = SW_SHOWNORMAL;
-
-  if (ShellExecuteExW(&executeInfo))
-    // Block until the program exits
-    WaitForSingleObject(executeInfo.hProcess, INFINITE);
-  else
-    return NS_ERROR_ABORT;
-
-  // We're going to ignore errors here since there's nothing we can do about
-  // them, and helper.exe seems to return non-zero ret on success.
-  return NS_OK;
+  return LaunchHelper(appHelperPath, params);
 }
 
 NS_IMETHODIMP
 nsWindowsShellService::GetShouldCheckDefaultClient(PRBool* aResult)
 {
   if (mCheckedThisSession)
   {
     *aResult = PR_FALSE;
--- a/mail/components/shell/nsMailWinIntegration.h
+++ b/mail/components/shell/nsMailWinIntegration.h
@@ -33,17 +33,17 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsMailWinIntegration_h_
 #define nsMailWinIntegration_h_
 
-#include "nsIShellService.h"
+#include "nsIWindowsShellService.h"
 #include "nsIObserver.h"
 #include "nsString.h"
 
 #include <ole2.h>
 #include <windows.h>
 
 #define NS_MAILWININTEGRATION_CID \
 {0x2ebbe84, 0xc179, 0x4598, {0xaf, 0x18, 0x1b, 0xf2, 0xc4, 0xbc, 0x1d, 0xf9}}
@@ -51,25 +51,26 @@
 typedef struct {
   char* keyName;
   char* valueName;
   char* valueData;
 
   PRInt32 flags;
 } SETTING;
 
-class nsWindowsShellService : public nsIShellService
+class nsWindowsShellService : public nsIWindowsShellService
 {
 public:
   nsWindowsShellService();
   virtual ~nsWindowsShellService() {};
   NS_HIDDEN_(nsresult) Init();
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSISHELLSERVICE
+  NS_DECL_NSIWINDOWSSHELLSERVICE
 
 protected:
   PRBool TestForDefault(SETTING aSettings[], PRInt32 aSize);
   PRBool IsDefaultClientVista(PRUint16 aApps, PRBool* aIsDefaultClient);
 
 private:
   PRBool mCheckedThisSession;
   nsAutoString mAppLongPath;
--- a/mail/components/shell/public/Makefile.in
+++ b/mail/components/shell/public/Makefile.in
@@ -40,11 +40,11 @@ topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH   = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE = shellservice
 XPIDL_MODULE  = shellservice
 
-XPIDLSRCS = nsIShellService.idl
+XPIDLSRCS = nsIShellService.idl nsIWindowsShellService.idl
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/mail/components/shell/public/nsIWindowsShellService.idl
@@ -0,0 +1,48 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Shell Service.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Siddharth Agarwal <sid.bugzilla@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsIShellService.idl"
+
+[scriptable, uuid(c6bcecde-6f2e-417c-bc30-466f560c3b51)]
+interface nsIWindowsShellService : nsIShellService
+{
+  /**
+   * Perform some Windows 7+ shortcut maintenance. This is called at program startup.
+   */
+  void shortcutMaintenance();
+};
--- a/mail/components/wintaskbar/windowsJumpLists.js
+++ b/mail/components/wintaskbar/windowsJumpLists.js
@@ -56,16 +56,20 @@ XPCOMUtils.defineLazyGetter(this, "_stri
   return Services.strings
                  .createBundle("chrome://messenger/locale/taskbar.properties");
 });
 
 XPCOMUtils.defineLazyServiceGetter(this, "_taskbarService",
                                    "@mozilla.org/windows-taskbar;1",
                                    "nsIWinTaskbar");
 
+XPCOMUtils.defineLazyServiceGetter(this, "_winShellService",
+                                   "@mozilla.org/mail/shell-service;1",
+                                   "nsIWindowsShellService");
+
 XPCOMUtils.defineLazyGetter(this, "_prefs", function() {
   return Services.prefs.getBranch(PREF_TASKBAR_BRANCH)
                        .QueryInterface(Ci.nsIPrefBranch2);
 });
 
 function _getString(aName) {
   return _stringBundle.GetStringFromName(aName);
 }
@@ -98,16 +102,26 @@ let WinTaskbarJumpList = {
    * Startup, shutdown, and update
    */
 
   startup: function WTBJL_startup() {
     // exit if this isn't win7 or higher.
     if (!this._initTaskbar())
       return;
 
+    // Win shell shortcut maintenance. If we've gone through an update,
+    // this will update any pinned taskbar shortcuts. Not specific to
+    // jump lists, but this was a convienent place to call it. 
+    try {
+      // dev builds may not have helper.exe, ignore failures.
+      this._shortcutMaintenance();
+    }
+    catch (ex) {
+    }
+
     // Store our task list config data
     this._tasks = gTasks;
 
     // retrieve taskbar related prefs.
     this._refreshPrefs();
 
     // observer for our prefs branch
     this._initObs();
@@ -125,16 +139,20 @@ let WinTaskbarJumpList = {
   },
 
   _shutdown: function WTBJL__shutdown() {
     this._shuttingDown = true;
 
     this._free();
   },
 
+  _shortcutMaintenance: function WTBJL__maintenace() {
+    _winShellService.shortcutMaintenance();
+  },
+
   /**
    * List building
    */
 
   _buildList: function WTBJL__buildList() {
     // anything to build?
     if (!this._showTasks) {
       // don't leave the last list hanging on the taskbar.
--- a/mail/installer/windows/nsis/defines.nsi.in
+++ b/mail/installer/windows/nsis/defines.nsi.in
@@ -2,17 +2,19 @@
 
 # Win7: AppVendor, AppName, and AppVersion must match the application.ini values
 # of Vendor, Name, and Version. These values are used in registering shortcuts
 # with the taskbar. ExplicitAppUserModelID registration when the app launches is
 # handled in widget/src/windows/WinTaskbar.cpp.
 # Note - Thunderbird does not set a Vendor in application.ini!
 !define AppName               "Thunderbird"
 !define AppVersion            "@MOZ_APP_VERSION@"
-!define AppUserModelID        "${AppName}.${AppVersion}"
+# The app vendor is blank, and the way app model IDs are formed is with
+# vendor.name.version.
+!define AppUserModelID        ".${AppName}.${AppVersion}"
 !define GREVersion            @MOZILLA_VERSION@
 !define AB_CD                 "@AB_CD@"
 
 !define FileMainEXE           "@MOZ_APP_NAME@.exe"
 !define WindowClass           "ThunderbirdMessageWindow"
 
 !define AppRegNameMail        "Thunderbird"
 !define AppRegNameNews        "Thunderbird (News)"
--- a/mail/installer/windows/nsis/installer.nsi
+++ b/mail/installer/windows/nsis/installer.nsi
@@ -109,16 +109,17 @@ VIAddVersionKey "OriginalFilename" "setu
 !insertmacro IsHandlerForInstallDir
 !insertmacro LogDesktopShortcut
 !insertmacro LogQuickLaunchShortcut
 !insertmacro LogStartMenuShortcut
 !insertmacro ManualCloseAppPrompt
 !insertmacro RegCleanMain
 !insertmacro RegCleanUninstall
 !insertmacro SetBrandNameVars
+!insertmacro UpdateShortcutAppModelIDs
 !insertmacro UnloadUAC
 !insertmacro WriteRegStr2
 !insertmacro WriteRegDWORD2
 
 !include shared.nsh
 
 ; Helper macros for ui callbacks. Insert these after shared.nsh
 !insertmacro CheckCustomCommon
@@ -396,16 +397,39 @@ Section "-Application" APP_IDX
 
   ; Always add the application's shortcuts to the shortcuts log ini file. The
   ; DeleteShortcuts macro will do the right thing on uninstall if the
   ; shortcuts don't exist.
   ${LogStartMenuShortcut} "${BrandFullName}.lnk"
   ${LogQuickLaunchShortcut} "${BrandFullName}.lnk"
   ${LogDesktopShortcut} "${BrandFullName}.lnk"
 
+  ; Best effort to update the Win7 taskbar and start menu shortcut app model
+  ; id's. The possible contexts are current user / system and the user that
+  ; elevated the installer.
+  Call FixShortcutAppModelIDs
+  ; If the current context is all also perform Win7 taskbar and start menu link
+  ; maintenance for the current user context.
+  ${If} $TmpVal == "HKLM"
+    SetShellVarContext current  ; Set SHCTX to HKCU
+    Call FixShortcutAppModelIDs
+    SetShellVarContext all  ; Set SHCTX to HKLM
+  ${EndIf}
+
+  ; If running elevated also perform Win7 taskbar and start menu link
+  ; maintenance for the unelevated user context in case that is different than
+  ; the current user.
+  ClearErrors
+  ${GetParameters} $0
+  ${GetOptions} "$0" "/UAC:" $0
+  ${Unless} ${Errors}
+    GetFunctionAddress $0 FixShortcutAppModelIDs
+    UAC::ExecCodeSegment $0
+  ${EndIf}
+
   ; UAC only allows elevating to an Admin account so there is no need to add
   ; the Start Menu or Desktop shortcuts from the original unelevated process
   ; since this will either add it for the user if unelevated or All Users if
   ; elevated.
   ${If} $AddStartMenuSC == 1
     CreateShortCut "$SMPROGRAMS\${BrandFullName}.lnk" "$INSTDIR\${FileMainEXE}" \
                    "" "$INSTDIR\${FileMainEXE}" 0
     ShellLink::SetShortCutWorkingDirectory "$SMPROGRAMS\${BrandFullName}.lnk" \
@@ -460,16 +484,19 @@ Section "-InstallEndCleanup"
         Call SetAsDefaultMailAppUserHKCU
       ${Else}
         GetFunctionAddress $0 SetAsDefaultMailAppUserHKCU
         UAC::ExecCodeSegment $0
       ${EndIf}
     ${EndIf}
   ${EndUnless}
 
+  ; Win7 taskbar and start menu link maintenance
+  Call FixShortcutAppModelIDs
+
   ; Refresh desktop icons
   System::Call "shell32::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)"
 
   ${InstallEndCleanupCommon}
 
   ${If} ${RebootFlag}
     ; If we have to reboot give SHChangeNotify time to finish refreshing
     ; the icons so the OS doesn't display the icons from helper.exe
--- a/mail/installer/windows/nsis/shared.nsh
+++ b/mail/installer/windows/nsis/shared.nsh
@@ -40,16 +40,18 @@
 
   ; Remove registry entries for non-existent apps and for apps that point to our
   ; install location in the Software\Mozilla key and uninstall registry entries
   ; that point to our install location for both HKCU and HKLM.
   SetShellVarContext current  ; Set SHCTX to the current user (e.g. HKCU)
   ${RegCleanMain} "Software\Mozilla"
   ${RegCleanUninstall}
   ${UpdateProtocolHandlers}
+  ; Win7 taskbar and start menu link maintenance
+  Call FixShortcutAppModelIDs
 
   ; Upgrade the copies of the MAPI DLL's
   ${UpgradeMapiDLLs}
 
   ; Delete two files installed by Kaspersky Anti-Spam extension that are only
   ; compatible with Thunderbird 2 (bug 533692).
   ${If} ${FileExists} "$INSTDIR\components\klthbplg.dll"
     Delete /REBOOTOK "$INSTDIR\components\klthbplg.dll"
@@ -65,16 +67,19 @@
   ${Else}
     DeleteRegValue HKLM "Software\Mozilla" "${BrandShortName}InstallerTest"
     SetShellVarContext all    ; Set SHCTX to all users (e.g. HKLM)
     StrCpy $TmpVal "HKLM" ; used primarily for logging
     ${RegCleanMain} "Software\Mozilla"
     ${RegCleanUninstall}
     ${UpdateProtocolHandlers}
 
+    ; Win7 taskbar and start menu link maintenance
+    Call FixShortcutAppModelIDs
+
     ; Only update the Clients\Mail registry key values if they don't exist or
     ; this installation is the same as the one set in those keys.
     ReadRegStr $0 HKLM "Software\Clients\Mail\${ClientsRegName}\DefaultIcon" ""
     ${GetPathFromString} "$0" $0
     ${GetParent} "$0" $0
     ${If} ${FileExists} "$0"
       ${GetLongPath} "$0" $0
     ${EndIf}
@@ -724,16 +729,21 @@
   Push "MapiProxy.dll"
   Push "MapiProxy_InUse.dll"
   Push "mozMapi32.dll"
   Push "mozMapi32_InUse.dll"
   Push "${FileMainEXE}"
 !macroend
 !define PushFilesToCheck "!insertmacro PushFilesToCheck"
 
+; Helper for updating the shortcut application model IDs.
+Function FixShortcutAppModelIDs
+  ${UpdateShortcutAppModelIDs} "$INSTDIR\${FileMainEXE}" "${AppUserModelID}" $0
+FunctionEnd
+
 ; The !ifdef NO_LOG prevents warnings when compiling the installer.nsi due to
 ; this function only being used by the uninstaller.nsi.
 !ifdef NO_LOG
 
 Function SetAsDefaultAppUser
   ; It is only possible to set this installation of the application as the
   ; Mail handler if it was added to the HKLM Mail
   ; registry keys.