|
//---------------------------------------------------------------------------
//
// <copyright file="DeploymentExceptionMapper.cs" company="Microsoft">
// Copyright (C) Microsoft Corporation. All rights reserved.
// </copyright>
//
// Description: DeploymentExceptionMapper class definition.
//
// History:
// 2005/02/08 : MingChan - Created
// 2005/08/22 : MingChan - Added boot WinFX functionality
// 2007/08/01 : Microsoft - Added detection of System.Core.dll to infer required platform version.
// Because WPF v3.5 still has WindowsBase v3.0.0.0, the original heuristic failed.
// 2008/04/08 : Microsoft - Update for v3.5 SP1. ClickOnce has bestowed upon us two more sentinel assemblies!
// > Sentinel.v3.5Client: for applications explicitly targeting the "client"
// ("Arrowhead") subset of the framework;
// > System.Data.Entity: for applications targeting the full v3.5 SP1.
// (It would have been much better if they agreed to just add System.Core v3.5.1
// and v3.5.2 instead, which would allow v3.0 SP1/v3.5 to properly bootstrap
// applications targeting this new release...)
//
//---------------------------------------------------------------------------
using System;
using System.Collections;
using System.Globalization;
using System.Resources;
using System.Reflection;
using System.Windows;
using System.Windows.Interop;
using System.Deployment.Application;
using System.Text.RegularExpressions;
namespace MS.Internal
{
internal enum MissingDependencyType
{
Others = 0,
WinFX = 1,
CLR = 2
}
internal static class DeploymentExceptionMapper
{
// This is the hardcoded fwlink query parameters, the only dynamic data we pass to fwlink server
// is the WinFX or CLR version we parse from the ClickOnce error message.
// Product ID is always 11953 which is the WinFX Runtime Components and subproduct is always bootwinfx
// The winfxsetup.exe is language neutral so we always specify 0x409 for languages.
const string fwlinkPrefix = "http://go.microsoft.com/fwlink?prd=11953&sbp=Bootwinfx&pver=";
const string fwlinkSuffix = "&plcid=0x409&clcid=0x409&";
//------------------------------------------------------
//
// Internal Methods
//
//------------------------------------------------------
#region Internal Methods
// Check if the platform exception is due to missing WinFX or CLR dependency
// Parse the exception message and find out the dependent WinFX version and create the
// corresponding fwlink Uri.
static internal MissingDependencyType GetWinFXRequirement(Exception e,
InPlaceHostingManager hostingManager,
out string version,
out Uri fwlinkUri)
{
version = String.Empty;
fwlinkUri = null;
// ClickOnce detects whether it's running as part of the v3.5 "client" subset ("Arrowhead") and
// if so blocks older applications that don't explicitly opt into the subset framework.
// (Unfortunately, it does not block an application targeting the full 3.5 SP1 framework this way.)
// The exception message has ".NET Framework 3.5 SP1" hard-coded in it (referring to the version
// of the framework needed to run the application, which is not strictly right, but older versions
// can't/shouldn't be installed on top of the "client" subset).
// To make this exception message parsing at least potentially somewhat future-proof, a regex is
// used that allows for some variability of syntax and version number.
// We don't include the "SP1" part in the fwlink query. This is for consistency with the detection
// via sentinel assemblies. The server can be updated to offer the latest release/SP compatible
// with the requested major.minor version.
if (e is DependentPlatformMissingException)
{
Regex regex = new Regex(@".NET Framework (v\.?)?(?<version>\d{1,2}(\.\d{1,2})?)",
RegexOptions.ExplicitCapture|RegexOptions.IgnoreCase);
string msg = e.Message;
Match match = regex.Match(msg);
if(match.Success)
{
version = match.Groups[1].Value;
ConstructFwlinkUrl(version, out fwlinkUri);
return MissingDependencyType.WinFX;
}
}
// Load the clickonce resource and use it to parse the exception message
Assembly deploymentDll = Assembly.GetAssembly(hostingManager.GetType());
if (deploymentDll == null)
{
return MissingDependencyType.Others;
}
ResourceManager resourceManager = new ResourceManager("System.Deployment", deploymentDll);
if (resourceManager == null)
{
return MissingDependencyType.Others;
}
String clrProductName = resourceManager.GetString("PlatformMicrosoftCommonLanguageRuntime", CultureInfo.CurrentUICulture);
String versionString = resourceManager.GetString("PlatformDependentAssemblyVersion", CultureInfo.CurrentUICulture);
if ((clrProductName == null) || (versionString == null))
{
return MissingDependencyType.Others;
}
// Need to trim off the parameters in the ClickOnce strings:
// "{0} Version {1}" -> "Version"
// "Microsoft Common Language Runtime Version {0}" -> "Microsoft Common Language Runtime Version"
clrProductName = clrProductName.Replace("{0}", "");
versionString = versionString.Replace("{0}", "");
versionString = versionString.Replace("{1}", "");
string[] sentinelAssemblies = {
// The Original & "Only"
"WindowsBase",
// A reference to System.Core is what makes an application target .NET v3.5.
// Because WindowsBase still has v3.0.0.0, it's not the one that fails the platform requirements
// test when a v3.5 app is run on the v3 runtime. (This additional check added for v3 SP1.)
"System.Core",
// New sentinel assemblies for v3.5 SP1 (see the revision history)
"Sentinel.v3.5Client", "System.Data.Entity" };
// Parse the required version and trim it to major and minor only
string excpMsg = e.Message;
int index = excpMsg.IndexOf(versionString, StringComparison.Ordinal);
if (index != -1)
{
// ClickOnce exception message is ErrorMessage_Platform*
// from clickonce/system.deployment.txt
version = String.Copy(excpMsg.Substring(index + versionString.Length));
int indexToFirstDot = version.IndexOf(".", StringComparison.Ordinal);
int indexToSecondDot = version.IndexOf(".", indexToFirstDot+1, StringComparison.Ordinal);
if (excpMsg.IndexOf(clrProductName, StringComparison.Ordinal) != -1)
{
if (OperatingSystemVersionCheck.IsVersionOrLater(OperatingSystemVersion.Windows8))
{
// CLR version are Major.Minor.Revision
// Defense in depth here in case CLR changes the version scheme to major + minor only
// and we might never see the third dot
int indexToThirdDot = version.IndexOf(".", indexToSecondDot+1, StringComparison.Ordinal);
if (indexToThirdDot != -1)
{
version = version.Substring(0, indexToThirdDot);
}
}
else if (indexToSecondDot != -1)
{
// Defense in depth here in case Avalon change the version scheme to major + minor only
// and we might never see the second dot
version = version.Substring(0, indexToSecondDot);
}
// prepend CLR to distinguish CLR version fwlink query
// vs. WinFX version query.
string clrVersion = String.Concat("CLR", version);
return (ConstructFwlinkUrl(clrVersion, out fwlinkUri) ? MissingDependencyType.CLR : MissingDependencyType.Others);
}
else
{
if (indexToSecondDot != -1)
{
// Defense in depth here in case Avalon change the version scheme to major + minor only
// and we might never see the second dot
version = version.Substring(0, indexToSecondDot);
}
bool sentinelMissing = false;
foreach (string sentinelAssembly in sentinelAssemblies)
{
if (excpMsg.IndexOf(sentinelAssembly, StringComparison.OrdinalIgnoreCase) > 0)
{
sentinelMissing = true;
break;
}
}
if (!sentinelMissing)
{
version = String.Empty;
}
}
}
return (ConstructFwlinkUrl(version, out fwlinkUri) ? MissingDependencyType.WinFX : MissingDependencyType.Others);
}
static internal void GetErrorTextFromException(Exception e, out string errorTitle, out string errorMessage)
{
errorTitle = String.Empty;
errorMessage = String.Empty;
if (e == null)
{
errorTitle = SR.Get(SRID.CancelledTitle);
errorMessage = SR.Get(SRID.CancelledText);
}
else if (e is DependentPlatformMissingException)
{
errorTitle = SR.Get(SRID.PlatformRequirementTitle);
errorMessage = e.Message;
}
else if (e is InvalidDeploymentException)
{
errorTitle = SR.Get(SRID.InvalidDeployTitle);
errorMessage = SR.Get(SRID.InvalidDeployText);
}
else if (e is TrustNotGrantedException)
{
errorTitle = SR.Get(SRID.TrustNotGrantedTitle);
errorMessage = SR.Get(SRID.TrustNotGrantedText);
}
else if (e is DeploymentDownloadException)
{
errorTitle = SR.Get(SRID.DownloadTitle);
errorMessage = SR.Get(SRID.DownloadText);
}
else if (e is DeploymentException)
{
errorTitle = SR.Get(SRID.DeployTitle);
errorMessage = SR.Get(SRID.DeployText);
}
else
{
errorTitle = SR.Get(SRID.UnknownErrorTitle);
errorMessage = SR.Get(SRID.UnknownErrorText) + "\n\n" + e.Message;
}
}
static internal bool ConstructFwlinkUrl(string version, out Uri fwlinkUri)
{
string fwlink = String.Empty;
fwlinkUri = null;
if (version != String.Empty)
{
fwlink = String.Copy(fwlinkPrefix);
fwlink = String.Concat(fwlink, version);
fwlink = String.Concat(fwlink, fwlinkSuffix);
// Mitigate against proxy server caching, append today's day to the fwlink
// query. This matches the fwlink query from unmanaged bootwap functionality
// in IE7.
DateTime today = System.DateTime.Today;
fwlink = String.Concat(fwlink, today.Year.ToString());
fwlink = String.Concat(fwlink, today.Month.ToString());
fwlink = String.Concat(fwlink, today.Day.ToString());
fwlinkUri = new Uri(fwlink);
return true;
}
return false;
}
#endregion
}
}
|