diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index b31c67319..f141af37d 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -10,14 +10,12 @@ using System.Text; using System.Threading.Tasks; using System.Windows.Media; using System.Windows.Media.Imaging; -using System.Xml.Linq; using Windows.ApplicationModel; using Windows.Management.Deployment; using Flow.Launcher.Infrastructure; using Flow.Launcher.Plugin.Program.Logger; using Rect = System.Windows.Rect; using Flow.Launcher.Plugin.SharedModels; -using Flow.Launcher.Infrastructure.Logger; using System.Threading.Channels; using System.Xml; using Windows.ApplicationModel.Core; @@ -34,7 +32,7 @@ namespace Flow.Launcher.Plugin.Program.Programs public Application[] Apps { get; set; } = Array.Empty(); - public PackageVersion Version { get; set; } + //public PackageVersion Version { get; set; } public UWP(Package package) { @@ -42,32 +40,30 @@ namespace Flow.Launcher.Plugin.Program.Programs Name = package.Id.Name; FullName = package.Id.FullName; FamilyName = package.Id.FamilyName; - //InitializeAppInfo(); InitAppsInPackage(package); } private void InitAppsInPackage(Package package) { var applist = new List(); + // WinRT + var appListEntries = package.GetAppListEntries(); + foreach (var app in appListEntries) + { + try + { + var tmp = new Application(app, this); + applist.Add(tmp); + } + catch (Exception e) + { + // TODO Logging + } + } + Apps = applist.ToArray(); + try { - var appListEntries = package.GetAppListEntries(); - int count = appListEntries.Count; - foreach (var app in appListEntries) - { - try - { - var tmp = new Application(app, this); - applist.Add(tmp); - } - catch (Exception e) - { - // TODO Logging - // Seems like logos for all games are missing? - } - } - Apps = applist.ToArray(); - // From powertoys run var xmlDoc = GetManifestXml(); if (xmlDoc == null) @@ -76,32 +72,36 @@ namespace Flow.Launcher.Plugin.Program.Programs } var xmlRoot = xmlDoc.DocumentElement; + var packageVersion = GetPackageVersionFromManifest(xmlRoot); + var logoName = logoNameFromNamespace[packageVersion]; + var namespaceManager = new XmlNamespaceManager(xmlDoc.NameTable); namespaceManager.AddNamespace("", "http://schemas.microsoft.com/appx/manifest/foundation/windows10"); namespaceManager.AddNamespace("rescap", "http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"); namespaceManager.AddNamespace("uap10", "http://schemas.microsoft.com/appx/manifest/uap/windows10/10"); - var allowElevationNode = xmlRoot.SelectSingleNode("//*[local-name()='Capability' and @Name='allowElevation']", namespaceManager); + var allowElevationNode = xmlRoot.SelectSingleNode("//*[name()='rescap:Capability' and @Name='allowElevation']", namespaceManager); bool packageCanElevate = allowElevationNode != null; - var appsNode = xmlRoot.SelectSingleNode("Applications", namespaceManager); + var appsNode = xmlRoot.SelectSingleNode("//*[name()='Applications']", namespaceManager); foreach (var app in Apps) { // According to https://learn.microsoft.com/windows/apps/desktop/modernize/grant-identity-to-nonpackaged-apps#create-a-package-manifest-for-the-sparse-package // and https://learn.microsoft.com/uwp/schemas/appxpackage/uapmanifestschema/element-application#attributes var id = app.UserModelId.Split('!')[1]; - var appNode = appsNode.SelectSingleNode($"//*[local-name()='Application' and @Id='{id}']", namespaceManager); + var appNode = appsNode?.SelectSingleNode($"//*[name()='Application' and @Id='{id}']", namespaceManager); if (appNode != null) { - //TODO app.CanRunElevated = packageCanElevate || Application.IfAppCanRunElevated(appNode, namespaceManager); + var visualElement = appNode.SelectSingleNode($"//*[name()='uap:VisualElements']", namespaceManager); + var logoUri = visualElement.Attributes[logoName]?.Value; + app.InitLogoPathFromUri(logoUri); } } } catch (Exception e) { // TODO Logging - Apps = Array.Empty(); } } @@ -128,68 +128,72 @@ namespace Flow.Launcher.Plugin.Program.Programs } } - private void InitializeAppInfo() - { - var path = Path.Combine(Location, "AppxManifest.xml"); + //private void InitializeAppInfo() + //{ + // var path = Path.Combine(Location, "AppxManifest.xml"); - var namespaces = XmlNamespaces(path); - InitPackageVersion(namespaces); + // var namespaces = XmlNamespaces(path); + // InitPackageVersion(namespaces); - const uint noAttribute = 0x80; - const Stgm nonExclusiveRead = Stgm.Read | Stgm.ShareDenyNone; - var hResult = SHCreateStreamOnFileEx(path, nonExclusiveRead, noAttribute, false, null, out IStream stream); + // const uint noAttribute = 0x80; + // const Stgm nonExclusiveRead = Stgm.Read | Stgm.ShareDenyNone; + // var hResult = SHCreateStreamOnFileEx(path, nonExclusiveRead, noAttribute, false, null, out IStream stream); - if (hResult == Hresult.Ok) - { - List _apps = AppxPackageHelper.GetAppsFromManifest(stream); + // if (hResult == Hresult.Ok) + // { + // List _apps = AppxPackageHelper.GetAppsFromManifest(stream); - Apps = _apps.Select(x => new Application(x, this)) - .Where(a => !string.IsNullOrEmpty(a.UserModelId) - && !string.IsNullOrEmpty(a.DisplayName)) - .ToArray(); - } - else - { - var e = Marshal.GetExceptionForHR((int)hResult); - ProgramLogger.LogException($"|UWP|InitializeAppInfo|{path}" + - "|Error caused while trying to get the details of the UWP program", e); + // Apps = _apps.Select(x => new Application(x, this)) + // .Where(a => !string.IsNullOrEmpty(a.UserModelId) + // && !string.IsNullOrEmpty(a.DisplayName)) + // .ToArray(); + // } + // else + // { + // var e = Marshal.GetExceptionForHR((int)hResult); + // ProgramLogger.LogException($"|UWP|InitializeAppInfo|{path}" + + // "|Error caused while trying to get the details of the UWP program", e); - Apps = Array.Empty(); - } + // Apps = Array.Empty(); + // } - if (stream != null && Marshal.ReleaseComObject(stream) > 0) - { - Log.Error("Flow.Launcher.Plugin.Program.Programs.UWP", "AppxManifest.xml was leaked"); - } - } + // if (stream != null && Marshal.ReleaseComObject(stream) > 0) + // { + // Log.Error("Flow.Launcher.Plugin.Program.Programs.UWP", "AppxManifest.xml was leaked"); + // } + //} /// http://www.hanselman.com/blog/GetNamespacesFromAnXMLDocumentWithXPathDocumentAndLINQToXML.aspx - private static string[] XmlNamespaces(string path) + private PackageVersion GetPackageVersionFromManifest(XmlElement xmlRoot) { - XDocument z = XDocument.Load(path); - if (z.Root != null) + if (xmlRoot != null) { - var namespaces = z.Root.Attributes().Where(a => a.IsNamespaceDeclaration).GroupBy( - a => a.Name.Namespace == XNamespace.None ? string.Empty : a.Name.LocalName, - a => XNamespace.Get(a.Value) - ).Select( - g => g.First().ToString() - ).ToArray(); - return namespaces; + + var namespaces = xmlRoot.Attributes; + foreach (XmlAttribute ns in namespaces) + { + if (versionFromNamespace.TryGetValue(ns.Value, out var packageVersion)) + { + return packageVersion; + } + } + + ProgramLogger.LogException($"|UWP|GetPackageVersionFromManifest|{Location}" + + "|Trying to get the package version of the UWP program, but an unknown UWP appmanifest version " + + $"{FullName} from location {Location} is returned.", new FormatException()); + return PackageVersion.Unknown; } else { - ProgramLogger.LogException($"|UWP|XmlNamespaces|{path}" + - $"|Error occured while trying to get the XML from {path}", new ArgumentNullException()); - - return Array.Empty(); + ProgramLogger.LogException($"|UWP|GetPackageVersionFromManifest|{Location}" + + "|Can't parse AppManifest.xml" + + $"{FullName} from location {Location} is returned.", new ArgumentNullException(nameof(xmlRoot))); + return PackageVersion.Unknown; } } - private void InitPackageVersion(string[] namespaces) + private static readonly Dictionary versionFromNamespace = new() { - var versionFromNamespace = new Dictionary - { { "http://schemas.microsoft.com/appx/manifest/foundation/windows10", PackageVersion.Windows10 }, @@ -199,23 +203,20 @@ namespace Flow.Launcher.Plugin.Program.Programs { "http://schemas.microsoft.com/appx/2010/manifest", PackageVersion.Windows8 }, - }; + }; - foreach (var n in versionFromNamespace.Keys) + private static readonly Dictionary logoNameFromNamespace = new() + { { - if (namespaces.Contains(n)) - { - Version = versionFromNamespace[n]; - return; - } - } - - ProgramLogger.LogException($"|UWP|XmlNamespaces|{Location}" + - "|Trying to get the package version of the UWP program, but a unknown UWP appmanifest version " - + $"{FullName} from location {Location} is returned.", new FormatException()); - - Version = PackageVersion.Unknown; - } + PackageVersion.Windows10, "Square44x44Logo" + }, + { + PackageVersion.Windows81, "Square30x30Logo" + }, + { + PackageVersion.Windows8, "SmallLogo" + }, + }; public static Application[] All() { @@ -375,9 +376,7 @@ namespace Flow.Launcher.Plugin.Program.Programs public bool Enabled { get; set; } public bool CanRunElevated { get; set; } = false; - - public string LogoUri { get; set; } - public string LogoPath { get; set; } + public string LogoPath { get; set; } = string.Empty; public Application() { } @@ -535,34 +534,34 @@ namespace Flow.Launcher.Plugin.Program.Programs Main.StartProcess(Process.Start, info); } - public Application(AppxPackageHelper.IAppxManifestApplication manifestApp, UWP package) - { - // This is done because we cannot use the keyword 'out' along with a property + //public Application(AppxPackageHelper.IAppxManifestApplication manifestApp, UWP package) + //{ + // // This is done because we cannot use the keyword 'out' along with a property - manifestApp.GetAppUserModelId(out string tmpUserModelId); - manifestApp.GetAppUserModelId(out string tmpUniqueIdentifier); - manifestApp.GetStringValue("DisplayName", out string tmpDisplayName); - manifestApp.GetStringValue("Description", out string tmpDescription); - manifestApp.GetStringValue("BackgroundColor", out string tmpBackgroundColor); - manifestApp.GetStringValue("EntryPoint", out string tmpEntryPoint); + // manifestApp.GetAppUserModelId(out string tmpUserModelId); + // manifestApp.GetAppUserModelId(out string tmpUniqueIdentifier); + // manifestApp.GetStringValue("DisplayName", out string tmpDisplayName); + // manifestApp.GetStringValue("Description", out string tmpDescription); + // manifestApp.GetStringValue("BackgroundColor", out string tmpBackgroundColor); + // manifestApp.GetStringValue("EntryPoint", out string tmpEntryPoint); - UserModelId = tmpUserModelId; - UniqueIdentifier = tmpUniqueIdentifier; - DisplayName = tmpDisplayName; - Description = tmpDescription; - BackgroundColor = tmpBackgroundColor; - EntryPoint = tmpEntryPoint; + // UserModelId = tmpUserModelId; + // UniqueIdentifier = tmpUniqueIdentifier; + // DisplayName = tmpDisplayName; + // Description = tmpDescription; + // BackgroundColor = tmpBackgroundColor; + // EntryPoint = tmpEntryPoint; - Location = package.Location; + // Location = package.Location; - DisplayName = ResourceFromPri(package.FullName, package.Name, DisplayName); - Description = ResourceFromPri(package.FullName, package.Name, Description); - LogoUri = LogoUriFromManifest(manifestApp); - LogoPath = LogoPathFromUri(LogoUri); + // DisplayName = ResourceFromPri(package.FullName, package.Name, DisplayName); + // Description = ResourceFromPri(package.FullName, package.Name, Description); + // LogoUri = LogoUriFromManifest(manifestApp); + // LogoPath = LogoPathFromUri(LogoUri); - Enabled = true; - CanRunElevated = CanApplicationRunElevated(); - } + // Enabled = true; + // CanRunElevated = CanApplicationRunElevated(); + //} private bool CanApplicationRunElevated() { @@ -654,39 +653,18 @@ namespace Flow.Launcher.Plugin.Program.Programs return $"{prefix}//{packageName}{key}"; } - internal string LogoUriFromManifest(AppxPackageHelper.IAppxManifestApplication app) - { - var logoKeyFromVersion = new Dictionary - { - { - PackageVersion.Windows10, "Square44x44Logo" - }, - { - PackageVersion.Windows81, "Square30x30Logo" - }, - { - PackageVersion.Windows8, "SmallLogo" - }, - }; - if (logoKeyFromVersion.ContainsKey(Package.Version)) - { - var key = logoKeyFromVersion[Package.Version]; - _ = app.GetStringValue(key, out string logoUri); - return logoUri; - } - else - { - return string.Empty; - } - } - - internal string LogoPathFromUri(string uri) + internal void InitLogoPathFromUri(string uri) { // all https://msdn.microsoft.com/windows/uwp/controls-and-patterns/tiles-and-notifications-app-assets // windows 10 https://msdn.microsoft.com/en-us/library/windows/apps/dn934817.aspx // windows 8.1 https://msdn.microsoft.com/en-us/library/windows/apps/hh965372.aspx#target_size // windows 8 https://msdn.microsoft.com/en-us/library/windows/apps/br211475.aspx + if (string.IsNullOrWhiteSpace(uri)) + { + throw new ArgumentException("uri"); + } + string path = Path.Combine(Location, uri); var logoPath = TryToFindLogo(uri, path); @@ -698,10 +676,17 @@ namespace Flow.Launcher.Plugin.Program.Programs // TODO: Don't know why, just keep it at the moment // Maybe on older version of Windows 10? // for C:\Windows\MiracastView etc - return TryToFindLogo(uri, tmp); + logoPath = TryToFindLogo(uri, tmp); + if (!String.IsNullOrEmpty(logoPath)) + { + LogoPath = logoPath; + } } } - return logoPath; + else + { + LogoPath = logoPath; + } string TryToFindLogo(string uri, string path) {