From afc0946eda2fb9a2f8c2b55b3681577e128f7f27 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Mon, 28 Nov 2022 15:51:28 +0800 Subject: [PATCH 01/26] Use winRT api for uwp info and logo --- .../Programs/UWP.cs | 132 +++++++++++++++++- 1 file changed, 129 insertions(+), 3 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index 8a731f5b998..688f112af80 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -19,6 +19,8 @@ using Flow.Launcher.Plugin.SharedModels; using Flow.Launcher.Infrastructure.Logger; using System.Threading.Channels; +using Windows.Storage.Streams; +using System.Xml; namespace Flow.Launcher.Plugin.Program.Programs { @@ -30,7 +32,7 @@ public class UWP public string FamilyName { get; } public string Location { get; set; } - public Application[] Apps { get; set; } + public Application[] Apps { get; set; } = Array.Empty(); public PackageVersion Version { get; set; } @@ -40,7 +42,113 @@ public UWP(Package package) Name = package.Id.Name; FullName = package.Id.FullName; FamilyName = package.Id.FamilyName; - InitializeAppInfo(); + //InitializeAppInfo(); + InitAppsInPackage(package); + } + + private void InitAppsInPackage(Package package) + { + var applist = new List(); + try + { + var appListEntries = package.GetAppListEntries(); + int count = appListEntries.Count; + foreach (var app in appListEntries) + { + try + { + var tmp = new Application() + { + UserModelId = app.AppUserModelId, + UniqueIdentifier = app.AppUserModelId, + DisplayName = app.DisplayInfo.DisplayName, + Description = app.DisplayInfo.Description, + Package = this, + Enabled = true, + }; + applist.Add(tmp); + tmp.LogoStream = app.DisplayInfo.GetLogo(new Windows.Foundation.Size(44, 44)); // todo too small + } + catch (Exception e) + { + // TODO Logging + // Seems like logos for all games are missing? + } + } + Apps = applist.ToArray(); + SetApplicationsCanRunElevated(); + } + catch (Exception e) + { + // TODO Logging + Apps = Array.Empty(); + } + } + + private XmlDocument GetManifestXml() + { + var manifest = Path.Combine(Location, "AppxManifest.xml"); + try + { + var file = File.ReadAllText(manifest); + var xmlDoc = new XmlDocument(); + xmlDoc.LoadXml(file); + return xmlDoc; + } + catch (FileNotFoundException e) + { + ProgramLogger.LogException("UWP", "GetManifestXml", $"{Location}", "AppxManifest.xml not found.", e); + return null; + } + catch (Exception e) + { + ProgramLogger.LogException("UWP", "GetManifestXml", $"{Location}", "An unexpected error occured and unable to parse AppxManifest.xml", e); + return null; + } + } + + private void SetApplicationsCanRunElevated() + { + // From powertoys run + var xmlDoc = GetManifestXml(); + if (xmlDoc == null) + { + return; + } + + var xmlRoot = xmlDoc.DocumentElement; + 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 rescapNode = xmlRoot.SelectSingleNode("//*[local-name()='Capability' and @Name='allowElevation']", namespaceManager); + + if (rescapNode != null) + { + // For all apps in this package + foreach(var app in Apps) + { + app.CanRunElevated = true; + } + } + else + { + 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 entryPointNode = xmlRoot.SelectSingleNode($"//*[local-name()='Application' and @Id='{id}' and @EntryPoint]", namespaceManager); + var trustLevelNode = xmlRoot.SelectSingleNode($"//*[local-name()='Application' and @Id='{id}' and @uap10:TrustLevel]", namespaceManager); + + if (trustLevelNode?.Attributes["uap10:TrustLevel"]?.Value == "mediumIL" || + entryPointNode?.Attributes["EntryPoint"]?.Value == "Windows.FullTrustApplication") + { + app.CanRunElevated = true; + } + } + } } private void InitializeAppInfo() @@ -282,7 +390,7 @@ public class Application : IProgram public string DisplayName { get; set; } public string Description { get; set; } public string UserModelId { get; set; } - public string BackgroundColor { get; set; } + public string BackgroundColor { get; set; } = "transparent"; public string EntryPoint { get; set; } public string Name => DisplayName; @@ -291,10 +399,16 @@ public class Application : IProgram public bool Enabled { get; set; } public bool CanRunElevated { get; set; } + public bool CanRunElevated { get; set; } = false; + public string LogoUri { get; set; } public string LogoPath { get; set; } public UWP Package { get; set; } + [NonSerialized] + private RandomAccessStreamReference logoStream = null; + public RandomAccessStreamReference LogoStream { get => logoStream; set => logoStream = value; } + public Application() { } public Result Result(string query, IPublicAPI api) @@ -677,6 +791,18 @@ public ImageSource Logo() return plated; } + public async Task LogoAsync() + { + var stream = await LogoStream.OpenReadAsync(); + BitmapImage image = new BitmapImage(); + image.BeginInit(); + image.StreamSource = stream.AsStream(); + image.CacheOption = BitmapCacheOption.OnLoad; + image.EndInit(); + var logo = PlatedImage(image); + logo.Freeze(); + return logo; + } private BitmapImage ImageFromPath(string path) { From 6fdf2f101b5a6977cc6358062e768c64024b2258 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Mon, 28 Nov 2022 17:48:27 +0800 Subject: [PATCH 02/26] Avoid unnecessary logo finding --- Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index 688f112af80..1c8665ed4b2 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -699,10 +699,14 @@ internal string LogoPathFromUri(string uri) var logoPath = TryToFindLogo(uri, path); if (String.IsNullOrEmpty(logoPath)) { + var tmp = Path.Combine(Package.Location, "Assets", uri); + if (!path.Equals(tmp, StringComparison.OrdinalIgnoreCase)) + { // 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, Path.Combine(Package.Location, "Assets", uri)); + return TryToFindLogo(uri, tmp); + } } return logoPath; From e4733207b38c6201ecd332417645e6cfa5bad8e3 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Mon, 28 Nov 2022 18:43:56 +0800 Subject: [PATCH 03/26] Use AppListEntry to construct app --- .../Programs/UWP.cs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index 1c8665ed4b2..627d6d1384b 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -411,6 +411,16 @@ public class Application : IProgram public Application() { } + public Application(AppListEntry appListEntry) + { + UserModelId = appListEntry.AppUserModelId; + UniqueIdentifier = appListEntry.AppUserModelId; + DisplayName = appListEntry.DisplayInfo.DisplayName; + Description = appListEntry.DisplayInfo.Description; + //Package = package; + Enabled = true; + } + public Result Result(string query, IPublicAPI api) { string title; @@ -604,6 +614,18 @@ private bool CanApplicationRunElevated() return false; } + internal static bool IfAppCanRunElevated(XmlNode appNode, XmlNamespaceManager namespaceManager) + { + // 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 entryPointNode = appNode.SelectSingleNode($"//*[local-name()='Application' and @EntryPoint]", namespaceManager); + var trustLevelNode = appNode.SelectSingleNode($"//*[local-name()='Application' and @uap10:TrustLevel]", namespaceManager); + + return entryPointNode?.Attributes["EntryPoint"]?.Value == "Windows.FullTrustApplication" || + trustLevelNode?.Attributes["uap10:TrustLevel"]?.Value == "mediumIL"; + } + internal string ResourceFromPri(string packageFullName, string packageName, string rawReferenceValue) { if (string.IsNullOrWhiteSpace(rawReferenceValue) || !rawReferenceValue.StartsWith("ms-resource:")) From e098426f5c524d8924edf07f95a0090e903cc59c Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Mon, 28 Nov 2022 18:46:48 +0800 Subject: [PATCH 04/26] Parse Appmanifest.xml --- .../Programs/UWP.cs | 43 ++++++++++++++----- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index 627d6d1384b..f8369f462aa 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -57,17 +57,8 @@ private void InitAppsInPackage(Package package) { try { - var tmp = new Application() - { - UserModelId = app.AppUserModelId, - UniqueIdentifier = app.AppUserModelId, - DisplayName = app.DisplayInfo.DisplayName, - Description = app.DisplayInfo.Description, - Package = this, - Enabled = true, - }; + var tmp = new Application(app); applist.Add(tmp); - tmp.LogoStream = app.DisplayInfo.GetLogo(new Windows.Foundation.Size(44, 44)); // todo too small } catch (Exception e) { @@ -76,13 +67,43 @@ private void InitAppsInPackage(Package package) } } Apps = applist.ToArray(); - SetApplicationsCanRunElevated(); + + // From powertoys run + var xmlDoc = GetManifestXml(); + if (xmlDoc == null) + { + return; + } + + var xmlRoot = xmlDoc.DocumentElement; + 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); + bool packageCanElevate = allowElevationNode != null; + + var appsNode = xmlRoot.SelectSingleNode("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); + if (appNode != null) + { + //TODO + app.CanRunElevated = packageCanElevate || Application.IfAppCanRunElevated(appNode, namespaceManager); + } + } } catch (Exception e) { // TODO Logging Apps = Array.Empty(); } + } private XmlDocument GetManifestXml() From 97d6b6e234892fac370e4fbcb4a3b63ea4794b9e Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Mon, 28 Nov 2022 18:47:27 +0800 Subject: [PATCH 05/26] fix typo --- Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index f8369f462aa..37f8dba8efa 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -418,8 +418,6 @@ public class Application : IProgram public string Location => Package.Location; public bool Enabled { get; set; } - public bool CanRunElevated { get; set; } - public bool CanRunElevated { get; set; } = false; public string LogoUri { get; set; } From a62ea0e9898615427d6b317a469194d23635de41 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Mon, 28 Nov 2022 19:06:16 +0800 Subject: [PATCH 06/26] Remove UWP member in Application --- .../Programs/UWP.cs | 128 +++++------------- 1 file changed, 33 insertions(+), 95 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index 37f8dba8efa..b31c6731967 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -19,8 +19,8 @@ using Flow.Launcher.Plugin.SharedModels; using Flow.Launcher.Infrastructure.Logger; using System.Threading.Channels; -using Windows.Storage.Streams; using System.Xml; +using Windows.ApplicationModel.Core; namespace Flow.Launcher.Plugin.Program.Programs { @@ -57,14 +57,14 @@ private void InitAppsInPackage(Package package) { try { - var tmp = new Application(app); + 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(); @@ -128,50 +128,6 @@ private XmlDocument GetManifestXml() } } - private void SetApplicationsCanRunElevated() - { - // From powertoys run - var xmlDoc = GetManifestXml(); - if (xmlDoc == null) - { - return; - } - - var xmlRoot = xmlDoc.DocumentElement; - 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 rescapNode = xmlRoot.SelectSingleNode("//*[local-name()='Capability' and @Name='allowElevation']", namespaceManager); - - if (rescapNode != null) - { - // For all apps in this package - foreach(var app in Apps) - { - app.CanRunElevated = true; - } - } - else - { - 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 entryPointNode = xmlRoot.SelectSingleNode($"//*[local-name()='Application' and @Id='{id}' and @EntryPoint]", namespaceManager); - var trustLevelNode = xmlRoot.SelectSingleNode($"//*[local-name()='Application' and @Id='{id}' and @uap10:TrustLevel]", namespaceManager); - - if (trustLevelNode?.Attributes["uap10:TrustLevel"]?.Value == "mediumIL" || - entryPointNode?.Attributes["EntryPoint"]?.Value == "Windows.FullTrustApplication") - { - app.CanRunElevated = true; - } - } - } - } - private void InitializeAppInfo() { var path = Path.Combine(Location, "AppxManifest.xml"); @@ -188,7 +144,7 @@ private void InitializeAppInfo() List _apps = AppxPackageHelper.GetAppsFromManifest(stream); Apps = _apps.Select(x => new Application(x, this)) - .Where(a => !string.IsNullOrEmpty(a.UserModelId) + .Where(a => !string.IsNullOrEmpty(a.UserModelId) && !string.IsNullOrEmpty(a.DisplayName)) .ToArray(); } @@ -377,7 +333,7 @@ public static async Task WatchPackageChange() PackageChangeChannel.Reader.TryRead(out _); await Task.Run(Main.IndexUwpPrograms); } - + } } @@ -415,28 +371,23 @@ public class Application : IProgram public string EntryPoint { get; set; } public string Name => DisplayName; - public string Location => Package.Location; + public string Location { get; set; } public bool Enabled { get; set; } public bool CanRunElevated { get; set; } = false; public string LogoUri { get; set; } public string LogoPath { get; set; } - public UWP Package { get; set; } - - [NonSerialized] - private RandomAccessStreamReference logoStream = null; - public RandomAccessStreamReference LogoStream { get => logoStream; set => logoStream = value; } public Application() { } - public Application(AppListEntry appListEntry) + public Application(AppListEntry appListEntry, UWP package) { UserModelId = appListEntry.AppUserModelId; UniqueIdentifier = appListEntry.AppUserModelId; DisplayName = appListEntry.DisplayInfo.DisplayName; Description = appListEntry.DisplayInfo.Description; - //Package = package; + Location = package.Location; Enabled = true; } @@ -478,7 +429,7 @@ public Result Result(string query, IPublicAPI api) var result = new Result { Title = title, - SubTitle = Main._settings.HideAppsPath ? string.Empty : Package.Location, + SubTitle = Main._settings.HideAppsPath ? string.Empty : Location, Icon = Logo, Score = matchResult.Score, TitleHighlightData = matchResult.MatchData, @@ -525,7 +476,7 @@ public List ContextMenus(IPublicAPI api) Title = api.GetTranslation("flowlauncher_plugin_program_open_containing_folder"), Action = _ => { - Main.Context.API.OpenDirectory(Package.Location); + Main.Context.API.OpenDirectory(Location); return true; }, @@ -577,7 +528,8 @@ private void LaunchElevated() var info = new ProcessStartInfo(command) { - UseShellExecute = true, Verb = "runas", + UseShellExecute = true, + Verb = "runas", }; Main.StartProcess(Process.Start, info); @@ -601,7 +553,7 @@ public Application(AppxPackageHelper.IAppxManifestApplication manifestApp, UWP p BackgroundColor = tmpBackgroundColor; EntryPoint = tmpEntryPoint; - Package = package; + Location = package.Location; DisplayName = ResourceFromPri(package.FullName, package.Name, DisplayName); Description = ResourceFromPri(package.FullName, package.Name, Description); @@ -619,7 +571,7 @@ private bool CanApplicationRunElevated() return true; } - var manifest = Package.Location + "\\AppxManifest.xml"; + var manifest = Location + "\\AppxManifest.xml"; if (File.Exists(manifest)) { var file = File.ReadAllText(manifest); @@ -665,15 +617,15 @@ internal string ResourceFromPri(string packageFullName, string packageName, stri } else { - ProgramLogger.LogException($"|UWP|ResourceFromPri|{Package.Location}|Can't load null or empty result " - + $"pri {source} in uwp location {Package.Location}", new NullReferenceException()); + ProgramLogger.LogException($"|UWP|ResourceFromPri|{Location}|Can't load null or empty result " + + $"pri {source} in uwp location {Location}", new NullReferenceException()); return string.Empty; } } else { var e = Marshal.GetExceptionForHR((int)hResult); - ProgramLogger.LogException($"|UWP|ResourceFromPri|{Package.Location}|Load pri failed {source} with HResult {hResult} and location {Package.Location}", e); + ProgramLogger.LogException($"|UWP|ResourceFromPri|{Location}|Load pri failed {source} with HResult {hResult} and location {Location}", e); return string.Empty; } } @@ -735,17 +687,17 @@ internal string LogoPathFromUri(string uri) // 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 - string path = Path.Combine(Package.Location, uri); + string path = Path.Combine(Location, uri); var logoPath = TryToFindLogo(uri, path); if (String.IsNullOrEmpty(logoPath)) { - var tmp = Path.Combine(Package.Location, "Assets", uri); + var tmp = Path.Combine(Location, "Assets", uri); if (!path.Equals(tmp, StringComparison.OrdinalIgnoreCase)) { - // TODO: Don't know why, just keep it at the moment - // Maybe on older version of Windows 10? - // for C:\Windows\MiracastView etc + // 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); } } @@ -766,8 +718,8 @@ string TryToFindLogo(string uri, string path) if (String.IsNullOrEmpty(logoNamePrefix) || String.IsNullOrEmpty(logoDir) || !Directory.Exists(logoDir)) { // Known issue: Edge always triggers it since logo is not at uri - ProgramLogger.LogException($"|UWP|LogoPathFromUri|{Package.Location}" + - $"|{UserModelId} can't find logo uri for {uri} in package location (logo name or directory not found): {Package.Location}", new FileNotFoundException()); + ProgramLogger.LogException($"|UWP|LogoPathFromUri|{Location}" + + $"|{UserModelId} can't find logo uri for {uri} in package location (logo name or directory not found): {Location}", new FileNotFoundException()); return string.Empty; } @@ -785,7 +737,7 @@ string TryToFindLogo(string uri, string path) var selected = logos.FirstOrDefault(); var closest = selected; int min = int.MaxValue; - foreach(var logo in logos) + foreach (var logo in logos) { var imageStream = File.OpenRead(logo); @@ -793,7 +745,7 @@ string TryToFindLogo(string uri, string path) var height = decoder.Frames[0].PixelHeight; var width = decoder.Frames[0].PixelWidth; int pixelCountDiff = Math.Abs(height * width - 1936); // 44*44=1936 - if(pixelCountDiff < min) + if (pixelCountDiff < min) { // try to find the closest to 44x44 logo closest = logo; @@ -810,16 +762,16 @@ string TryToFindLogo(string uri, string path) } else { - ProgramLogger.LogException($"|UWP|LogoPathFromUri|{Package.Location}" + - $"|{UserModelId} can't find logo uri for {uri} in package location (can't find specified logo): {Package.Location}", new FileNotFoundException()); + ProgramLogger.LogException($"|UWP|LogoPathFromUri|{Location}" + + $"|{UserModelId} can't find logo uri for {uri} in package location (can't find specified logo): {Location}", new FileNotFoundException()); return string.Empty; } } else { - ProgramLogger.LogException($"|UWP|LogoPathFromUri|{Package.Location}" + + ProgramLogger.LogException($"|UWP|LogoPathFromUri|{Location}" + $"|Unable to find extension from {uri} for {UserModelId} " + - $"in package location {Package.Location}", new FileNotFoundException()); + $"in package location {Location}", new FileNotFoundException()); return string.Empty; } } @@ -835,20 +787,6 @@ public ImageSource Logo() plated.Freeze(); return plated; } - - public async Task LogoAsync() - { - var stream = await LogoStream.OpenReadAsync(); - BitmapImage image = new BitmapImage(); - image.BeginInit(); - image.StreamSource = stream.AsStream(); - image.CacheOption = BitmapCacheOption.OnLoad; - image.EndInit(); - var logo = PlatedImage(image); - logo.Freeze(); - return logo; - } - private BitmapImage ImageFromPath(string path) { // TODO: Consider using infrastructure.image.imageloader? @@ -866,7 +804,7 @@ private BitmapImage ImageFromPath(string path) { ProgramLogger.LogException($"|UWP|ImageFromPath|{(string.IsNullOrEmpty(path) ? "Not Avaliable" : path)}" + $"|Unable to get logo for {UserModelId} from {path} and" + - $" located in {Package.Location}", new FileNotFoundException()); + $" located in {Location}", new FileNotFoundException()); return new BitmapImage(new Uri(Constant.MissingImgIcon)); } } @@ -913,9 +851,9 @@ private ImageSource PlatedImage(BitmapImage image) } else { - ProgramLogger.LogException($"|UWP|PlatedImage|{Package.Location}" + + ProgramLogger.LogException($"|UWP|PlatedImage|{Location}" + $"|Unable to convert background string {BackgroundColor} " + - $"to color for {Package.Location}", new InvalidOperationException()); + $"to color for {Location}", new InvalidOperationException()); return new BitmapImage(new Uri(Constant.MissingImgIcon)); } From cd801e8ae7ec76128b5121504a8b0ac9abc2be47 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Mon, 28 Nov 2022 22:01:16 +0800 Subject: [PATCH 07/26] Get uwp logo from xml --- .../Programs/UWP.cs | 275 +++++++++--------- 1 file changed, 130 insertions(+), 145 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index b31c6731967..f141af37d56 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.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 @@ public class UWP 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 @@ public UWP(Package package) Name = package.Id.Name; FullName = package.Id.FullName; FamilyName = package.Id.FamilyName; - //InitializeAppInfo(); InitAppsInPackage(package); } private void InitAppsInPackage(Package package) { var applist = new List(); - try + // WinRT + var appListEntries = package.GetAppListEntries(); + foreach (var app in appListEntries) { - var appListEntries = package.GetAppListEntries(); - int count = appListEntries.Count; - foreach (var app in appListEntries) + try { - try - { - var tmp = new Application(app, this); - applist.Add(tmp); - } - catch (Exception e) - { - // TODO Logging - // Seems like logos for all games are missing? - } + var tmp = new Application(app, this); + applist.Add(tmp); + } + catch (Exception e) + { + // TODO Logging } - Apps = applist.ToArray(); + } + Apps = applist.ToArray(); + try + { // From powertoys run var xmlDoc = GetManifestXml(); if (xmlDoc == null) @@ -76,32 +72,36 @@ private void InitAppsInPackage(Package package) } 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 @@ private XmlDocument GetManifestXml() } } - private void InitializeAppInfo() - { - var path = Path.Combine(Location, "AppxManifest.xml"); - - 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); - - if (hResult == Hresult.Ok) - { - List _apps = AppxPackageHelper.GetAppsFromManifest(stream); + //private void InitializeAppInfo() + //{ + // var path = Path.Combine(Location, "AppxManifest.xml"); + + // 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); + + // 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 = Array.Empty(); + // } + + // if (stream != null && Marshal.ReleaseComObject(stream) > 0) + // { + // Log.Error("Flow.Launcher.Plugin.Program.Programs.UWP", "AppxManifest.xml was leaked"); + // } + //} - Apps = _apps.Select(x => new Application(x, this)) - .Where(a => !string.IsNullOrEmpty(a.UserModelId) - && !string.IsNullOrEmpty(a.DisplayName)) - .ToArray(); - } - else + /// http://www.hanselman.com/blog/GetNamespacesFromAnXMLDocumentWithXPathDocumentAndLINQToXML.aspx + private PackageVersion GetPackageVersionFromManifest(XmlElement xmlRoot) + { + if (xmlRoot != null) { - 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(); - } - - if (stream != null && Marshal.ReleaseComObject(stream) > 0) - { - Log.Error("Flow.Launcher.Plugin.Program.Programs.UWP", "AppxManifest.xml was leaked"); - } - } + var namespaces = xmlRoot.Attributes; + foreach (XmlAttribute ns in namespaces) + { + if (versionFromNamespace.TryGetValue(ns.Value, out var packageVersion)) + { + return packageVersion; + } + } - /// http://www.hanselman.com/blog/GetNamespacesFromAnXMLDocumentWithXPathDocumentAndLINQToXML.aspx - private static string[] XmlNamespaces(string path) - { - XDocument z = XDocument.Load(path); - if (z.Root != 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; + 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 @@ private void InitPackageVersion(string[] namespaces) { "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 @@ public class Application : IProgram 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 @@ private void LaunchElevated() 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 @@ public static string FormattedPriReferenceValue(string packageName, string rawPr 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 @@ internal string LogoPathFromUri(string uri) // 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) { From 54f408aeada781e9e165cefda097cc6a991127ec Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Mon, 28 Nov 2022 22:08:21 +0800 Subject: [PATCH 08/26] comment unused --- Flow.Launcher.Test/Plugins/ProgramTest.cs | 32 ++-- .../Programs/UWP.cs | 178 +++++++++--------- 2 files changed, 104 insertions(+), 106 deletions(-) diff --git a/Flow.Launcher.Test/Plugins/ProgramTest.cs b/Flow.Launcher.Test/Plugins/ProgramTest.cs index e3a05f484f0..10818ec4951 100644 --- a/Flow.Launcher.Test/Plugins/ProgramTest.cs +++ b/Flow.Launcher.Test/Plugins/ProgramTest.cs @@ -8,23 +8,23 @@ namespace Flow.Launcher.Test.Plugins [TestFixture] public class ProgramTest { - [TestCase("Microsoft.WindowsCamera", "ms-resource:LensSDK/Resources/AppTitle", "ms-resource://Microsoft.WindowsCamera/LensSDK/Resources/AppTitle")] - [TestCase("microsoft.windowscommunicationsapps", "ms-resource://microsoft.windowscommunicationsapps/hxoutlookintl/AppManifest_MailDesktop_DisplayName", - "ms-resource://microsoft.windowscommunicationsapps/hxoutlookintl/AppManifest_MailDesktop_DisplayName")] - [TestCase("windows.immersivecontrolpanel", "ms-resource:DisplayName", "ms-resource://windows.immersivecontrolpanel/Resources/DisplayName")] - [TestCase("Microsoft.MSPaint", "ms-resource:AppName", "ms-resource://Microsoft.MSPaint/Resources/AppName")] - public void WhenGivenPriReferenceValueShouldReturnCorrectFormat(string packageName, string rawPriReferenceValue, string expectedFormat) - { - // Arrange - var app = new UWP.Application(); + //[TestCase("Microsoft.WindowsCamera", "ms-resource:LensSDK/Resources/AppTitle", "ms-resource://Microsoft.WindowsCamera/LensSDK/Resources/AppTitle")] + //[TestCase("microsoft.windowscommunicationsapps", "ms-resource://microsoft.windowscommunicationsapps/hxoutlookintl/AppManifest_MailDesktop_DisplayName", + // "ms-resource://microsoft.windowscommunicationsapps/hxoutlookintl/AppManifest_MailDesktop_DisplayName")] + //[TestCase("windows.immersivecontrolpanel", "ms-resource:DisplayName", "ms-resource://windows.immersivecontrolpanel/Resources/DisplayName")] + //[TestCase("Microsoft.MSPaint", "ms-resource:AppName", "ms-resource://Microsoft.MSPaint/Resources/AppName")] + //public void WhenGivenPriReferenceValueShouldReturnCorrectFormat(string packageName, string rawPriReferenceValue, string expectedFormat) + //{ + // // Arrange + // var app = new UWP.Application(); - // Act - var result = UWP.Application.FormattedPriReferenceValue(packageName, rawPriReferenceValue); + // // Act + // var result = UWP.Application.FormattedPriReferenceValue(packageName, rawPriReferenceValue); - // Assert - Assert.IsTrue(result == expectedFormat, - $"Expected Pri reference format: {expectedFormat}{Environment.NewLine} " + - $"Actual: {result}{Environment.NewLine}"); - } + // // Assert + // Assert.IsTrue(result == expectedFormat, + // $"Expected Pri reference format: {expectedFormat}{Environment.NewLine} " + + // $"Actual: {result}{Environment.NewLine}"); + //} } } diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index f141af37d56..c287e31e934 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -374,12 +374,10 @@ public class Application : IProgram public string Name => DisplayName; public string Location { get; set; } - public bool Enabled { get; set; } + public bool Enabled { get; set; } = false; public bool CanRunElevated { get; set; } = false; public string LogoPath { get; set; } = string.Empty; - public Application() { } - public Application(AppListEntry appListEntry, UWP package) { UserModelId = appListEntry.AppUserModelId; @@ -563,26 +561,26 @@ private void LaunchElevated() // CanRunElevated = CanApplicationRunElevated(); //} - private bool CanApplicationRunElevated() - { - if (EntryPoint == "Windows.FullTrustApplication") - { - return true; - } - - var manifest = Location + "\\AppxManifest.xml"; - if (File.Exists(manifest)) - { - var file = File.ReadAllText(manifest); - - if (file.Contains("TrustLevel=\"mediumIL\"", StringComparison.OrdinalIgnoreCase)) - { - return true; - } - } - - return false; - } + //private bool CanApplicationRunElevated() + //{ + // if (EntryPoint == "Windows.FullTrustApplication") + // { + // return true; + // } + + // var manifest = Location + "\\AppxManifest.xml"; + // if (File.Exists(manifest)) + // { + // var file = File.ReadAllText(manifest); + + // if (file.Contains("TrustLevel=\"mediumIL\"", StringComparison.OrdinalIgnoreCase)) + // { + // return true; + // } + // } + + // return false; + //} internal static bool IfAppCanRunElevated(XmlNode appNode, XmlNamespaceManager namespaceManager) { @@ -596,62 +594,62 @@ internal static bool IfAppCanRunElevated(XmlNode appNode, XmlNamespaceManager na trustLevelNode?.Attributes["uap10:TrustLevel"]?.Value == "mediumIL"; } - internal string ResourceFromPri(string packageFullName, string packageName, string rawReferenceValue) - { - if (string.IsNullOrWhiteSpace(rawReferenceValue) || !rawReferenceValue.StartsWith("ms-resource:")) - return rawReferenceValue; - - var formattedPriReference = FormattedPriReferenceValue(packageName, rawReferenceValue); - - var outBuffer = new StringBuilder(128); - string source = $"@{{{packageFullName}? {formattedPriReference}}}"; - var capacity = (uint)outBuffer.Capacity; - var hResult = SHLoadIndirectString(source, outBuffer, capacity, IntPtr.Zero); - if (hResult == Hresult.Ok) - { - var loaded = outBuffer.ToString(); - if (!string.IsNullOrEmpty(loaded)) - { - return loaded; - } - else - { - ProgramLogger.LogException($"|UWP|ResourceFromPri|{Location}|Can't load null or empty result " - + $"pri {source} in uwp location {Location}", new NullReferenceException()); - return string.Empty; - } - } - else - { - var e = Marshal.GetExceptionForHR((int)hResult); - ProgramLogger.LogException($"|UWP|ResourceFromPri|{Location}|Load pri failed {source} with HResult {hResult} and location {Location}", e); - return string.Empty; - } - } + //internal string ResourceFromPri(string packageFullName, string packageName, string rawReferenceValue) + //{ + // if (string.IsNullOrWhiteSpace(rawReferenceValue) || !rawReferenceValue.StartsWith("ms-resource:")) + // return rawReferenceValue; + + // var formattedPriReference = FormattedPriReferenceValue(packageName, rawReferenceValue); + + // var outBuffer = new StringBuilder(128); + // string source = $"@{{{packageFullName}? {formattedPriReference}}}"; + // var capacity = (uint)outBuffer.Capacity; + // var hResult = SHLoadIndirectString(source, outBuffer, capacity, IntPtr.Zero); + // if (hResult == Hresult.Ok) + // { + // var loaded = outBuffer.ToString(); + // if (!string.IsNullOrEmpty(loaded)) + // { + // return loaded; + // } + // else + // { + // ProgramLogger.LogException($"|UWP|ResourceFromPri|{Location}|Can't load null or empty result " + // + $"pri {source} in uwp location {Location}", new NullReferenceException()); + // return string.Empty; + // } + // } + // else + // { + // var e = Marshal.GetExceptionForHR((int)hResult); + // ProgramLogger.LogException($"|UWP|ResourceFromPri|{Location}|Load pri failed {source} with HResult {hResult} and location {Location}", e); + // return string.Empty; + // } + //} - public static string FormattedPriReferenceValue(string packageName, string rawPriReferenceValue) - { - const string prefix = "ms-resource:"; + //public static string FormattedPriReferenceValue(string packageName, string rawPriReferenceValue) + //{ + // const string prefix = "ms-resource:"; - if (string.IsNullOrWhiteSpace(rawPriReferenceValue) || !rawPriReferenceValue.StartsWith(prefix)) - return rawPriReferenceValue; + // if (string.IsNullOrWhiteSpace(rawPriReferenceValue) || !rawPriReferenceValue.StartsWith(prefix)) + // return rawPriReferenceValue; - string key = rawPriReferenceValue.Substring(prefix.Length); - if (key.StartsWith("//")) - return $"{prefix}{key}"; + // string key = rawPriReferenceValue.Substring(prefix.Length); + // if (key.StartsWith("//")) + // return $"{prefix}{key}"; - if (!key.StartsWith("/")) - { - key = $"/{key}"; - } + // if (!key.StartsWith("/")) + // { + // key = $"/{key}"; + // } - if (!key.ToLower().Contains("resources")) - { - key = $"/Resources{key}"; - } + // if (!key.ToLower().Contains("resources")) + // { + // key = $"/Resources{key}"; + // } - return $"{prefix}//{packageName}{key}"; - } + // return $"{prefix}//{packageName}{key}"; + //} internal void InitLogoPathFromUri(string uri) { @@ -881,26 +879,26 @@ public enum PackageVersion Unknown } - [Flags] - private enum Stgm : uint - { - Read = 0x0, - ShareExclusive = 0x10, - ShareDenyNone = 0x40 - } + //[Flags] + //private enum Stgm : uint + //{ + // Read = 0x0, + // ShareExclusive = 0x10, + // ShareDenyNone = 0x40 + //} - private enum Hresult : uint - { - Ok = 0x0000, - } + //private enum Hresult : uint + //{ + // Ok = 0x0000, + //} - [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)] - private static extern Hresult SHCreateStreamOnFileEx(string fileName, Stgm grfMode, uint attributes, bool create, - IStream reserved, out IStream stream); + //[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)] + //private static extern Hresult SHCreateStreamOnFileEx(string fileName, Stgm grfMode, uint attributes, bool create, + // IStream reserved, out IStream stream); - [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)] - private static extern Hresult SHLoadIndirectString(string pszSource, StringBuilder pszOutBuf, uint cchOutBuf, - IntPtr ppvReserved); + //[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)] + //private static extern Hresult SHLoadIndirectString(string pszSource, StringBuilder pszOutBuf, uint cchOutBuf, + // IntPtr ppvReserved); } } From 0374e9f56fb1608be3feb055c99159c0ec343500 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Mon, 28 Nov 2022 22:13:55 +0800 Subject: [PATCH 09/26] set default values of fields --- Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index c287e31e934..d4a41fc07ec 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -365,14 +365,12 @@ public class Application : IProgram { private string _uid = string.Empty; public string UniqueIdentifier { get => _uid; set => _uid = value == null ? string.Empty : value.ToLowerInvariant(); } - public string DisplayName { get; set; } - public string Description { get; set; } - public string UserModelId { get; set; } - public string BackgroundColor { get; set; } = "transparent"; - - public string EntryPoint { get; set; } + public string DisplayName { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; + public string UserModelId { get; set; } = string.Empty; + public string BackgroundColor { get; set; } = string.Empty; public string Name => DisplayName; - public string Location { get; set; } + public string Location { get; set; } = string.Empty; public bool Enabled { get; set; } = false; public bool CanRunElevated { get; set; } = false; From 07a68ed1920f37ff414089d5ac57b08ec1b24bd7 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Mon, 28 Nov 2022 22:55:10 +0800 Subject: [PATCH 10/26] Use imageloader to load icon --- .../Programs/UWP.cs | 38 +------------------ 1 file changed, 2 insertions(+), 36 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index d4a41fc07ec..5198f576177 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -128,41 +128,6 @@ private XmlDocument GetManifestXml() } } - //private void InitializeAppInfo() - //{ - // var path = Path.Combine(Location, "AppxManifest.xml"); - - // 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); - - // 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 = Array.Empty(); - // } - - // 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 PackageVersion GetPackageVersionFromManifest(XmlElement xmlRoot) { @@ -425,7 +390,8 @@ public Result Result(string query, IPublicAPI api) { Title = title, SubTitle = Main._settings.HideAppsPath ? string.Empty : Location, - Icon = Logo, + //Icon = Logo, + IcoPath = LogoPath, Score = matchResult.Score, TitleHighlightData = matchResult.MatchData, ContextData = this, From b50f43ae7cdc646400c0744094974419a4caa744 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Tue, 29 Nov 2022 01:02:53 +0800 Subject: [PATCH 11/26] Remove ApplicationActivationHelper --- .../Programs/ApplicationActivationHelper.cs | 42 ----------------- .../Programs/UWP.cs | 47 ++++--------------- .../Programs/Win32.cs | 4 +- 3 files changed, 12 insertions(+), 81 deletions(-) delete mode 100644 Plugins/Flow.Launcher.Plugin.Program/Programs/ApplicationActivationHelper.cs diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/ApplicationActivationHelper.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/ApplicationActivationHelper.cs deleted file mode 100644 index 790a70392e6..00000000000 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/ApplicationActivationHelper.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace Flow.Launcher.Plugin.Program.Programs -{ - public class ApplicationActivationHelper - { - // Reference : https://github.com/MicrosoftEdge/edge-launcher/blob/108e63df0b4cb5cd9d5e45aa7a264690851ec51d/MIcrosoftEdgeLauncherCsharp/Program.cs - public enum ActivateOptions - { - None = 0x00000000, - DesignMode = 0x00000001, - NoErrorUI = 0x00000002, - NoSplashScreen = 0x00000004, - } - - /// ApplicationActivationManager - [ComImport, Guid("2e941141-7f97-4756-ba1d-9decde894a3d"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - interface IApplicationActivationManager - { - IntPtr ActivateApplication([In] string appUserModelId, [In] string arguments, [In] ActivateOptions options, [Out] out uint processId); - IntPtr ActivateForFile([In] string appUserModelId, [In] IntPtr /*IShellItemArray* */ itemArray, [In] string verb, [Out] out uint processId); - IntPtr ActivateForProtocol([In] string appUserModelId, [In] IntPtr /* IShellItemArray* */itemArray, [Out] out uint processId); - } - - // Application Activation Manager Class - [ComImport, Guid("45BA127D-10A8-46EA-8AB7-56EA9078943C")] - public class ApplicationActivationManager : IApplicationActivationManager - { - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)/*, PreserveSig*/] - public extern IntPtr ActivateApplication([In] string appUserModelId, [In] string arguments, [In] ActivateOptions options, [Out] out uint processId); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public extern IntPtr ActivateForFile([In] string appUserModelId, [In] IntPtr /*IShellItemArray* */ itemArray, [In] string verb, [Out] out uint processId); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - public extern IntPtr ActivateForProtocol([In] string appUserModelId, [In] IntPtr /* IShellItemArray* */itemArray, [Out] out uint processId); - } - } -} diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index 5198f576177..47443b7ee22 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -404,20 +404,13 @@ public Result Result(string query, IPublicAPI api) !e.SpecialKeyState.WinPressed ); - if (elevated && CanRunElevated) + bool shouldRunElevated = elevated && CanRunElevated; + _ = Task.Run(() => Launch(shouldRunElevated)).ConfigureAwait(false); + if (elevated && !shouldRunElevated) { - LaunchElevated(); - } - else - { - Launch(api); - - if (elevated) - { - var title = "Plugin: Program"; - var message = api.GetTranslation("flowlauncher_plugin_program_run_as_administrator_not_supported_message"); - api.ShowMsg(title, message, string.Empty); - } + var title = "Plugin: Program"; + var message = api.GetTranslation("flowlauncher_plugin_program_run_as_administrator_not_supported_message"); + api.ShowMsg(title, message, string.Empty); } return true; @@ -452,7 +445,7 @@ public List ContextMenus(IPublicAPI api) Title = api.GetTranslation("flowlauncher_plugin_program_run_as_administrator"), Action = _ => { - LaunchElevated(); + Task.Run(() => Launch(true)).ConfigureAwait(false); return true; }, IcoPath = "Images/cmd.png" @@ -462,35 +455,15 @@ public List ContextMenus(IPublicAPI api) return contextMenus; } - private async void Launch(IPublicAPI api) - { - var appManager = new ApplicationActivationHelper.ApplicationActivationManager(); - const string noArgs = ""; - const ApplicationActivationHelper.ActivateOptions noFlags = ApplicationActivationHelper.ActivateOptions.None; - await Task.Run(() => - { - try - { - _ = appManager.ActivateApplication(UserModelId, noArgs, noFlags, out _); - } - catch (Exception) - { - var name = "Plugin: Program"; - var message = $"Can't start UWP: {DisplayName}"; - api.ShowMsg(name, message, string.Empty); - } - }); - } - - private void LaunchElevated() + private void Launch(bool elevated=false) { - string command = "shell:AppsFolder\\" + UniqueIdentifier; + string command = "shell:AppsFolder\\" + UserModelId; command = Environment.ExpandEnvironmentVariables(command.Trim()); var info = new ProcessStartInfo(command) { UseShellExecute = true, - Verb = "runas", + Verb = elevated ? "runas" : "" }; Main.StartProcess(Process.Start, info); diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index 7fbffb548e4..e89970fb4bd 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -142,10 +142,10 @@ public Result Result(string query, IPublicAPI api) FileName = FullPath, WorkingDirectory = ParentDirectory, UseShellExecute = true, - Verb = runAsAdmin ? "runas" : null + Verb = runAsAdmin ? "runas" : "" }; - Task.Run(() => Main.StartProcess(Process.Start, info)); + _ = Task.Run(() => Main.StartProcess(Process.Start, info)); return true; } From df30ec97424269e8a0ddfb172750b9ca90889523 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Tue, 29 Nov 2022 01:22:13 +0800 Subject: [PATCH 12/26] Init apps after construction --- Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index 47443b7ee22..56ccfc22cde 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -40,10 +40,9 @@ public UWP(Package package) Name = package.Id.Name; FullName = package.Id.FullName; FamilyName = package.Id.FamilyName; - InitAppsInPackage(package); } - private void InitAppsInPackage(Package package) + public void InitAppsInPackage(Package package) { var applist = new List(); // WinRT @@ -195,6 +194,7 @@ public static Application[] All() try { u = new UWP(p); + u.InitAppsInPackage(p); } #if !DEBUG catch (Exception e) @@ -245,22 +245,19 @@ private static IEnumerable CurrentUserPackages() var ps = m.FindPackagesForUser(id); ps = ps.Where(p => { - bool valid; try { var f = p.IsFramework; var d = p.IsDevelopmentMode; var path = p.InstalledLocation.Path; - valid = !f && !d && !string.IsNullOrEmpty(path); + return !f && !d && !string.IsNullOrEmpty(path); } catch (Exception e) { - ProgramLogger.LogException("UWP", "CurrentUserPackages", $"id", "An unexpected error occured and " + ProgramLogger.LogException("UWP", "CurrentUserPackages", $"{id}", "An unexpected error occured and " + $"unable to verify if package is valid", e); return false; } - - return valid; }); return ps; } From 202c660ee64148fb2122969d9011bad11b2bea3c Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Tue, 29 Nov 2022 01:46:48 +0800 Subject: [PATCH 13/26] Log exceptions when initing apps --- .../Programs/UWP.cs | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index 56ccfc22cde..ab582a033cc 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -56,7 +56,9 @@ public void InitAppsInPackage(Package package) } catch (Exception e) { - // TODO Logging + ProgramLogger.LogException($"|UWP|InitAppsInPackage|{Location}" + + "|Unexpected exception occurs when trying to construct a Application from package" + + $"{FullName} from location {Location}", e); } } Apps = applist.ToArray(); @@ -72,7 +74,10 @@ public void InitAppsInPackage(Package package) var xmlRoot = xmlDoc.DocumentElement; var packageVersion = GetPackageVersionFromManifest(xmlRoot); - var logoName = logoNameFromNamespace[packageVersion]; + if (!logoNameFromVersion.TryGetValue(packageVersion, out string logoName)) + { + return; + } var namespaceManager = new XmlNamespaceManager(xmlDoc.NameTable); namespaceManager.AddNamespace("", "http://schemas.microsoft.com/appx/manifest/foundation/windows10"); @@ -92,15 +97,18 @@ public void InitAppsInPackage(Package package) if (appNode != null) { app.CanRunElevated = packageCanElevate || Application.IfAppCanRunElevated(appNode, namespaceManager); + var visualElement = appNode.SelectSingleNode($"//*[name()='uap:VisualElements']", namespaceManager); var logoUri = visualElement.Attributes[logoName]?.Value; - app.InitLogoPathFromUri(logoUri); + app.LogoPath = app.LogoPathFromUri(logoUri); } } } catch (Exception e) { - // TODO Logging + ProgramLogger.LogException($"|UWP|InitAppsInPackage|{Location}" + + "|Unexpected exception occurs when trying to construct a Application from package" + + $"{FullName} from location {Location}", e); } } @@ -143,15 +151,15 @@ private PackageVersion GetPackageVersionFromManifest(XmlElement xmlRoot) } 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()); + "|Trying to get the package version of the UWP program, but an unknown UWP appmanifest version in package " + + $"{FullName} from location {Location}", new FormatException()); return PackageVersion.Unknown; } else { ProgramLogger.LogException($"|UWP|GetPackageVersionFromManifest|{Location}" + - "|Can't parse AppManifest.xml" - + $"{FullName} from location {Location} is returned.", new ArgumentNullException(nameof(xmlRoot))); + "|Can't parse AppManifest.xml of package " + + $"{FullName} from location {Location}", new ArgumentNullException(nameof(xmlRoot))); return PackageVersion.Unknown; } } @@ -169,7 +177,7 @@ private PackageVersion GetPackageVersionFromManifest(XmlElement xmlRoot) }, }; - private static readonly Dictionary logoNameFromNamespace = new() + private static readonly Dictionary logoNameFromVersion = new() { { PackageVersion.Windows10, "Square44x44Logo" From a9c271502a85088868a82afbd6678dc532b48e4a Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Tue, 29 Nov 2022 01:48:04 +0800 Subject: [PATCH 14/26] Reuse LogoPathFromUri() for preview image --- .../Programs/UWP.cs | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index ab582a033cc..1d292cbab9f 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -78,7 +78,7 @@ public void InitAppsInPackage(Package package) { return; } - + 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"); @@ -593,7 +593,7 @@ internal static bool IfAppCanRunElevated(XmlNode appNode, XmlNamespaceManager na // return $"{prefix}//{packageName}{key}"; //} - internal void InitLogoPathFromUri(string uri) + internal string LogoPathFromUri(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 @@ -602,13 +602,15 @@ internal void InitLogoPathFromUri(string uri) if (string.IsNullOrWhiteSpace(uri)) { - throw new ArgumentException("uri"); + ProgramLogger.LogException($"|UWP|LogoPathFromUri|{Location}" + + $"|{UserModelId} 's logo uri is null or empty: {Location}", new ArgumentException("uri")); + return string.Empty; } string path = Path.Combine(Location, uri); var logoPath = TryToFindLogo(uri, path); - if (String.IsNullOrEmpty(logoPath)) + if (logoPath == string.Empty) { var tmp = Path.Combine(Location, "Assets", uri); if (!path.Equals(tmp, StringComparison.OrdinalIgnoreCase)) @@ -616,17 +618,10 @@ internal void InitLogoPathFromUri(string uri) // TODO: Don't know why, just keep it at the moment // Maybe on older version of Windows 10? // for C:\Windows\MiracastView etc - logoPath = TryToFindLogo(uri, tmp); - if (!String.IsNullOrEmpty(logoPath)) - { - LogoPath = logoPath; - } + return TryToFindLogo(uri, tmp); } } - else - { - LogoPath = logoPath; - } + return logoPath; string TryToFindLogo(string uri, string path) { @@ -714,7 +709,6 @@ public ImageSource Logo() } private BitmapImage ImageFromPath(string path) { - // TODO: Consider using infrastructure.image.imageloader? if (File.Exists(path)) { var image = new BitmapImage(); From da19788df14fb46ef2343d736228b734ce1ffdf0 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Tue, 29 Nov 2022 02:06:11 +0800 Subject: [PATCH 15/26] Fix potential null reference --- Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index 1d292cbab9f..33c469acb94 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -32,7 +32,6 @@ public class UWP public Application[] Apps { get; set; } = Array.Empty(); - //public PackageVersion Version { get; set; } public UWP(Package package) { @@ -99,7 +98,7 @@ public void InitAppsInPackage(Package package) app.CanRunElevated = packageCanElevate || Application.IfAppCanRunElevated(appNode, namespaceManager); var visualElement = appNode.SelectSingleNode($"//*[name()='uap:VisualElements']", namespaceManager); - var logoUri = visualElement.Attributes[logoName]?.Value; + var logoUri = visualElement?.Attributes[logoName]?.Value; app.LogoPath = app.LogoPathFromUri(logoUri); } } @@ -110,7 +109,6 @@ public void InitAppsInPackage(Package package) "|Unexpected exception occurs when trying to construct a Application from package" + $"{FullName} from location {Location}", e); } - } private XmlDocument GetManifestXml() @@ -635,7 +633,7 @@ string TryToFindLogo(string uri, string path) var logoNamePrefix = Path.GetFileNameWithoutExtension(uri); // e.g Square44x44 var logoDir = Path.GetDirectoryName(path); // e.g ..\..\Assets - if (String.IsNullOrEmpty(logoNamePrefix) || String.IsNullOrEmpty(logoDir) || !Directory.Exists(logoDir)) + if (String.IsNullOrEmpty(logoNamePrefix) || !Directory.Exists(logoDir)) { // Known issue: Edge always triggers it since logo is not at uri ProgramLogger.LogException($"|UWP|LogoPathFromUri|{Location}" + From 729983352901bcd1e919686666e01561a62d86cf Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Tue, 29 Nov 2022 02:14:43 +0800 Subject: [PATCH 16/26] Formatting --- .../Programs/UWP.cs | 137 +----------------- 1 file changed, 2 insertions(+), 135 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index 33c469acb94..c9cdf9cd71a 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -3,10 +3,7 @@ using System.Diagnostics; using System.IO; using System.Linq; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.ComTypes; using System.Security.Principal; -using System.Text; using System.Threading.Tasks; using System.Windows.Media; using System.Windows.Media.Imaging; @@ -77,7 +74,7 @@ public void InitAppsInPackage(Package package) { return; } - + 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"); @@ -393,7 +390,6 @@ public Result Result(string query, IPublicAPI api) { Title = title, SubTitle = Main._settings.HideAppsPath ? string.Empty : Location, - //Icon = Logo, IcoPath = LogoPath, Score = matchResult.Score, TitleHighlightData = matchResult.MatchData, @@ -458,7 +454,7 @@ public List ContextMenus(IPublicAPI api) return contextMenus; } - private void Launch(bool elevated=false) + private void Launch(bool elevated = false) { string command = "shell:AppsFolder\\" + UserModelId; command = Environment.ExpandEnvironmentVariables(command.Trim()); @@ -472,56 +468,6 @@ private void Launch(bool elevated=false) 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 - - // 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; - - // Location = package.Location; - - // DisplayName = ResourceFromPri(package.FullName, package.Name, DisplayName); - // Description = ResourceFromPri(package.FullName, package.Name, Description); - // LogoUri = LogoUriFromManifest(manifestApp); - // LogoPath = LogoPathFromUri(LogoUri); - - // Enabled = true; - // CanRunElevated = CanApplicationRunElevated(); - //} - - //private bool CanApplicationRunElevated() - //{ - // if (EntryPoint == "Windows.FullTrustApplication") - // { - // return true; - // } - - // var manifest = Location + "\\AppxManifest.xml"; - // if (File.Exists(manifest)) - // { - // var file = File.ReadAllText(manifest); - - // if (file.Contains("TrustLevel=\"mediumIL\"", StringComparison.OrdinalIgnoreCase)) - // { - // return true; - // } - // } - - // return false; - //} - internal static bool IfAppCanRunElevated(XmlNode appNode, XmlNamespaceManager namespaceManager) { // According to https://learn.microsoft.com/windows/apps/desktop/modernize/grant-identity-to-nonpackaged-apps#create-a-package-manifest-for-the-sparse-package @@ -534,63 +480,6 @@ internal static bool IfAppCanRunElevated(XmlNode appNode, XmlNamespaceManager na trustLevelNode?.Attributes["uap10:TrustLevel"]?.Value == "mediumIL"; } - //internal string ResourceFromPri(string packageFullName, string packageName, string rawReferenceValue) - //{ - // if (string.IsNullOrWhiteSpace(rawReferenceValue) || !rawReferenceValue.StartsWith("ms-resource:")) - // return rawReferenceValue; - - // var formattedPriReference = FormattedPriReferenceValue(packageName, rawReferenceValue); - - // var outBuffer = new StringBuilder(128); - // string source = $"@{{{packageFullName}? {formattedPriReference}}}"; - // var capacity = (uint)outBuffer.Capacity; - // var hResult = SHLoadIndirectString(source, outBuffer, capacity, IntPtr.Zero); - // if (hResult == Hresult.Ok) - // { - // var loaded = outBuffer.ToString(); - // if (!string.IsNullOrEmpty(loaded)) - // { - // return loaded; - // } - // else - // { - // ProgramLogger.LogException($"|UWP|ResourceFromPri|{Location}|Can't load null or empty result " - // + $"pri {source} in uwp location {Location}", new NullReferenceException()); - // return string.Empty; - // } - // } - // else - // { - // var e = Marshal.GetExceptionForHR((int)hResult); - // ProgramLogger.LogException($"|UWP|ResourceFromPri|{Location}|Load pri failed {source} with HResult {hResult} and location {Location}", e); - // return string.Empty; - // } - //} - - //public static string FormattedPriReferenceValue(string packageName, string rawPriReferenceValue) - //{ - // const string prefix = "ms-resource:"; - - // if (string.IsNullOrWhiteSpace(rawPriReferenceValue) || !rawPriReferenceValue.StartsWith(prefix)) - // return rawPriReferenceValue; - - // string key = rawPriReferenceValue.Substring(prefix.Length); - // if (key.StartsWith("//")) - // return $"{prefix}{key}"; - - // if (!key.StartsWith("/")) - // { - // key = $"/{key}"; - // } - - // if (!key.ToLower().Contains("resources")) - // { - // key = $"/Resources{key}"; - // } - - // return $"{prefix}//{packageName}{key}"; - //} - internal string LogoPathFromUri(string uri) { // all https://msdn.microsoft.com/windows/uwp/controls-and-patterns/tiles-and-notifications-app-assets @@ -812,27 +701,5 @@ public enum PackageVersion Windows8, Unknown } - - //[Flags] - //private enum Stgm : uint - //{ - // Read = 0x0, - // ShareExclusive = 0x10, - // ShareDenyNone = 0x40 - //} - - //private enum Hresult : uint - //{ - // Ok = 0x0000, - //} - - //[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)] - //private static extern Hresult SHCreateStreamOnFileEx(string fileName, Stgm grfMode, uint attributes, bool create, - // IStream reserved, out IStream stream); - - //[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)] - //private static extern Hresult SHLoadIndirectString(string pszSource, StringBuilder pszOutBuf, uint cchOutBuf, - // IntPtr ppvReserved); - } } From f40f41628da05becc9a122da096aa2d2112475ff Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Tue, 29 Nov 2022 12:12:09 +0800 Subject: [PATCH 17/26] Comment legacy logo code --- .../Programs/UWP.cs | 178 +++++++++--------- 1 file changed, 91 insertions(+), 87 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index c9cdf9cd71a..756334ec692 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -333,7 +333,7 @@ public class Application : IProgram public string DisplayName { get; set; } = string.Empty; public string Description { get; set; } = string.Empty; public string UserModelId { get; set; } = string.Empty; - public string BackgroundColor { get; set; } = string.Empty; + //public string BackgroundColor { get; set; } = string.Empty; // preserve for future use public string Name => DisplayName; public string Location { get; set; } = string.Empty; @@ -585,92 +585,96 @@ string TryToFindLogo(string uri, string path) } - public ImageSource Logo() - { - var logo = ImageFromPath(LogoPath); - var plated = PlatedImage(logo); // TODO: maybe get plated directly from app package? - - // todo magic! temp fix for cross thread object - plated.Freeze(); - return plated; - } - private BitmapImage ImageFromPath(string path) - { - if (File.Exists(path)) - { - var image = new BitmapImage(); - image.BeginInit(); - image.UriSource = new Uri(path); - image.CacheOption = BitmapCacheOption.OnLoad; - image.EndInit(); - image.Freeze(); - return image; - } - else - { - ProgramLogger.LogException($"|UWP|ImageFromPath|{(string.IsNullOrEmpty(path) ? "Not Avaliable" : path)}" + - $"|Unable to get logo for {UserModelId} from {path} and" + - $" located in {Location}", new FileNotFoundException()); - return new BitmapImage(new Uri(Constant.MissingImgIcon)); - } - } - - private ImageSource PlatedImage(BitmapImage image) - { - if (!string.IsNullOrEmpty(BackgroundColor) && BackgroundColor != "transparent") - { - var width = image.Width; - var height = image.Height; - var x = 0; - var y = 0; - - var group = new DrawingGroup(); - - var converted = ColorConverter.ConvertFromString(BackgroundColor); - if (converted != null) - { - var color = (Color)converted; - var brush = new SolidColorBrush(color); - var pen = new Pen(brush, 1); - var backgroundArea = new Rect(0, 0, width, width); - var rectabgle = new RectangleGeometry(backgroundArea); - var rectDrawing = new GeometryDrawing(brush, pen, rectabgle); - group.Children.Add(rectDrawing); - - var imageArea = new Rect(x, y, image.Width, image.Height); - var imageDrawing = new ImageDrawing(image, imageArea); - group.Children.Add(imageDrawing); - - // http://stackoverflow.com/questions/6676072/get-system-drawing-bitmap-of-a-wpf-area-using-visualbrush - var visual = new DrawingVisual(); - var context = visual.RenderOpen(); - context.DrawDrawing(group); - context.Close(); - const int dpiScale100 = 96; - var bitmap = new RenderTargetBitmap( - Convert.ToInt32(width), Convert.ToInt32(height), - dpiScale100, dpiScale100, - PixelFormats.Pbgra32 - ); - bitmap.Render(visual); - return bitmap; - } - else - { - ProgramLogger.LogException($"|UWP|PlatedImage|{Location}" + - $"|Unable to convert background string {BackgroundColor} " + - $"to color for {Location}", new InvalidOperationException()); - - return new BitmapImage(new Uri(Constant.MissingImgIcon)); - } - } - else - { - // todo use windows theme as background - return image; - } - } - + #region logo legacy + // preserve for potential future use + + //public ImageSource Logo() + //{ + // var logo = ImageFromPath(LogoPath); + // var plated = PlatedImage(logo); // TODO: maybe get plated directly from app package? + + // // todo magic! temp fix for cross thread object + // plated.Freeze(); + // return plated; + //} + //private BitmapImage ImageFromPath(string path) + //{ + // if (File.Exists(path)) + // { + // var image = new BitmapImage(); + // image.BeginInit(); + // image.UriSource = new Uri(path); + // image.CacheOption = BitmapCacheOption.OnLoad; + // image.EndInit(); + // image.Freeze(); + // return image; + // } + // else + // { + // ProgramLogger.LogException($"|UWP|ImageFromPath|{(string.IsNullOrEmpty(path) ? "Not Avaliable" : path)}" + + // $"|Unable to get logo for {UserModelId} from {path} and" + + // $" located in {Location}", new FileNotFoundException()); + // return new BitmapImage(new Uri(Constant.MissingImgIcon)); + // } + //} + + //private ImageSource PlatedImage(BitmapImage image) + //{ + // if (!string.IsNullOrEmpty(BackgroundColor) && BackgroundColor != "transparent") + // { + // var width = image.Width; + // var height = image.Height; + // var x = 0; + // var y = 0; + + // var group = new DrawingGroup(); + + // var converted = ColorConverter.ConvertFromString(BackgroundColor); + // if (converted != null) + // { + // var color = (Color)converted; + // var brush = new SolidColorBrush(color); + // var pen = new Pen(brush, 1); + // var backgroundArea = new Rect(0, 0, width, width); + // var rectabgle = new RectangleGeometry(backgroundArea); + // var rectDrawing = new GeometryDrawing(brush, pen, rectabgle); + // group.Children.Add(rectDrawing); + + // var imageArea = new Rect(x, y, image.Width, image.Height); + // var imageDrawing = new ImageDrawing(image, imageArea); + // group.Children.Add(imageDrawing); + + // // http://stackoverflow.com/questions/6676072/get-system-drawing-bitmap-of-a-wpf-area-using-visualbrush + // var visual = new DrawingVisual(); + // var context = visual.RenderOpen(); + // context.DrawDrawing(group); + // context.Close(); + // const int dpiScale100 = 96; + // var bitmap = new RenderTargetBitmap( + // Convert.ToInt32(width), Convert.ToInt32(height), + // dpiScale100, dpiScale100, + // PixelFormats.Pbgra32 + // ); + // bitmap.Render(visual); + // return bitmap; + // } + // else + // { + // ProgramLogger.LogException($"|UWP|PlatedImage|{Location}" + + // $"|Unable to convert background string {BackgroundColor} " + + // $"to color for {Location}", new InvalidOperationException()); + + // return new BitmapImage(new Uri(Constant.MissingImgIcon)); + // } + // } + // else + // { + // // todo use windows theme as background + // return image; + // } + //} + + #endregion public override string ToString() { return $"{DisplayName}: {Description}"; From 78bc3f48093d4373ba3d0ae68835fe5a935b8c75 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Tue, 29 Nov 2022 12:30:36 +0800 Subject: [PATCH 18/26] Prepare for preview panel --- .../Programs/UWP.cs | 39 ++++++++++++++----- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index 756334ec692..05194b8de9f 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -70,7 +70,8 @@ public void InitAppsInPackage(Package package) var xmlRoot = xmlDoc.DocumentElement; var packageVersion = GetPackageVersionFromManifest(xmlRoot); - if (!logoNameFromVersion.TryGetValue(packageVersion, out string logoName)) + if (!smallLogoNameFromVersion.TryGetValue(packageVersion, out string logoName) || + !bigLogoNameFromVersion.TryGetValue(packageVersion, out string bigLogoName)) { return; } @@ -96,7 +97,9 @@ public void InitAppsInPackage(Package package) var visualElement = appNode.SelectSingleNode($"//*[name()='uap:VisualElements']", namespaceManager); var logoUri = visualElement?.Attributes[logoName]?.Value; - app.LogoPath = app.LogoPathFromUri(logoUri); + app.LogoPath = app.LogoPathFromUri(logoUri, (32, 32)); + var previewUri = visualElement?.Attributes[bigLogoName]?.Value; + app.PreviewImagePath = app.LogoPathFromUri(previewUri, (128, 128)); } } } @@ -172,7 +175,7 @@ private PackageVersion GetPackageVersionFromManifest(XmlElement xmlRoot) }, }; - private static readonly Dictionary logoNameFromVersion = new() + private static readonly Dictionary smallLogoNameFromVersion = new() { { PackageVersion.Windows10, "Square44x44Logo" @@ -185,6 +188,19 @@ private PackageVersion GetPackageVersionFromManifest(XmlElement xmlRoot) }, }; + private static readonly Dictionary bigLogoNameFromVersion = new() + { + { + PackageVersion.Windows10, "Square150x150Logo" + }, + { + PackageVersion.Windows81, "Square150x150Logo" + }, + { + PackageVersion.Windows8, "Logo" + }, + }; + public static Application[] All() { var windows10 = new Version(10, 0); @@ -340,6 +356,7 @@ public class Application : IProgram public bool Enabled { get; set; } = false; public bool CanRunElevated { get; set; } = false; public string LogoPath { get; set; } = string.Empty; + public string PreviewImagePath { get; set; } = string.Empty; public Application(AppListEntry appListEntry, UWP package) { @@ -480,7 +497,7 @@ internal static bool IfAppCanRunElevated(XmlNode appNode, XmlNamespaceManager na trustLevelNode?.Attributes["uap10:TrustLevel"]?.Value == "mediumIL"; } - internal string LogoPathFromUri(string uri) + internal string LogoPathFromUri(string uri, (int, int) desiredSize) { // 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 @@ -496,7 +513,8 @@ internal string LogoPathFromUri(string uri) string path = Path.Combine(Location, uri); - var logoPath = TryToFindLogo(uri, path); + var pxCount = desiredSize.Item1 * desiredSize.Item2; + var logoPath = TryToFindLogo(uri, path, pxCount); if (logoPath == string.Empty) { var tmp = Path.Combine(Location, "Assets", uri); @@ -505,12 +523,12 @@ internal string LogoPathFromUri(string uri) // 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); + return TryToFindLogo(uri, tmp, pxCount); } } return logoPath; - string TryToFindLogo(string uri, string path) + string TryToFindLogo(string uri, string path, int px) { var extension = Path.GetExtension(path); if (extension != null) @@ -544,6 +562,7 @@ string TryToFindLogo(string uri, string path) var selected = logos.FirstOrDefault(); var closest = selected; int min = int.MaxValue; + // TODO in one iterartion find preview and logo foreach (var logo in logos) { @@ -551,13 +570,13 @@ string TryToFindLogo(string uri, string path) var decoder = BitmapDecoder.Create(imageStream, BitmapCreateOptions.IgnoreColorProfile, BitmapCacheOption.None); var height = decoder.Frames[0].PixelHeight; var width = decoder.Frames[0].PixelWidth; - int pixelCountDiff = Math.Abs(height * width - 1936); // 44*44=1936 + int pixelCountDiff = Math.Abs(height * width - px); if (pixelCountDiff < min) { - // try to find the closest to 44x44 logo + // try to find the closest to desired size closest = logo; if (pixelCountDiff == 0) - break; // found 44x44 + break; // found min = pixelCountDiff; } } From 2fd241d776d20e44367e42cbbda0e1d1dd3926ca Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Tue, 29 Nov 2022 14:34:15 +0800 Subject: [PATCH 19/26] Use search pattern in Directory.EnumerateFiles --- Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index 05194b8de9f..795588aa644 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -548,21 +548,16 @@ string TryToFindLogo(string uri, string path, int px) return string.Empty; } - var files = Directory.EnumerateFiles(logoDir); + var logos = Directory.EnumerateFiles(logoDir, $"{logoNamePrefix}*{extension}"); // Currently we don't care which one to choose // Just ignore all qualifiers // select like logo.[xxx_yyy].png // https://learn.microsoft.com/en-us/windows/uwp/app-resources/tailor-resources-lang-scale-contrast - var logos = files.Where(file => - Path.GetFileName(file)?.StartsWith(logoNamePrefix, StringComparison.OrdinalIgnoreCase) ?? false - && extension.Equals(Path.GetExtension(file), StringComparison.OrdinalIgnoreCase) - ); var selected = logos.FirstOrDefault(); var closest = selected; int min = int.MaxValue; - // TODO in one iterartion find preview and logo foreach (var logo in logos) { From f9fbd0887310eb528b7cfbdbabfb6aba742f5db8 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Tue, 29 Nov 2022 14:38:09 +0800 Subject: [PATCH 20/26] Delete unused file --- .../Programs/AppxPackageHelper.cs | 80 ------------------- 1 file changed, 80 deletions(-) delete mode 100644 Plugins/Flow.Launcher.Plugin.Program/Programs/AppxPackageHelper.cs diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/AppxPackageHelper.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/AppxPackageHelper.cs deleted file mode 100644 index 62416609e9c..00000000000 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/AppxPackageHelper.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.ComTypes; - -namespace Flow.Launcher.Plugin.Program.Programs -{ - public class AppxPackageHelper - { - // This function returns a list of attributes of applications - public static List GetAppsFromManifest(IStream stream) - { - IAppxFactory appxFactory = (IAppxFactory)new AppxFactory(); - List apps = new List(); - var reader = appxFactory.CreateManifestReader(stream); - var manifestApps = reader.GetApplications(); - while (manifestApps.GetHasCurrent()) - { - string appListEntry; - var manifestApp = manifestApps.GetCurrent(); - manifestApp.GetStringValue("AppListEntry", out appListEntry); - if (appListEntry != "none") - { - apps.Add(manifestApp); - } - manifestApps.MoveNext(); - } - return apps; - } - - // Reference : https://stackoverflow.com/questions/32122679/getting-icon-of-modern-windows-app-from-a-desktop-application - [Guid("5842a140-ff9f-4166-8f5c-62f5b7b0c781"), ComImport] - public class AppxFactory - { - } - - [Guid("BEB94909-E451-438B-B5A7-D79E767B75D8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - public interface IAppxFactory - { - void _VtblGap0_2(); // skip 2 methods - IAppxManifestReader CreateManifestReader(IStream inputStream); - } - - [Guid("4E1BD148-55A0-4480-A3D1-15544710637C"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - public interface IAppxManifestReader - { - void _VtblGap0_1(); // skip 1 method - IAppxManifestProperties GetProperties(); - void _VtblGap1_5(); // skip 5 methods - IAppxManifestApplicationsEnumerator GetApplications(); - } - - [Guid("9EB8A55A-F04B-4D0D-808D-686185D4847A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - public interface IAppxManifestApplicationsEnumerator - { - IAppxManifestApplication GetCurrent(); - bool GetHasCurrent(); - bool MoveNext(); - } - - [Guid("5DA89BF4-3773-46BE-B650-7E744863B7E8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - public interface IAppxManifestApplication - { - [PreserveSig] - int GetStringValue([MarshalAs(UnmanagedType.LPWStr)] string name, [MarshalAs(UnmanagedType.LPWStr)] out string value); - - [PreserveSig] - int GetAppUserModelId([MarshalAs(UnmanagedType.LPWStr)] out string value); - } - - [Guid("03FAF64D-F26F-4B2C-AAF7-8FE7789B8BCA"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - public interface IAppxManifestProperties - { - [PreserveSig] - int GetBoolValue([MarshalAs(UnmanagedType.LPWStr)]string name, out bool value); - [PreserveSig] - int GetStringValue([MarshalAs(UnmanagedType.LPWStr)] string name, [MarshalAs(UnmanagedType.LPWStr)] out string value); - } - } -} From 713c586b9f382dd0a34551270d8ab93b5220ec6e Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Tue, 29 Nov 2022 14:40:50 +0800 Subject: [PATCH 21/26] Fix IDE0005 --- Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index 795588aa644..28a368948fd 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -5,13 +5,11 @@ using System.Linq; using System.Security.Principal; using System.Threading.Tasks; -using System.Windows.Media; using System.Windows.Media.Imaging; 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 System.Threading.Channels; using System.Xml; From a2d57c14bf18739b3ecbe8f383fde70279cc33c8 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Tue, 29 Nov 2022 17:49:36 +0800 Subject: [PATCH 22/26] Fix xpath issue --- .../Programs/UWP.cs | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index 28a368948fd..4964550d739 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -75,25 +75,26 @@ public void InitAppsInPackage(Package package) } var namespaceManager = new XmlNamespaceManager(xmlDoc.NameTable); - namespaceManager.AddNamespace("", "http://schemas.microsoft.com/appx/manifest/foundation/windows10"); + namespaceManager.AddNamespace("d", "http://schemas.microsoft.com/appx/manifest/foundation/windows10"); // still need a name 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("//*[name()='rescap:Capability' and @Name='allowElevation']", namespaceManager); + var allowElevationNode = xmlRoot.SelectSingleNode("//rescap:Capability[@Name='allowElevation']", namespaceManager); bool packageCanElevate = allowElevationNode != null; - var appsNode = xmlRoot.SelectSingleNode("//*[name()='Applications']", namespaceManager); + var appsNode = xmlRoot.SelectSingleNode("d: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($"//*[name()='Application' and @Id='{id}']", namespaceManager); + var appNode = appsNode?.SelectSingleNode($"d:Application[@Id='{id}']", namespaceManager); if (appNode != null) { - app.CanRunElevated = packageCanElevate || Application.IfAppCanRunElevated(appNode, namespaceManager); + app.CanRunElevated = packageCanElevate || Application.IfAppCanRunElevated(appNode); - var visualElement = appNode.SelectSingleNode($"//*[name()='uap:VisualElements']", namespaceManager); + // local name to fit all versions + var visualElement = appNode.SelectSingleNode($"*[local-name()='VisualElements']", namespaceManager); var logoUri = visualElement?.Attributes[logoName]?.Value; app.LogoPath = app.LogoPathFromUri(logoUri, (32, 32)); var previewUri = visualElement?.Attributes[bigLogoName]?.Value; @@ -131,8 +132,7 @@ private XmlDocument GetManifestXml() } } - /// http://www.hanselman.com/blog/GetNamespacesFromAnXMLDocumentWithXPathDocumentAndLINQToXML.aspx - private PackageVersion GetPackageVersionFromManifest(XmlElement xmlRoot) + private PackageVersion GetPackageVersionFromManifest(XmlNode xmlRoot) { if (xmlRoot != null) { @@ -483,16 +483,13 @@ private void Launch(bool elevated = false) Main.StartProcess(Process.Start, info); } - internal static bool IfAppCanRunElevated(XmlNode appNode, XmlNamespaceManager namespaceManager) + internal static bool IfAppCanRunElevated(XmlNode appNode) { // 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 entryPointNode = appNode.SelectSingleNode($"//*[local-name()='Application' and @EntryPoint]", namespaceManager); - var trustLevelNode = appNode.SelectSingleNode($"//*[local-name()='Application' and @uap10:TrustLevel]", namespaceManager); - - return entryPointNode?.Attributes["EntryPoint"]?.Value == "Windows.FullTrustApplication" || - trustLevelNode?.Attributes["uap10:TrustLevel"]?.Value == "mediumIL"; + return appNode?.Attributes["EntryPoint"]?.Value == "Windows.FullTrustApplication" || + appNode?.Attributes["uap10:TrustLevel"]?.Value == "mediumIL"; } internal string LogoPathFromUri(string uri, (int, int) desiredSize) From 416fcd1881682bfe3efd7f3d91bd0e39abd66f0d Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Tue, 29 Nov 2022 17:50:49 +0800 Subject: [PATCH 23/26] set desired size to 64x64 --- Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index 4964550d739..180e8aea172 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -96,7 +96,7 @@ public void InitAppsInPackage(Package package) // local name to fit all versions var visualElement = appNode.SelectSingleNode($"*[local-name()='VisualElements']", namespaceManager); var logoUri = visualElement?.Attributes[logoName]?.Value; - app.LogoPath = app.LogoPathFromUri(logoUri, (32, 32)); + app.LogoPath = app.LogoPathFromUri(logoUri, (64, 64)); var previewUri = visualElement?.Attributes[bigLogoName]?.Value; app.PreviewImagePath = app.LogoPathFromUri(previewUri, (128, 128)); } From 422307d507d8c763071ba72cb425b8755f3441fa Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Tue, 29 Nov 2022 17:56:56 +0800 Subject: [PATCH 24/26] formatting --- Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index 180e8aea172..8ea5d0c9aa0 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -59,7 +59,6 @@ public void InitAppsInPackage(Package package) try { - // From powertoys run var xmlDoc = GetManifestXml(); if (xmlDoc == null) { From 592b838f3eafa723f0d888d52ac2b3e578cabfa0 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Tue, 29 Nov 2022 18:13:55 +0800 Subject: [PATCH 25/26] Fix hard-coded translation --- Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml | 2 ++ Plugins/Flow.Launcher.Plugin.Program/Main.cs | 7 ++++--- Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml index 65432280590..700c37046d4 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml @@ -81,7 +81,9 @@ Success + Error Successfully disabled this program from displaying in your query This app is not intended to be run as administrator + Unable to run {0} \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index feeb8e78a24..c94c1dca2a9 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -5,6 +5,7 @@ using System.Threading; using System.Threading.Tasks; using System.Windows.Controls; +using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.Storage; using Flow.Launcher.Plugin.Program.Programs; @@ -225,9 +226,9 @@ public static void StartProcess(Func runProcess, Proc } catch (Exception) { - var name = "Plugin: Program"; - var message = $"Unable to start: {info.FileName}"; - Context.API.ShowMsg(name, message, string.Empty); + var title = Context.API.GetTranslation("flowlauncher_plugin_program_disable_dlgtitle_error"); + var message = string.Format(Context.API.GetTranslation("flowlauncher_plugin_program_run_failed"), info.FileName); + Context.API.ShowMsg(title, string.Format(message, info.FileName), string.Empty); } } diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs index 8ea5d0c9aa0..627502ea1c5 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs @@ -421,7 +421,7 @@ public Result Result(string query, IPublicAPI api) _ = Task.Run(() => Launch(shouldRunElevated)).ConfigureAwait(false); if (elevated && !shouldRunElevated) { - var title = "Plugin: Program"; + var title = api.GetTranslation("flowlauncher_plugin_program_disable_dlgtitle_error"); var message = api.GetTranslation("flowlauncher_plugin_program_run_as_administrator_not_supported_message"); api.ShowMsg(title, message, string.Empty); } From 8dffdfc61d5517f525a0a47a9061f40a3a4d2943 Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Wed, 30 Nov 2022 16:42:12 +0800 Subject: [PATCH 26/26] Delete ProgramTest.cs --- Flow.Launcher.Test/Plugins/ProgramTest.cs | 30 ----------------------- 1 file changed, 30 deletions(-) delete mode 100644 Flow.Launcher.Test/Plugins/ProgramTest.cs diff --git a/Flow.Launcher.Test/Plugins/ProgramTest.cs b/Flow.Launcher.Test/Plugins/ProgramTest.cs deleted file mode 100644 index 10818ec4951..00000000000 --- a/Flow.Launcher.Test/Plugins/ProgramTest.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Flow.Launcher.Plugin.Program.Programs; -using NUnit.Framework; -using System; -using Windows.ApplicationModel; - -namespace Flow.Launcher.Test.Plugins -{ - [TestFixture] - public class ProgramTest - { - //[TestCase("Microsoft.WindowsCamera", "ms-resource:LensSDK/Resources/AppTitle", "ms-resource://Microsoft.WindowsCamera/LensSDK/Resources/AppTitle")] - //[TestCase("microsoft.windowscommunicationsapps", "ms-resource://microsoft.windowscommunicationsapps/hxoutlookintl/AppManifest_MailDesktop_DisplayName", - // "ms-resource://microsoft.windowscommunicationsapps/hxoutlookintl/AppManifest_MailDesktop_DisplayName")] - //[TestCase("windows.immersivecontrolpanel", "ms-resource:DisplayName", "ms-resource://windows.immersivecontrolpanel/Resources/DisplayName")] - //[TestCase("Microsoft.MSPaint", "ms-resource:AppName", "ms-resource://Microsoft.MSPaint/Resources/AppName")] - //public void WhenGivenPriReferenceValueShouldReturnCorrectFormat(string packageName, string rawPriReferenceValue, string expectedFormat) - //{ - // // Arrange - // var app = new UWP.Application(); - - // // Act - // var result = UWP.Application.FormattedPriReferenceValue(packageName, rawPriReferenceValue); - - // // Assert - // Assert.IsTrue(result == expectedFormat, - // $"Expected Pri reference format: {expectedFormat}{Environment.NewLine} " + - // $"Actual: {result}{Environment.NewLine}"); - //} - } -}