[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:
Roy 2021-06-14 10:19:05 +02:00 committed by GitHub
parent b2cff5cbd3
commit 5b804a1ff6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 97 additions and 108 deletions

View file

@ -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)

View file

@ -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;
}
}

View file

@ -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;
}
}
}

View file

@ -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()

View file

@ -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
{