[PT Run] Improve the UWP Program Indexing speed (#11683)
* Improve UWP Indexing speed * Optimize Linq usage Added static readonly vars * Optimzie GetAppsFromManifest * LogoUriFromManifest uses logoUri which is also an instance variable * Add IsPackageDotInstallationPathAvailable * Dispose FileStream * Fix InstalledPath after testing in build 1903 * Fix typo
This commit is contained in:
parent
b2cff5cbd3
commit
5b804a1ff6
|
@ -11,12 +11,12 @@ namespace Microsoft.Plugin.Program.Programs
|
|||
{
|
||||
public static class AppxPackageHelper
|
||||
{
|
||||
private static readonly IAppxFactory AppxFactory = (IAppxFactory)new AppxFactory();
|
||||
|
||||
// This function returns a list of attributes of applications
|
||||
public static List<IAppxManifestApplication> GetAppsFromManifest(IStream stream)
|
||||
public static IEnumerable<IAppxManifestApplication> GetAppsFromManifest(IStream stream)
|
||||
{
|
||||
List<IAppxManifestApplication> apps = new List<IAppxManifestApplication>();
|
||||
var appxFactory = new AppxFactory();
|
||||
var reader = ((IAppxFactory)appxFactory).CreateManifestReader(stream);
|
||||
var reader = AppxFactory.CreateManifestReader(stream);
|
||||
var manifestApps = reader.GetApplications();
|
||||
|
||||
while (manifestApps.GetHasCurrent())
|
||||
|
@ -26,13 +26,11 @@ namespace Microsoft.Plugin.Program.Programs
|
|||
_ = CheckHRAndReturnOrThrow(hr, appListEntry);
|
||||
if (appListEntry != "none")
|
||||
{
|
||||
apps.Add(manifestApp);
|
||||
yield return manifestApp;
|
||||
}
|
||||
|
||||
manifestApps.MoveNext();
|
||||
}
|
||||
|
||||
return apps;
|
||||
}
|
||||
|
||||
public static T CheckHRAndReturnOrThrow<T>(Hresult hr, T result)
|
||||
|
|
|
@ -6,6 +6,7 @@ using System;
|
|||
using System.IO;
|
||||
using System.Reflection;
|
||||
using Microsoft.Plugin.Program.Logger;
|
||||
using Windows.Foundation.Metadata;
|
||||
using Package = Windows.ApplicationModel.Package;
|
||||
|
||||
namespace Microsoft.Plugin.Program.Programs
|
||||
|
@ -38,6 +39,9 @@ namespace Microsoft.Plugin.Program.Programs
|
|||
InstalledLocation = installedLocation;
|
||||
}
|
||||
|
||||
private static readonly Lazy<bool> IsPackageDotInstallationPathAvailable = new Lazy<bool>(() =>
|
||||
ApiInformation.IsPropertyPresent(typeof(Package).FullName, nameof(Package.InstalledPath)));
|
||||
|
||||
public static PackageWrapper GetWrapperFromPackage(Package package)
|
||||
{
|
||||
if (package == null)
|
||||
|
@ -48,7 +52,7 @@ namespace Microsoft.Plugin.Program.Programs
|
|||
string path;
|
||||
try
|
||||
{
|
||||
path = package.InstalledLocation.Path;
|
||||
path = IsPackageDotInstallationPathAvailable.Value ? GetInstalledPath(package) : package.InstalledLocation.Path;
|
||||
}
|
||||
catch (Exception e) when (e is ArgumentException || e is FileNotFoundException || e is DirectoryNotFoundException)
|
||||
{
|
||||
|
@ -70,5 +74,9 @@ namespace Microsoft.Plugin.Program.Programs
|
|||
package.IsDevelopmentMode,
|
||||
path);
|
||||
}
|
||||
|
||||
// This is a separate method so the reference to .InstalledPath won't be loaded in API versions which do not support this API (e.g. older then Build 19041)
|
||||
private static string GetInstalledPath(Package package)
|
||||
=> package.InstalledPath;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Principal;
|
||||
using Windows.Management.Deployment;
|
||||
using Wox.Plugin.Logger;
|
||||
|
@ -20,30 +21,28 @@ namespace Microsoft.Plugin.Program.Programs
|
|||
_packageManager = new PackageManager();
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "We want to catch all exception to prevent error in a program from affecting loading of program plugin.")]
|
||||
public IEnumerable<IPackage> FindPackagesForCurrentUser()
|
||||
{
|
||||
List<PackageWrapper> packages = new List<PackageWrapper>();
|
||||
var user = WindowsIdentity.GetCurrent().User;
|
||||
|
||||
if (user != null)
|
||||
return user != null
|
||||
? _packageManager.FindPackagesForUser(user.Value).Select(TryGetWrapperFromPackage).Where(package => package != null)
|
||||
: Enumerable.Empty<IPackage>();
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "We want to catch all exception to prevent error in a program from affecting loading of program plugin.")]
|
||||
private static PackageWrapper TryGetWrapperFromPackage(Package package)
|
||||
{
|
||||
try
|
||||
{
|
||||
var id = user.Value;
|
||||
var m = _packageManager.FindPackagesForUser(id);
|
||||
foreach (Package p in m)
|
||||
{
|
||||
try
|
||||
{
|
||||
packages.Add(PackageWrapper.GetWrapperFromPackage(p));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e.Message, GetType());
|
||||
}
|
||||
}
|
||||
return PackageWrapper.GetWrapperFromPackage(package);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e.Message, typeof(PackageManagerWrapper));
|
||||
}
|
||||
|
||||
return packages;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) Microsoft Corporation
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
|
@ -21,6 +21,13 @@ namespace Microsoft.Plugin.Program.Programs
|
|||
{
|
||||
private static readonly IPath Path = new FileSystem().Path;
|
||||
|
||||
private static readonly Dictionary<string, PackageVersion> _versionFromNamespace = new Dictionary<string, PackageVersion>
|
||||
{
|
||||
{ "http://schemas.microsoft.com/appx/manifest/foundation/windows10", PackageVersion.Windows10 },
|
||||
{ "http://schemas.microsoft.com/appx/2013/manifest", PackageVersion.Windows81 },
|
||||
{ "http://schemas.microsoft.com/appx/2010/manifest", PackageVersion.Windows8 },
|
||||
};
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
public string FullName { get; }
|
||||
|
@ -61,16 +68,7 @@ namespace Microsoft.Plugin.Program.Programs
|
|||
|
||||
if (hResult == Hresult.Ok)
|
||||
{
|
||||
var apps = new List<UWPApplication>();
|
||||
|
||||
List<IAppxManifestApplication> appsViaManifests = AppxPackageHelper.GetAppsFromManifest(stream);
|
||||
foreach (var appInManifest in appsViaManifests)
|
||||
{
|
||||
var app = new UWPApplication(appInManifest, this);
|
||||
apps.Add(app);
|
||||
}
|
||||
|
||||
Apps = apps.Where(a =>
|
||||
Apps = AppxPackageHelper.GetAppsFromManifest(stream).Select(appInManifest => new UWPApplication(appInManifest, this)).Where(a =>
|
||||
{
|
||||
var valid =
|
||||
!string.IsNullOrEmpty(a.UserModelId) &&
|
||||
|
@ -78,7 +76,7 @@ namespace Microsoft.Plugin.Program.Programs
|
|||
a.AppListEntry != "none";
|
||||
|
||||
return valid;
|
||||
}).ToArray();
|
||||
}).ToList();
|
||||
|
||||
if (Marshal.ReleaseComObject(stream) > 0)
|
||||
{
|
||||
|
@ -90,7 +88,7 @@ namespace Microsoft.Plugin.Program.Programs
|
|||
var e = Marshal.GetExceptionForHR((int)hResult);
|
||||
ProgramLogger.Exception("Error caused while trying to get the details of the UWP program", e, GetType(), path);
|
||||
|
||||
Apps = new List<UWPApplication>().ToArray();
|
||||
Apps = Array.Empty<UWPApplication>();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -118,20 +116,10 @@ namespace Microsoft.Plugin.Program.Programs
|
|||
|
||||
private void InitPackageVersion(string[] namespaces)
|
||||
{
|
||||
var versionFromNamespace = new Dictionary<string, PackageVersion>
|
||||
foreach (var n in _versionFromNamespace.Keys.Where(namespaces.Contains))
|
||||
{
|
||||
{ "http://schemas.microsoft.com/appx/manifest/foundation/windows10", PackageVersion.Windows10 },
|
||||
{ "http://schemas.microsoft.com/appx/2013/manifest", PackageVersion.Windows81 },
|
||||
{ "http://schemas.microsoft.com/appx/2010/manifest", PackageVersion.Windows8 },
|
||||
};
|
||||
|
||||
foreach (var n in versionFromNamespace.Keys)
|
||||
{
|
||||
if (namespaces.Contains(n))
|
||||
{
|
||||
Version = versionFromNamespace[n];
|
||||
return;
|
||||
}
|
||||
Version = _versionFromNamespace[n];
|
||||
return;
|
||||
}
|
||||
|
||||
ProgramLogger.Exception($"|Trying to get the package version of the UWP program, but a unknown UWP appmanifest version {FullName} from location {Location} is returned.", new FormatException(), GetType(), Location);
|
||||
|
@ -162,11 +150,10 @@ namespace Microsoft.Plugin.Program.Programs
|
|||
}
|
||||
|
||||
return u.Apps;
|
||||
}).ToArray();
|
||||
});
|
||||
|
||||
var updatedListWithoutDisabledApps = applications
|
||||
.Where(t1 => !Main.Settings.DisabledProgramSources
|
||||
.Any(x => x.UniqueIdentifier == t1.UniqueIdentifier))
|
||||
.Where(t1 => Main.Settings.DisabledProgramSources.All(x => x.UniqueIdentifier != t1.UniqueIdentifier))
|
||||
.Select(x => x);
|
||||
|
||||
return updatedListWithoutDisabledApps.ToArray();
|
||||
|
@ -180,26 +167,20 @@ namespace Microsoft.Plugin.Program.Programs
|
|||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Intentionally keeping the process alive.")]
|
||||
private static IEnumerable<IPackage> CurrentUserPackages()
|
||||
{
|
||||
var ps = PackageManagerWrapper.FindPackagesForCurrentUser();
|
||||
ps = ps.Where(p =>
|
||||
return PackageManagerWrapper.FindPackagesForCurrentUser().Where(p =>
|
||||
{
|
||||
bool valid;
|
||||
try
|
||||
{
|
||||
var f = p.IsFramework;
|
||||
var path = p.InstalledLocation;
|
||||
valid = !f && !string.IsNullOrEmpty(path);
|
||||
return !f && !string.IsNullOrEmpty(path);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ProgramLogger.Exception("An unexpected error occurred and unable to verify if package is valid", e, MethodBase.GetCurrentMethod().DeclaringType, "id");
|
||||
return false;
|
||||
}
|
||||
|
||||
return valid;
|
||||
});
|
||||
|
||||
return ps;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
|
|
|
@ -349,20 +349,20 @@ namespace Microsoft.Plugin.Program.Programs
|
|||
}
|
||||
}
|
||||
|
||||
private static readonly Dictionary<PackageVersion, string> _logoKeyFromVersion = new Dictionary<PackageVersion, string>
|
||||
{
|
||||
{ PackageVersion.Windows10, "Square44x44Logo" },
|
||||
{ PackageVersion.Windows81, "Square30x30Logo" },
|
||||
{ PackageVersion.Windows8, "SmallLogo" },
|
||||
};
|
||||
|
||||
internal string LogoUriFromManifest(IAppxManifestApplication app)
|
||||
{
|
||||
var logoKeyFromVersion = new Dictionary<PackageVersion, string>
|
||||
{
|
||||
{ PackageVersion.Windows10, "Square44x44Logo" },
|
||||
{ PackageVersion.Windows81, "Square30x30Logo" },
|
||||
{ PackageVersion.Windows8, "SmallLogo" },
|
||||
};
|
||||
if (logoKeyFromVersion.ContainsKey(Package.Version))
|
||||
if (_logoKeyFromVersion.TryGetValue(Package.Version, out var key))
|
||||
{
|
||||
var key = logoKeyFromVersion[Package.Version];
|
||||
var hr = app.GetStringValue(key, out var logoUri);
|
||||
_ = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, logoUri);
|
||||
return logoUri;
|
||||
var hr = app.GetStringValue(key, out var logoUriFromApp);
|
||||
_ = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, logoUriFromApp);
|
||||
return logoUriFromApp;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -372,9 +372,17 @@ namespace Microsoft.Plugin.Program.Programs
|
|||
|
||||
public void UpdatePath(Theme theme)
|
||||
{
|
||||
LogoPathFromUri(this.logoUri, theme);
|
||||
LogoPathFromUri(logoUri, theme);
|
||||
}
|
||||
|
||||
// scale factors on win10: https://docs.microsoft.com/en-us/windows/uwp/controls-and-patterns/tiles-and-notifications-app-assets#asset-size-tables,
|
||||
private static readonly Dictionary<PackageVersion, List<int>> _scaleFactors = new Dictionary<PackageVersion, List<int>>
|
||||
{
|
||||
{ PackageVersion.Windows10, new List<int> { 100, 125, 150, 200, 400 } },
|
||||
{ PackageVersion.Windows81, new List<int> { 100, 120, 140, 160, 180 } },
|
||||
{ PackageVersion.Windows8, new List<int> { 100 } },
|
||||
};
|
||||
|
||||
private bool SetScaleIcons(string path, string colorscheme, bool highContrast = false)
|
||||
{
|
||||
var extension = Path.GetExtension(path);
|
||||
|
@ -383,22 +391,15 @@ namespace Microsoft.Plugin.Program.Programs
|
|||
var end = path.Length - extension.Length;
|
||||
var prefix = path.Substring(0, end);
|
||||
var paths = new List<string> { };
|
||||
var scaleFactors = new Dictionary<PackageVersion, List<int>>
|
||||
{
|
||||
// scale factors on win10: https://docs.microsoft.com/en-us/windows/uwp/controls-and-patterns/tiles-and-notifications-app-assets#asset-size-tables,
|
||||
{ PackageVersion.Windows10, new List<int> { 100, 125, 150, 200, 400 } },
|
||||
{ PackageVersion.Windows81, new List<int> { 100, 120, 140, 160, 180 } },
|
||||
{ PackageVersion.Windows8, new List<int> { 100 } },
|
||||
};
|
||||
|
||||
if (!highContrast)
|
||||
{
|
||||
paths.Add(path);
|
||||
}
|
||||
|
||||
if (scaleFactors.ContainsKey(Package.Version))
|
||||
if (_scaleFactors.ContainsKey(Package.Version))
|
||||
{
|
||||
foreach (var factor in scaleFactors[Package.Version])
|
||||
foreach (var factor in _scaleFactors[Package.Version])
|
||||
{
|
||||
if (highContrast)
|
||||
{
|
||||
|
@ -440,7 +441,7 @@ namespace Microsoft.Plugin.Program.Programs
|
|||
var end = path.Length - extension.Length;
|
||||
var prefix = path.Substring(0, end);
|
||||
var paths = new List<string> { };
|
||||
int appIconSize = 36;
|
||||
const int appIconSize = 36;
|
||||
var targetSizes = new List<int> { 16, 24, 30, 36, 44, 60, 72, 96, 128, 180, 256 }.AsParallel();
|
||||
var pathFactorPairs = new Dictionary<string, int>();
|
||||
|
||||
|
@ -564,21 +565,22 @@ namespace Microsoft.Plugin.Program.Programs
|
|||
path = Path.Combine(Package.Location, "Assets", uri);
|
||||
}
|
||||
|
||||
if (theme == Theme.HighContrastBlack || theme == Theme.HighContrastOne || theme == Theme.HighContrastTwo)
|
||||
switch (theme)
|
||||
{
|
||||
isLogoUriSet = SetHighContrastIcon(path, ContrastBlack);
|
||||
}
|
||||
else if (theme == Theme.HighContrastWhite)
|
||||
{
|
||||
isLogoUriSet = SetHighContrastIcon(path, ContrastWhite);
|
||||
}
|
||||
else if (theme == Theme.Light)
|
||||
{
|
||||
isLogoUriSet = SetColoredIcon(path, ContrastWhite);
|
||||
}
|
||||
else
|
||||
{
|
||||
isLogoUriSet = SetColoredIcon(path, ContrastBlack);
|
||||
case Theme.HighContrastBlack:
|
||||
case Theme.HighContrastOne:
|
||||
case Theme.HighContrastTwo:
|
||||
isLogoUriSet = SetHighContrastIcon(path, ContrastBlack);
|
||||
break;
|
||||
case Theme.HighContrastWhite:
|
||||
isLogoUriSet = SetHighContrastIcon(path, ContrastWhite);
|
||||
break;
|
||||
case Theme.Light:
|
||||
isLogoUriSet = SetColoredIcon(path, ContrastWhite);
|
||||
break;
|
||||
default:
|
||||
isLogoUriSet = SetColoredIcon(path, ContrastBlack);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!isLogoUriSet)
|
||||
|
@ -677,17 +679,18 @@ namespace Microsoft.Plugin.Program.Programs
|
|||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
MemoryStream memoryStream = new MemoryStream();
|
||||
var memoryStream = new MemoryStream();
|
||||
using (var fileStream = File.OpenRead(path))
|
||||
{
|
||||
fileStream.CopyTo(memoryStream);
|
||||
memoryStream.Position = 0;
|
||||
|
||||
byte[] fileBytes = File.ReadAllBytes(path);
|
||||
memoryStream.Write(fileBytes, 0, fileBytes.Length);
|
||||
memoryStream.Position = 0;
|
||||
|
||||
var image = new BitmapImage();
|
||||
image.BeginInit();
|
||||
image.StreamSource = memoryStream;
|
||||
image.EndInit();
|
||||
return image;
|
||||
var image = new BitmapImage();
|
||||
image.BeginInit();
|
||||
image.StreamSource = memoryStream;
|
||||
image.EndInit();
|
||||
return image;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue