|
|
|
@@ -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,61 @@ 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 = "אתה על הגרסה האחרונה (" + currentVersion + ").";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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 = $"הותקנה גרסה {info.AvailableVersion}. סגור ופתח את Outlook כדי לטעון אותה.";
|
|
|
|
|
UpdateStatus =
|
|
|
|
|
$"זמינה גרסה {availableVersion}. סגור ופתח את Outlook — " +
|
|
|
|
|
"הגרסה החדשה תותקן אוטומטית בעלייה.";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
catch (System.Deployment.Application.DeploymentDownloadException ex)
|
|
|
|
|
{
|
|
|
|
@@ -215,6 +259,33 @@ 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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool CanCheckForUpdate() => !IsCheckingForUpdate;
|
|
|
|
|
|
|
|
|
|
partial void OnIsCheckingForUpdateChanged(bool value)
|
|
|
|
|