|
|
|
@@ -1,6 +1,7 @@
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Collections.ObjectModel;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Net.Http;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using System.Windows.Media;
|
|
|
|
@@ -178,18 +179,78 @@ namespace MarcusLaw.OutlookAddin.UI.ViewModels
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var deployment = System.Deployment.Application.ApplicationDeployment.CurrentDeployment;
|
|
|
|
|
var info = await Task.Run(() => deployment.CheckForDetailedUpdate(false)).ConfigureAwait(true);
|
|
|
|
|
if (!info.UpdateAvailable)
|
|
|
|
|
var currentVersion = deployment.CurrentVersion;
|
|
|
|
|
var manifestUri = deployment.UpdateLocation;
|
|
|
|
|
|
|
|
|
|
// ApplicationDeployment.CheckForDetailedUpdate triggers a
|
|
|
|
|
// TrustManager re-evaluation which throws TrustNotGranted
|
|
|
|
|
// inside a hosted VSTO process even when the publisher and
|
|
|
|
|
// permissions are unchanged. Bypass it by fetching the
|
|
|
|
|
// deployment manifest directly and reading the version.
|
|
|
|
|
Version? availableVersion = null;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
UpdateStatusBrush = SuccessBrush;
|
|
|
|
|
UpdateStatus = "אתה על הגרסה האחרונה (" + deployment.CurrentVersion + ").";
|
|
|
|
|
availableVersion = await Task.Run(() => FetchManifestVersion(manifestUri)).ConfigureAwait(true);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception fetchEx)
|
|
|
|
|
{
|
|
|
|
|
_logger.Warning(fetchEx, "CheckForUpdate: manifest fetch failed");
|
|
|
|
|
UpdateStatusBrush = ErrorBrush;
|
|
|
|
|
UpdateStatus = "שגיאה בבדיקת השרת: " + fetchEx.Message;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UpdateStatus = $"זמינה גרסה {info.AvailableVersion} — מוריד…";
|
|
|
|
|
var result = await Task.Run(() => deployment.Update()).ConfigureAwait(true);
|
|
|
|
|
if (availableVersion == null)
|
|
|
|
|
{
|
|
|
|
|
UpdateStatusBrush = ErrorBrush;
|
|
|
|
|
UpdateStatus = "לא ניתן לקרוא את גרסת השרת מ-" + manifestUri;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (availableVersion <= currentVersion)
|
|
|
|
|
{
|
|
|
|
|
UpdateStatusBrush = SuccessBrush;
|
|
|
|
|
UpdateStatus = $"הותקנה גרסה {info.AvailableVersion}. סגור ופתח את Outlook כדי לטעון אותה.";
|
|
|
|
|
UpdateStatus = "אתה על הגרסה האחרונה (" + currentVersion + ").";
|
|
|
|
|
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
|
|
|
|
|
});
|
|
|
|
|
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 כדי להתקין אוטומטית.";
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
catch (System.Deployment.Application.DeploymentDownloadException ex)
|
|
|
|
|
{
|
|
|
|
@@ -215,6 +276,77 @@ namespace MarcusLaw.OutlookAddin.UI.ViewModels
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Reads the top-level <assemblyIdentity> version from the
|
|
|
|
|
/// .vsto deployment manifest at <paramref name="manifestUri"/>.
|
|
|
|
|
/// Returns null if the document doesn't look like a deployment
|
|
|
|
|
/// manifest. Runs on a worker thread (synchronous HTTP).
|
|
|
|
|
/// </summary>
|
|
|
|
|
private static Version? FetchManifestVersion(Uri manifestUri)
|
|
|
|
|
{
|
|
|
|
|
// The .vsto manifest is small (≈6KB). Keep this synchronous —
|
|
|
|
|
// no point pulling in HttpClient and its lifecycle just for one
|
|
|
|
|
// GET. WebRequest works fine on .NET FW 4.8 and through proxies.
|
|
|
|
|
var req = System.Net.WebRequest.Create(manifestUri);
|
|
|
|
|
req.Timeout = 10_000;
|
|
|
|
|
using (var response = req.GetResponse())
|
|
|
|
|
using (var stream = response.GetResponseStream())
|
|
|
|
|
{
|
|
|
|
|
var doc = System.Xml.Linq.XDocument.Load(stream);
|
|
|
|
|
System.Xml.Linq.XNamespace asmv1 = "urn:schemas-microsoft-com:asm.v1";
|
|
|
|
|
var identity = doc.Root?
|
|
|
|
|
.Elements()
|
|
|
|
|
.FirstOrDefault(e => e.Name.LocalName == "assemblyIdentity" && e.Name.Namespace == asmv1);
|
|
|
|
|
var versionAttr = identity?.Attribute("version")?.Value;
|
|
|
|
|
if (string.IsNullOrWhiteSpace(versionAttr)) return null;
|
|
|
|
|
return Version.TryParse(versionAttr, out var v) ? v : null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <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)
|
|
|
|
|