Compare commits

...

1 Commits

Author SHA1 Message Date
PointStar 870426ec08 fix(updater): invoke VSTOInstaller.exe directly so Chrome doesn't hijack the .vsto
v1.2.3 changed the updater to Process.Start the .vsto URL with
UseShellExecute=true. On any browser-as-default-handler (Chrome / Edge)
that downloads the file to disk instead of handing it to VSTOInstaller,
and re-opening the local copy then fails with InvalidDeploymentException
"לפריסה וליישום אין אזורי אבטחה תואמים" — the downloaded file lives in
the Local Machine zone while the manifest declares the Internet zone.

The reliable path is to invoke VSTOInstaller.exe directly with /I and
the manifest URL. The URL is then processed in the Internet zone,
matching what's declared in the manifest. FindVstoInstaller() looks at
the canonical VSTO 4.0 path and falls back to the HKCR registration of
the .vsto extension. If even that fails, we tell the user to close and
reopen Outlook — the VSTO runtime picks up the new manifest on its own
during startup, so the worst case is just an extra restart, not a stuck
install.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 20:07:58 +03:00
3 changed files with 76 additions and 15 deletions
@@ -216,22 +216,39 @@ namespace MarcusLaw.OutlookAddin.UI.ViewModels
// VSTO add-ins cannot be updated programmatically from inside // VSTO add-ins cannot be updated programmatically from inside
// a running Outlook — ApplicationDeployment.Update() throws. // a running Outlook — ApplicationDeployment.Update() throws.
// Open the .vsto URL via the shell; Windows hands it to // Don't shell-launch the .vsto URL either: Chrome / Edge will
// VSTOInstaller.exe which installs the update correctly. // download it to disk, and the resulting file has a different
try // security zone than the deployment manifest, which fails with
// InvalidDeploymentException ("no matching security zones").
// Call VSTOInstaller.exe directly so the URL is processed in
// the Internet zone, matching the manifest.
var installerPath = FindVstoInstaller();
if (installerPath != null)
{ {
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(manifestUri.ToString()) try
{ {
UseShellExecute = true System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(
}); installerPath, "/I \"" + manifestUri + "\"")
UpdateStatusBrush = SuccessBrush; {
UpdateStatus = $"זמינה גרסה {availableVersion} — אשר את ההתקנה בחלון שנפתח, ואז סגור ופתח את Outlook."; UseShellExecute = false
});
UpdateStatusBrush = SuccessBrush;
UpdateStatus = $"זמינה גרסה {availableVersion} — אשר את ההתקנה בחלון שנפתח, ואז סגור ופתח את Outlook.";
}
catch (Exception launchEx)
{
_logger.Warning(launchEx, "CheckForUpdate: VSTOInstaller launch failed");
UpdateStatusBrush = ErrorBrush;
UpdateStatus = $"זמינה גרסה {availableVersion} — סגור ופתח את Outlook כדי להתקין אוטומטית.";
}
} }
catch (Exception launchEx) else
{ {
_logger.Warning(launchEx, "CheckForUpdate: shell launch of installer URL failed"); // VSTOInstaller not at the canonical path — VSTO runtime
UpdateStatusBrush = ErrorBrush; // still picks up new manifests on Outlook startup, so
UpdateStatus = $"זמינה גרסה {availableVersion} אבל לא הצלחנו לפתוח את חלון ההתקנה. גש ידנית ל-{manifestUri}"; // ask the user to restart instead of attempting a hack.
UpdateStatusBrush = SuccessBrush;
UpdateStatus = $"זמינה גרסה {availableVersion} — סגור ופתח את Outlook כדי להתקין אוטומטית.";
} }
return; return;
} }
@@ -286,6 +303,50 @@ namespace MarcusLaw.OutlookAddin.UI.ViewModels
} }
} }
/// <summary>
/// Locates VSTOInstaller.exe on the machine. The path is stable
/// for VSTO 4.0 (Office 2010+) — falls back to a registry lookup
/// if the default install location was customised.
/// </summary>
private static string? FindVstoInstaller()
{
string[] candidates =
{
@"C:\Program Files (x86)\Common Files\Microsoft Shared\VSTO\10.0\VSTOInstaller.exe",
@"C:\Program Files\Common Files\Microsoft Shared\VSTO\10.0\VSTOInstaller.exe"
};
foreach (var p in candidates)
{
if (System.IO.File.Exists(p)) return p;
}
// Registry fallback: HKCR\VstoFile\shell\Open\command stores
// the verb VSTOInstaller registers for the .vsto extension.
try
{
using (var key = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(@"VstoFile\shell\Open\command"))
{
var cmd = key?.GetValue(null) as string;
if (!string.IsNullOrEmpty(cmd))
{
// Value looks like: "C:\...\VSTOInstaller.exe" "%1"
var firstQuote = cmd!.IndexOf('"');
if (firstQuote == 0)
{
var endQuote = cmd.IndexOf('"', 1);
if (endQuote > 1)
{
var exe = cmd.Substring(1, endQuote - 1);
if (System.IO.File.Exists(exe)) return exe;
}
}
}
}
}
catch { /* registry access denied or schema differs — fall through */ }
return null;
}
private bool CanCheckForUpdate() => !IsCheckingForUpdate; private bool CanCheckForUpdate() => !IsCheckingForUpdate;
partial void OnIsCheckingForUpdateChanged(bool value) partial void OnIsCheckingForUpdateChanged(bool value)
+1 -1
View File
@@ -36,7 +36,7 @@
<PublishUrl>C:\Users\Chaim\source\repos\OutlookAddin\Publish\</PublishUrl> <PublishUrl>C:\Users\Chaim\source\repos\OutlookAddin\Publish\</PublishUrl>
<InstallUrl>https://gitea.dev.marcus-law.co.il/espocrm-extensions/OutlookAddin/raw/branch/main/Publish/</InstallUrl> <InstallUrl>https://gitea.dev.marcus-law.co.il/espocrm-extensions/OutlookAddin/raw/branch/main/Publish/</InstallUrl>
<TargetCulture>en</TargetCulture> <TargetCulture>en</TargetCulture>
<ApplicationVersion>1.2.4.0</ApplicationVersion> <ApplicationVersion>1.2.5.0</ApplicationVersion>
<AutoIncrementApplicationRevision>false</AutoIncrementApplicationRevision> <AutoIncrementApplicationRevision>false</AutoIncrementApplicationRevision>
<UpdateEnabled>true</UpdateEnabled> <UpdateEnabled>true</UpdateEnabled>
<UpdateInterval>7</UpdateInterval> <UpdateInterval>7</UpdateInterval>
+2 -2
View File
@@ -33,6 +33,6 @@ using System.Security;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.2.4.0")] [assembly: AssemblyVersion("1.2.5.0")]
[assembly: AssemblyFileVersion("1.2.4.0")] [assembly: AssemblyFileVersion("1.2.5.0")]