fix(updater): stop trying to install in-process; tell user to restart Outlook
v1.2.6 fired VSTOInstaller.exe /I directly. It returned 0x8007007E
(ERROR_MOD_NOT_FOUND) — the loaded add-in DLLs are file-locked by the
running Outlook, so even the canonical installer can't overwrite them
while the host is up. This caps a series of failed attempts:
- 1.2.0/1/2: ApplicationDeployment.Update() → TrustNotGrantedException
- 1.2.3/4: Process.Start(.vsto URL, shell) → Chrome downloads to
local-machine zone → InvalidDeploymentException
- 1.2.5/6: VSTOInstaller.exe /I <url> → 0x8007007E file lock
There is no API that updates a running VSTO add-in. The VSTO runtime's
own auto-update path (check manifest on Outlook startup, install before
loading) is the only mechanism that works — and it already does, every
time the user restarts Outlook.
So the button now just detects the new version via manifest HTTP GET
and displays "זמינה גרסה X. סגור ופתח את Outlook — הגרסה החדשה תותקן
אוטומטית בעלייה." No install attempt, no spawned process, no zone or
trust dance. The status line is honest about what the user has to do.
Removed the now-unused FindVstoInstaller registry-walk helper.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -214,42 +214,25 @@ namespace MarcusLaw.OutlookAddin.UI.ViewModels
|
||||
return;
|
||||
}
|
||||
|
||||
// VSTO add-ins cannot be updated programmatically from inside
|
||||
// a running Outlook — ApplicationDeployment.Update() throws.
|
||||
// Don't shell-launch the .vsto URL either: Chrome / Edge will
|
||||
// download it to disk, and the resulting file has a different
|
||||
// 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)
|
||||
{
|
||||
try
|
||||
{
|
||||
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(
|
||||
installerPath, "/I \"" + manifestUri + "\"")
|
||||
{
|
||||
UseShellExecute = false
|
||||
});
|
||||
// A running Outlook holds file locks on the loaded add-in
|
||||
// DLLs. Neither ApplicationDeployment.Update (TrustNotGranted)
|
||||
// nor a direct VSTOInstaller.exe /I call (0x8007007E
|
||||
// ERROR_MOD_NOT_FOUND) nor Process.Start(.vsto URL) (Chrome
|
||||
// downloads to local-machine zone — InvalidDeploymentException)
|
||||
// can install over those locks. The only path that actually
|
||||
// works is the one VSTO runtime takes automatically on Outlook
|
||||
// startup: it checks the manifest URL before loading the
|
||||
// add-in, downloads + installs if there's a newer version,
|
||||
// then loads it.
|
||||
//
|
||||
// So the button's job is just to tell the user: "close and
|
||||
// reopen Outlook." That's the same flow the user has been
|
||||
// doing manually all along; we're just confirming the new
|
||||
// version is there waiting.
|
||||
UpdateStatusBrush = SuccessBrush;
|
||||
UpdateStatus = $"זמינה גרסה {availableVersion} — אשר את ההתקנה בחלון שנפתח, ואז סגור ופתח את Outlook.";
|
||||
}
|
||||
catch (Exception launchEx)
|
||||
{
|
||||
_logger.Warning(launchEx, "CheckForUpdate: VSTOInstaller launch failed");
|
||||
UpdateStatusBrush = ErrorBrush;
|
||||
UpdateStatus = $"זמינה גרסה {availableVersion} — סגור ופתח את Outlook כדי להתקין אוטומטית.";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// VSTOInstaller not at the canonical path — VSTO runtime
|
||||
// still picks up new manifests on Outlook startup, so
|
||||
// ask the user to restart instead of attempting a hack.
|
||||
UpdateStatusBrush = SuccessBrush;
|
||||
UpdateStatus = $"זמינה גרסה {availableVersion} — סגור ופתח את Outlook כדי להתקין אוטומטית.";
|
||||
}
|
||||
UpdateStatus =
|
||||
$"זמינה גרסה {availableVersion}. סגור ופתח את Outlook — " +
|
||||
"הגרסה החדשה תותקן אוטומטית בעלייה.";
|
||||
return;
|
||||
}
|
||||
catch (System.Deployment.Application.DeploymentDownloadException ex)
|
||||
@@ -303,50 +286,6 @@ 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;
|
||||
|
||||
partial void OnIsCheckingForUpdateChanged(bool value)
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
<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>
|
||||
<TargetCulture>en</TargetCulture>
|
||||
<ApplicationVersion>1.2.6.0</ApplicationVersion>
|
||||
<ApplicationVersion>1.2.7.0</ApplicationVersion>
|
||||
<AutoIncrementApplicationRevision>false</AutoIncrementApplicationRevision>
|
||||
<UpdateEnabled>true</UpdateEnabled>
|
||||
<UpdateInterval>7</UpdateInterval>
|
||||
|
||||
@@ -33,6 +33,6 @@ using System.Security;
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.2.6.0")]
|
||||
[assembly: AssemblyFileVersion("1.2.6.0")]
|
||||
[assembly: AssemblyVersion("1.2.7.0")]
|
||||
[assembly: AssemblyFileVersion("1.2.7.0")]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user