2022-08-09 00:35:38 +00:00
using System ;
2016-08-18 00:16:40 +00:00
using System.Collections.Generic ;
2016-08-22 01:21:22 +00:00
using System.Diagnostics ;
2016-08-18 00:16:40 +00:00
using System.IO ;
using System.Linq ;
using System.Runtime.InteropServices ;
2020-06-24 11:55:52 +00:00
using System.Runtime.InteropServices.ComTypes ;
2016-08-18 00:16:40 +00:00
using System.Security.Principal ;
2016-11-29 01:18:38 +00:00
using System.Text ;
using System.Threading.Tasks ;
2016-08-18 00:56:26 +00:00
using System.Windows.Media ;
2016-08-18 00:16:40 +00:00
using System.Windows.Media.Imaging ;
using Windows.ApplicationModel ;
2016-08-19 22:24:21 +00:00
using Windows.Management.Deployment ;
2020-04-21 09:12:17 +00:00
using Flow.Launcher.Infrastructure ;
using Flow.Launcher.Plugin.Program.Logger ;
2016-08-19 22:24:21 +00:00
using Rect = System . Windows . Rect ;
2021-02-01 11:59:56 +00:00
using Flow.Launcher.Plugin.SharedModels ;
2022-02-21 21:54:46 +00:00
using System.Threading.Channels ;
2022-11-28 07:51:28 +00:00
using System.Xml ;
2022-11-28 11:06:16 +00:00
using Windows.ApplicationModel.Core ;
2016-08-18 00:16:40 +00:00
2020-04-21 09:12:17 +00:00
namespace Flow.Launcher.Plugin.Program.Programs
2016-08-18 00:16:40 +00:00
{
2017-01-13 01:21:00 +00:00
[Serializable]
2016-08-18 00:56:26 +00:00
public class UWP
2016-08-18 00:16:40 +00:00
{
public string Name { get ; }
public string FullName { get ; }
public string FamilyName { get ; }
public string Location { get ; set ; }
2022-11-28 07:51:28 +00:00
public Application [ ] Apps { get ; set ; } = Array . Empty < Application > ( ) ;
2016-08-18 00:16:40 +00:00
2016-11-29 01:18:38 +00:00
2016-08-18 00:56:26 +00:00
public UWP ( Package package )
2016-08-18 00:16:40 +00:00
{
2017-01-13 01:21:00 +00:00
Location = package . InstalledLocation . Path ;
Name = package . Id . Name ;
FullName = package . Id . FullName ;
FamilyName = package . Id . FamilyName ;
2022-11-28 07:51:28 +00:00
}
2022-11-28 17:22:13 +00:00
public void InitAppsInPackage ( Package package )
2022-11-28 07:51:28 +00:00
{
var applist = new List < Application > ( ) ;
2022-11-28 14:01:16 +00:00
// WinRT
var appListEntries = package . GetAppListEntries ( ) ;
foreach ( var app in appListEntries )
2022-11-28 07:51:28 +00:00
{
2022-11-28 14:01:16 +00:00
try
2022-11-28 07:51:28 +00:00
{
2022-11-28 14:01:16 +00:00
var tmp = new Application ( app , this ) ;
applist . Add ( tmp ) ;
}
catch ( Exception e )
{
2022-11-28 17:46:48 +00:00
ProgramLogger . LogException ( $"|UWP|InitAppsInPackage|{Location}" +
"|Unexpected exception occurs when trying to construct a Application from package"
+ $"{FullName} from location {Location}" , e ) ;
2022-11-28 07:51:28 +00:00
}
2022-11-28 14:01:16 +00:00
}
Apps = applist . ToArray ( ) ;
2022-11-28 10:46:48 +00:00
2022-11-28 14:01:16 +00:00
try
{
2022-11-28 10:46:48 +00:00
// From powertoys run
var xmlDoc = GetManifestXml ( ) ;
if ( xmlDoc = = null )
{
return ;
}
var xmlRoot = xmlDoc . DocumentElement ;
2022-11-28 14:01:16 +00:00
var packageVersion = GetPackageVersionFromManifest ( xmlRoot ) ;
2022-11-28 17:46:48 +00:00
if ( ! logoNameFromVersion . TryGetValue ( packageVersion , out string logoName ) )
{
return ;
}
2022-11-28 17:48:04 +00:00
2022-11-28 10:46:48 +00:00
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" ) ;
2022-11-28 14:01:16 +00:00
var allowElevationNode = xmlRoot . SelectSingleNode ( "//*[name()='rescap:Capability' and @Name='allowElevation']" , namespaceManager ) ;
2022-11-28 10:46:48 +00:00
bool packageCanElevate = allowElevationNode ! = null ;
2022-11-28 14:01:16 +00:00
var appsNode = xmlRoot . SelectSingleNode ( "//*[name()='Applications']" , namespaceManager ) ;
2022-11-28 10:46:48 +00:00
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 ] ;
2022-11-28 14:01:16 +00:00
var appNode = appsNode ? . SelectSingleNode ( $"//*[name()='Application' and @Id='{id}']" , namespaceManager ) ;
2022-11-28 10:46:48 +00:00
if ( appNode ! = null )
{
app . CanRunElevated = packageCanElevate | | Application . IfAppCanRunElevated ( appNode , namespaceManager ) ;
2022-11-28 17:46:48 +00:00
2022-11-28 14:01:16 +00:00
var visualElement = appNode . SelectSingleNode ( $"//*[name()='uap:VisualElements']" , namespaceManager ) ;
2022-11-28 18:06:11 +00:00
var logoUri = visualElement ? . Attributes [ logoName ] ? . Value ;
2022-11-28 17:46:48 +00:00
app . LogoPath = app . LogoPathFromUri ( logoUri ) ;
2022-11-28 10:46:48 +00:00
}
}
2022-11-28 07:51:28 +00:00
}
catch ( Exception e )
{
2022-11-28 17:46:48 +00:00
ProgramLogger . LogException ( $"|UWP|InitAppsInPackage|{Location}" +
"|Unexpected exception occurs when trying to construct a Application from package"
+ $"{FullName} from location {Location}" , e ) ;
2022-11-28 07:51:28 +00:00
}
}
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 ;
}
}
2016-11-29 01:18:38 +00:00
/// http://www.hanselman.com/blog/GetNamespacesFromAnXMLDocumentWithXPathDocumentAndLINQToXML.aspx
2022-11-28 14:01:16 +00:00
private PackageVersion GetPackageVersionFromManifest ( XmlElement xmlRoot )
2016-11-29 01:18:38 +00:00
{
2022-11-28 14:01:16 +00:00
if ( xmlRoot ! = null )
2016-11-29 01:18:38 +00:00
{
2022-11-28 14:01:16 +00:00
var namespaces = xmlRoot . Attributes ;
foreach ( XmlAttribute ns in namespaces )
{
if ( versionFromNamespace . TryGetValue ( ns . Value , out var packageVersion ) )
{
return packageVersion ;
}
}
ProgramLogger . LogException ( $"|UWP|GetPackageVersionFromManifest|{Location}" +
2022-11-28 17:46:48 +00:00
"|Trying to get the package version of the UWP program, but an unknown UWP appmanifest version in package "
+ $"{FullName} from location {Location}" , new FormatException ( ) ) ;
2022-11-28 14:01:16 +00:00
return PackageVersion . Unknown ;
2016-11-29 01:18:38 +00:00
}
else
{
2022-11-28 14:01:16 +00:00
ProgramLogger . LogException ( $"|UWP|GetPackageVersionFromManifest|{Location}" +
2022-11-28 17:46:48 +00:00
"|Can't parse AppManifest.xml of package "
+ $"{FullName} from location {Location}" , new ArgumentNullException ( nameof ( xmlRoot ) ) ) ;
2022-11-28 14:01:16 +00:00
return PackageVersion . Unknown ;
2016-11-29 01:18:38 +00:00
}
}
2022-11-28 14:01:16 +00:00
private static readonly Dictionary < string , PackageVersion > versionFromNamespace = new ( )
2016-11-29 01:18:38 +00:00
{
2022-01-08 21:50:27 +00:00
{
"http://schemas.microsoft.com/appx/manifest/foundation/windows10" , PackageVersion . Windows10
} ,
{
"http://schemas.microsoft.com/appx/2013/manifest" , PackageVersion . Windows81
} ,
{
"http://schemas.microsoft.com/appx/2010/manifest" , PackageVersion . Windows8
} ,
2022-11-28 14:01:16 +00:00
} ;
2016-11-29 01:18:38 +00:00
2022-11-28 17:46:48 +00:00
private static readonly Dictionary < PackageVersion , string > logoNameFromVersion = new ( )
2022-11-28 14:01:16 +00:00
{
2016-11-29 01:18:38 +00:00
{
2022-11-28 14:01:16 +00:00
PackageVersion . Windows10 , "Square44x44Logo"
} ,
{
PackageVersion . Windows81 , "Square30x30Logo"
} ,
{
PackageVersion . Windows8 , "SmallLogo"
} ,
} ;
2016-09-05 20:58:40 +00:00
2016-08-20 17:25:55 +00:00
public static Application [ ] All ( )
2016-08-18 00:16:40 +00:00
{
2016-08-20 00:17:28 +00:00
var windows10 = new Version ( 10 , 0 ) ;
var support = Environment . OSVersion . Version . Major > = windows10 . Major ;
if ( support )
2016-08-18 00:16:40 +00:00
{
2017-04-01 18:15:23 +00:00
var applications = CurrentUserPackages ( ) . AsParallel ( ) . SelectMany ( p = >
{
UWP u ;
try
{
u = new UWP ( p ) ;
2022-11-28 17:22:13 +00:00
u . InitAppsInPackage ( p ) ;
2017-04-01 18:15:23 +00:00
}
2019-10-25 02:11:52 +00:00
#if ! DEBUG
2017-04-01 18:15:23 +00:00
catch ( Exception e )
{
2022-08-10 18:52:16 +00:00
ProgramLogger . LogException ( $"|UWP|All|{p.InstalledLocation}|An unexpected error occured and unable to convert Package to UWP for {p.Id.FullName}" , e ) ;
2022-08-09 00:48:17 +00:00
return Array . Empty < Application > ( ) ;
2017-04-01 18:15:23 +00:00
}
2019-10-25 02:11:52 +00:00
#endif
#if DEBUG //make developer aware and implement handling
2019-11-16 00:37:01 +00:00
catch
2019-10-25 02:11:52 +00:00
{
2019-11-16 00:37:01 +00:00
throw ;
2019-10-25 02:11:52 +00:00
}
#endif
2017-04-01 18:15:23 +00:00
return u . Apps ;
} ) . ToArray ( ) ;
2019-09-09 21:57:03 +00:00
var updatedListWithoutDisabledApps = applications
2022-01-08 21:50:27 +00:00
. Where ( t1 = > ! Main . _settings . DisabledProgramSources
2022-10-24 17:13:36 +00:00
. Any ( x = > x . UniqueIdentifier = = t1 . UniqueIdentifier ) ) ;
2019-09-09 21:57:03 +00:00
return updatedListWithoutDisabledApps . ToArray ( ) ;
2016-08-20 00:17:28 +00:00
}
else
{
2022-10-21 10:35:10 +00:00
return Array . Empty < Application > ( ) ;
2016-08-20 00:17:28 +00:00
}
2016-08-18 00:16:40 +00:00
}
private static IEnumerable < Package > CurrentUserPackages ( )
{
2017-04-01 18:15:23 +00:00
var u = WindowsIdentity . GetCurrent ( ) . User ;
2016-08-18 00:16:40 +00:00
2017-04-01 18:15:23 +00:00
if ( u ! = null )
2016-08-18 00:16:40 +00:00
{
2017-04-01 18:15:23 +00:00
var id = u . Value ;
2021-02-11 19:18:38 +00:00
PackageManager m ;
try
{
m = new PackageManager ( ) ;
}
catch
{
2021-02-23 19:34:09 +00:00
// Bug from https://github.com/microsoft/CsWinRT, using Microsoft.Windows.SDK.NET.Ref 10.0.19041.0.
// Only happens on the first time, so a try catch can fix it.
2021-02-11 19:18:38 +00:00
m = new PackageManager ( ) ;
}
2017-04-01 18:15:23 +00:00
var ps = m . FindPackagesForUser ( id ) ;
ps = ps . Where ( p = >
{
2019-11-01 19:44:55 +00:00
try
{
var f = p . IsFramework ;
var d = p . IsDevelopmentMode ;
var path = p . InstalledLocation . Path ;
2022-11-28 17:22:13 +00:00
return ! f & & ! d & & ! string . IsNullOrEmpty ( path ) ;
2019-11-01 19:44:55 +00:00
}
catch ( Exception e )
{
2022-11-28 17:22:13 +00:00
ProgramLogger . LogException ( "UWP" , "CurrentUserPackages" , $"{id}" , "An unexpected error occured and "
2022-01-08 21:50:27 +00:00
+ $"unable to verify if package is valid" , e ) ;
2019-11-01 19:44:55 +00:00
return false ;
}
2017-04-01 18:15:23 +00:00
} ) ;
return ps ;
2016-08-18 00:16:40 +00:00
}
else
{
2022-10-21 10:35:10 +00:00
return Array . Empty < Package > ( ) ;
2016-08-18 00:16:40 +00:00
}
}
2022-02-21 21:54:46 +00:00
private static Channel < byte > PackageChangeChannel = Channel . CreateBounded < byte > ( 1 ) ;
2022-01-08 21:50:27 +00:00
2022-02-21 21:54:46 +00:00
public static async Task WatchPackageChange ( )
2022-01-08 21:50:27 +00:00
{
2022-02-21 21:54:46 +00:00
if ( Environment . OSVersion . Version . Major > = 10 )
{
var catalog = PackageCatalog . OpenForCurrentUser ( ) ;
catalog . PackageInstalling + = ( _ , args ) = >
{
if ( args . IsComplete )
PackageChangeChannel . Writer . TryWrite ( default ) ;
} ;
catalog . PackageUninstalling + = ( _ , args ) = >
2022-01-08 22:03:53 +00:00
{
2022-02-21 21:54:46 +00:00
if ( args . IsComplete )
PackageChangeChannel . Writer . TryWrite ( default ) ;
2022-01-08 22:03:53 +00:00
} ;
2022-02-21 21:54:46 +00:00
catalog . PackageUpdating + = ( _ , args ) = >
{
if ( args . IsComplete )
PackageChangeChannel . Writer . TryWrite ( default ) ;
} ;
while ( await PackageChangeChannel . Reader . WaitToReadAsync ( ) . ConfigureAwait ( false ) )
{
await Task . Delay ( 3000 ) . ConfigureAwait ( false ) ;
PackageChangeChannel . Reader . TryRead ( out _ ) ;
await Task . Run ( Main . IndexUwpPrograms ) ;
}
2022-11-28 11:06:16 +00:00
2022-02-21 21:54:46 +00:00
}
2022-01-08 21:50:27 +00:00
}
2016-08-18 00:16:40 +00:00
public override string ToString ( )
{
return FamilyName ;
}
public override bool Equals ( object obj )
{
2019-11-16 00:37:01 +00:00
if ( obj is UWP uwp )
2016-08-18 00:16:40 +00:00
{
return FamilyName . Equals ( uwp . FamilyName ) ;
}
else
{
return false ;
}
}
public override int GetHashCode ( )
{
return FamilyName . GetHashCode ( ) ;
}
2017-01-13 01:21:00 +00:00
[Serializable]
2016-08-22 01:21:22 +00:00
public class Application : IProgram
2016-08-18 00:16:40 +00:00
{
2022-11-14 16:07:12 +00:00
private string _uid = string . Empty ;
public string UniqueIdentifier { get = > _uid ; set = > _uid = value = = null ? string . Empty : value . ToLowerInvariant ( ) ; }
2022-11-28 14:13:55 +00:00
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 ;
2019-10-17 11:47:00 +00:00
public string Name = > DisplayName ;
2022-11-28 14:13:55 +00:00
public string Location { get ; set ; } = string . Empty ;
2019-10-17 11:47:00 +00:00
2022-11-28 14:08:21 +00:00
public bool Enabled { get ; set ; } = false ;
2022-11-28 07:51:28 +00:00
public bool CanRunElevated { get ; set ; } = false ;
2022-11-28 14:01:16 +00:00
public string LogoPath { get ; set ; } = string . Empty ;
2022-11-28 07:51:28 +00:00
2022-11-28 11:06:16 +00:00
public Application ( AppListEntry appListEntry , UWP package )
2022-11-28 10:43:56 +00:00
{
UserModelId = appListEntry . AppUserModelId ;
UniqueIdentifier = appListEntry . AppUserModelId ;
DisplayName = appListEntry . DisplayInfo . DisplayName ;
Description = appListEntry . DisplayInfo . Description ;
2022-11-28 11:06:16 +00:00
Location = package . Location ;
2022-11-28 10:43:56 +00:00
Enabled = true ;
}
2016-08-22 01:21:22 +00:00
public Result Result ( string query , IPublicAPI api )
2016-08-18 00:16:40 +00:00
{
2021-02-01 11:57:08 +00:00
string title ;
MatchResult matchResult ;
2020-10-25 03:26:54 +00:00
2021-02-01 11:57:08 +00:00
// We suppose Name won't be null
2021-05-20 09:07:44 +00:00
if ( ! Main . _settings . EnableDescription | | Description = = null | | Name . StartsWith ( Description ) )
2021-02-01 11:57:08 +00:00
{
title = Name ;
matchResult = StringMatcher . FuzzySearch ( query , title ) ;
}
else if ( Description . StartsWith ( Name ) )
{
title = Description ;
matchResult = StringMatcher . FuzzySearch ( query , Description ) ;
}
else
{
title = $"{Name}: {Description}" ;
var nameMatch = StringMatcher . FuzzySearch ( query , Name ) ;
var desciptionMatch = StringMatcher . FuzzySearch ( query , Description ) ;
if ( desciptionMatch . Score > nameMatch . Score )
{
for ( int i = 0 ; i < desciptionMatch . MatchData . Count ; i + + )
{
desciptionMatch . MatchData [ i ] + = Name . Length + 2 ; // 2 is ": "
}
matchResult = desciptionMatch ;
}
else matchResult = nameMatch ;
}
2020-10-25 03:26:54 +00:00
2021-02-02 09:28:52 +00:00
if ( ! matchResult . Success )
return null ;
2019-11-28 23:38:50 +00:00
2016-08-22 01:21:22 +00:00
var result = new Result
{
2020-10-25 03:26:54 +00:00
Title = title ,
2022-11-28 11:06:16 +00:00
SubTitle = Main . _settings . HideAppsPath ? string . Empty : Location ,
2022-11-28 14:55:10 +00:00
//Icon = Logo,
IcoPath = LogoPath ,
2020-10-25 03:26:54 +00:00
Score = matchResult . Score ,
TitleHighlightData = matchResult . MatchData ,
2016-08-22 01:21:22 +00:00
ContextData = this ,
Action = e = >
{
2021-09-11 12:30:30 +00:00
var elevated = (
e . SpecialKeyState . CtrlPressed & &
e . SpecialKeyState . ShiftPressed & &
! e . SpecialKeyState . AltPressed & &
! e . SpecialKeyState . WinPressed
2022-01-08 21:50:27 +00:00
) ;
2021-09-11 12:30:30 +00:00
2022-11-28 17:02:53 +00:00
bool shouldRunElevated = elevated & & CanRunElevated ;
_ = Task . Run ( ( ) = > Launch ( shouldRunElevated ) ) . ConfigureAwait ( false ) ;
if ( elevated & & ! shouldRunElevated )
2021-09-11 12:30:30 +00:00
{
2022-11-28 17:02:53 +00:00
var title = "Plugin: Program" ;
var message = api . GetTranslation ( "flowlauncher_plugin_program_run_as_administrator_not_supported_message" ) ;
api . ShowMsg ( title , message , string . Empty ) ;
2021-09-11 12:30:30 +00:00
}
2016-08-22 01:21:22 +00:00
return true ;
}
} ;
2020-10-25 03:26:54 +00:00
2016-08-22 01:21:22 +00:00
return result ;
}
public List < Result > ContextMenus ( IPublicAPI api )
{
var contextMenus = new List < Result >
{
new Result
{
2020-04-21 12:54:41 +00:00
Title = api . GetTranslation ( "flowlauncher_plugin_program_open_containing_folder" ) ,
2016-08-22 01:21:22 +00:00
Action = _ = >
{
2022-11-28 11:06:16 +00:00
Main . Context . API . OpenDirectory ( Location ) ;
2019-12-16 10:01:45 +00:00
return true ;
2016-08-22 01:21:22 +00:00
} ,
IcoPath = "Images/folder.png"
}
} ;
2021-09-11 12:30:30 +00:00
if ( CanRunElevated )
{
contextMenus . Add ( new Result
{
Title = api . GetTranslation ( "flowlauncher_plugin_program_run_as_administrator" ) ,
Action = _ = >
{
2022-11-28 17:02:53 +00:00
Task . Run ( ( ) = > Launch ( true ) ) . ConfigureAwait ( false ) ;
2021-09-11 12:30:30 +00:00
return true ;
} ,
IcoPath = "Images/cmd.png"
} ) ;
}
2016-08-22 01:21:22 +00:00
return contextMenus ;
}
2022-11-28 17:02:53 +00:00
private void Launch ( bool elevated = false )
2021-09-11 12:30:30 +00:00
{
2022-11-28 17:02:53 +00:00
string command = "shell:AppsFolder\\" + UserModelId ;
2021-09-11 12:30:30 +00:00
command = Environment . ExpandEnvironmentVariables ( command . Trim ( ) ) ;
var info = new ProcessStartInfo ( command )
{
2022-11-28 11:06:16 +00:00
UseShellExecute = true ,
2022-11-28 17:02:53 +00:00
Verb = elevated ? "runas" : ""
2021-09-11 12:30:30 +00:00
} ;
Main . StartProcess ( Process . Start , info ) ;
}
2022-11-28 14:01:16 +00:00
//public Application(AppxPackageHelper.IAppxManifestApplication manifestApp, UWP package)
//{
// // This is done because we cannot use the keyword 'out' along with a property
2020-06-24 11:34:33 +00:00
2022-11-28 14:01:16 +00:00
// 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);
2016-11-29 01:55:11 +00:00
2022-11-28 14:01:16 +00:00
// UserModelId = tmpUserModelId;
// UniqueIdentifier = tmpUniqueIdentifier;
// DisplayName = tmpDisplayName;
// Description = tmpDescription;
// BackgroundColor = tmpBackgroundColor;
// EntryPoint = tmpEntryPoint;
2019-09-08 12:18:55 +00:00
2022-11-28 14:01:16 +00:00
// 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();
//}
2021-09-11 12:30:30 +00:00
2022-11-28 14:08:21 +00:00
//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;
//}
2016-11-29 01:46:29 +00:00
2022-11-28 10:43:56 +00:00
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" ;
}
2022-11-28 14:08:21 +00:00
//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;
// }
//}
2016-11-29 01:46:29 +00:00
2022-11-28 14:08:21 +00:00
//public static string FormattedPriReferenceValue(string packageName, string rawPriReferenceValue)
//{
// const string prefix = "ms-resource:";
2021-02-01 11:57:08 +00:00
2022-11-28 14:08:21 +00:00
// if (string.IsNullOrWhiteSpace(rawPriReferenceValue) || !rawPriReferenceValue.StartsWith(prefix))
// return rawPriReferenceValue;
2020-04-26 10:30:18 +00:00
2022-11-28 14:08:21 +00:00
// string key = rawPriReferenceValue.Substring(prefix.Length);
// if (key.StartsWith("//"))
// return $"{prefix}{key}";
2021-02-01 11:57:08 +00:00
2022-11-28 14:08:21 +00:00
// if (!key.StartsWith("/"))
// {
// key = $"/{key}";
// }
2020-04-26 10:30:18 +00:00
2022-11-28 14:08:21 +00:00
// if (!key.ToLower().Contains("resources"))
// {
// key = $"/Resources{key}";
// }
2020-04-26 10:30:18 +00:00
2022-11-28 14:08:21 +00:00
// return $"{prefix}//{packageName}{key}";
//}
2016-11-29 01:46:29 +00:00
2022-11-28 17:48:04 +00:00
internal string LogoPathFromUri ( string uri )
2016-11-29 01:46:29 +00:00
{
// 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
2022-11-28 14:01:16 +00:00
if ( string . IsNullOrWhiteSpace ( uri ) )
{
2022-11-28 17:48:04 +00:00
ProgramLogger . LogException ( $"|UWP|LogoPathFromUri|{Location}" +
$"|{UserModelId} 's logo uri is null or empty: {Location}" , new ArgumentException ( "uri" ) ) ;
return string . Empty ;
2022-11-28 14:01:16 +00:00
}
2022-11-28 11:06:16 +00:00
string path = Path . Combine ( Location , uri ) ;
2022-10-19 18:30:58 +00:00
2022-10-20 17:01:07 +00:00
var logoPath = TryToFindLogo ( uri , path ) ;
2022-11-28 17:48:04 +00:00
if ( logoPath = = string . Empty )
2016-11-29 01:46:29 +00:00
{
2022-11-28 11:06:16 +00:00
var tmp = Path . Combine ( Location , "Assets" , uri ) ;
2022-11-28 09:48:27 +00:00
if ( ! path . Equals ( tmp , StringComparison . OrdinalIgnoreCase ) )
{
2022-11-28 11:06:16 +00:00
// TODO: Don't know why, just keep it at the moment
// Maybe on older version of Windows 10?
// for C:\Windows\MiracastView etc
2022-11-28 17:48:04 +00:00
return TryToFindLogo ( uri , tmp ) ;
2022-11-28 09:48:27 +00:00
}
2016-11-29 01:46:29 +00:00
}
2022-11-28 17:48:04 +00:00
return logoPath ;
2016-11-29 01:46:29 +00:00
2022-10-20 17:01:07 +00:00
string TryToFindLogo ( string uri , string path )
2016-11-29 01:46:29 +00:00
{
2022-10-20 17:01:07 +00:00
var extension = Path . GetExtension ( path ) ;
if ( extension ! = null )
2022-01-08 21:50:27 +00:00
{
2022-10-28 08:36:44 +00:00
//if (File.Exists(path))
//{
// return path; // shortcut, avoid enumerating files
//}
2016-11-29 01:46:29 +00:00
2022-10-20 17:01:07 +00:00
var logoNamePrefix = Path . GetFileNameWithoutExtension ( uri ) ; // e.g Square44x44
var logoDir = Path . GetDirectoryName ( path ) ; // e.g ..\..\Assets
2022-11-28 18:06:11 +00:00
if ( String . IsNullOrEmpty ( logoNamePrefix ) | | ! Directory . Exists ( logoDir ) )
2022-01-08 21:50:27 +00:00
{
2022-10-20 17:01:07 +00:00
// Known issue: Edge always triggers it since logo is not at uri
2022-11-28 11:06:16 +00:00
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 ( ) ) ;
2022-10-20 17:01:07 +00:00
return string . Empty ;
}
var files = Directory . EnumerateFiles ( logoDir ) ;
// 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
2022-10-28 08:36:44 +00:00
var logos = files . Where ( file = >
2022-11-04 08:01:14 +00:00
Path . GetFileName ( file ) ? . StartsWith ( logoNamePrefix , StringComparison . OrdinalIgnoreCase ) ? ? false
& & extension . Equals ( Path . GetExtension ( file ) , StringComparison . OrdinalIgnoreCase )
2022-10-20 17:01:07 +00:00
) ;
2022-10-28 08:36:44 +00:00
var selected = logos . FirstOrDefault ( ) ;
2022-10-31 12:03:34 +00:00
var closest = selected ;
int min = int . MaxValue ;
2022-11-28 11:06:16 +00:00
foreach ( var logo in logos )
2022-01-08 21:50:27 +00:00
{
2022-10-28 08:36:44 +00:00
var imageStream = File . OpenRead ( logo ) ;
var decoder = BitmapDecoder . Create ( imageStream , BitmapCreateOptions . IgnoreColorProfile , BitmapCacheOption . None ) ;
var height = decoder . Frames [ 0 ] . PixelHeight ;
var width = decoder . Frames [ 0 ] . PixelWidth ;
2022-10-31 12:03:34 +00:00
int pixelCountDiff = Math . Abs ( height * width - 1936 ) ; // 44*44=1936
2022-11-28 11:06:16 +00:00
if ( pixelCountDiff < min )
2022-01-08 21:50:27 +00:00
{
2022-10-31 12:03:34 +00:00
// try to find the closest to 44x44 logo
closest = logo ;
if ( pixelCountDiff = = 0 )
break ; // found 44x44
min = pixelCountDiff ;
2022-01-08 21:50:27 +00:00
}
}
2018-07-15 03:32:21 +00:00
2022-10-31 12:03:34 +00:00
selected = closest ;
2022-10-20 17:01:07 +00:00
if ( ! string . IsNullOrEmpty ( selected ) )
2018-07-15 03:32:21 +00:00
{
2022-10-20 17:01:07 +00:00
return selected ;
}
else
{
2022-11-28 11:06:16 +00:00
ProgramLogger . LogException ( $"|UWP|LogoPathFromUri|{Location}" +
$"|{UserModelId} can't find logo uri for {uri} in package location (can't find specified logo): {Location}" , new FileNotFoundException ( ) ) ;
2022-10-20 17:01:07 +00:00
return string . Empty ;
2018-07-15 03:32:21 +00:00
}
2016-11-29 01:46:29 +00:00
}
else
{
2022-11-28 11:06:16 +00:00
ProgramLogger . LogException ( $"|UWP|LogoPathFromUri|{Location}" +
2022-10-20 17:01:07 +00:00
$"|Unable to find extension from {uri} for {UserModelId} " +
2022-11-28 11:06:16 +00:00
$"in package location {Location}" , new FileNotFoundException ( ) ) ;
2016-11-29 01:46:29 +00:00
return string . Empty ;
}
}
}
2016-08-18 00:16:40 +00:00
2016-08-18 00:56:26 +00:00
public ImageSource Logo ( )
2016-08-18 00:16:40 +00:00
{
2016-11-29 01:18:38 +00:00
var logo = ImageFromPath ( LogoPath ) ;
2022-10-20 17:01:07 +00:00
var plated = PlatedImage ( logo ) ; // TODO: maybe get plated directly from app package?
2016-08-18 00:56:26 +00:00
2016-08-19 02:00:52 +00:00
// todo magic! temp fix for cross thread object
plated . Freeze ( ) ;
return plated ;
}
2016-11-29 01:18:38 +00:00
private BitmapImage ImageFromPath ( string path )
2016-08-19 02:00:52 +00:00
{
2016-11-29 01:18:38 +00:00
if ( File . Exists ( path ) )
2016-08-19 22:15:40 +00:00
{
2022-10-21 16:28:17 +00:00
var image = new BitmapImage ( ) ;
image . BeginInit ( ) ;
2022-10-28 08:50:08 +00:00
image . UriSource = new Uri ( path ) ;
2022-10-21 16:28:17 +00:00
image . CacheOption = BitmapCacheOption . OnLoad ;
image . EndInit ( ) ;
2022-10-28 08:50:08 +00:00
image . Freeze ( ) ;
2016-11-29 01:18:38 +00:00
return image ;
2016-09-05 19:33:31 +00:00
}
else
{
2021-07-03 12:35:22 +00:00
ProgramLogger . LogException ( $"|UWP|ImageFromPath|{(string.IsNullOrEmpty(path) ? " Not Avaliable " : path)}" +
2022-01-08 21:50:27 +00:00
$"|Unable to get logo for {UserModelId} from {path} and" +
2022-11-28 11:06:16 +00:00
$" located in {Location}" , new FileNotFoundException ( ) ) ;
2020-09-06 20:55:12 +00:00
return new BitmapImage ( new Uri ( Constant . MissingImgIcon ) ) ;
2016-09-05 19:33:31 +00:00
}
}
2016-11-29 01:18:38 +00:00
private ImageSource PlatedImage ( BitmapImage image )
2016-09-05 19:33:31 +00:00
{
2016-11-29 01:18:38 +00:00
if ( ! string . IsNullOrEmpty ( BackgroundColor ) & & BackgroundColor ! = "transparent" )
2016-09-05 19:33:31 +00:00
{
2016-11-29 01:18:38 +00:00
var width = image . Width ;
var height = image . Height ;
var x = 0 ;
var y = 0 ;
var group = new DrawingGroup ( ) ;
2016-09-05 19:33:31 +00:00
2016-11-29 01:18:38 +00:00
var converted = ColorConverter . ConvertFromString ( BackgroundColor ) ;
if ( converted ! = null )
2016-09-05 16:45:29 +00:00
{
2016-11-29 01:46:29 +00:00
var color = ( Color ) converted ;
2016-11-29 01:18:38 +00:00
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 ;
2016-09-05 16:45:29 +00:00
}
else
{
2022-11-28 11:06:16 +00:00
ProgramLogger . LogException ( $"|UWP|PlatedImage|{Location}" +
2022-01-08 21:50:27 +00:00
$"|Unable to convert background string {BackgroundColor} " +
2022-11-28 11:06:16 +00:00
$"to color for {Location}" , new InvalidOperationException ( ) ) ;
2019-10-25 02:11:52 +00:00
2020-09-06 20:55:12 +00:00
return new BitmapImage ( new Uri ( Constant . MissingImgIcon ) ) ;
2016-09-05 16:45:29 +00:00
}
2016-08-19 22:15:40 +00:00
}
else
{
2016-11-29 01:18:38 +00:00
// todo use windows theme as background
2016-09-05 16:45:29 +00:00
return image ;
2016-08-19 22:15:40 +00:00
}
2016-08-19 02:00:52 +00:00
}
2016-08-18 00:16:40 +00:00
public override string ToString ( )
{
return $"{DisplayName}: {Description}" ;
}
2022-11-03 16:47:52 +00:00
public override bool Equals ( object obj )
{
if ( obj is Application other )
{
return UniqueIdentifier = = other . UniqueIdentifier ;
}
else
{
return false ;
}
}
public override int GetHashCode ( )
{
return UniqueIdentifier . GetHashCode ( ) ;
}
2016-08-18 00:16:40 +00:00
}
2016-11-29 01:18:38 +00:00
public enum PackageVersion
{
Windows10 ,
Windows81 ,
Windows8 ,
Unknown
}
2022-11-28 14:08:21 +00:00
//[Flags]
//private enum Stgm : uint
//{
// Read = 0x0,
// ShareExclusive = 0x10,
// ShareDenyNone = 0x40
//}
2016-08-18 00:16:40 +00:00
2022-11-28 14:08:21 +00:00
//private enum Hresult : uint
//{
// Ok = 0x0000,
//}
2016-08-18 00:16:40 +00:00
2022-11-28 14:08:21 +00:00
//[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
//private static extern Hresult SHCreateStreamOnFileEx(string fileName, Stgm grfMode, uint attributes, bool create,
// IStream reserved, out IStream stream);
2016-11-29 01:18:38 +00:00
2022-11-28 14:08:21 +00:00
//[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
//private static extern Hresult SHLoadIndirectString(string pszSource, StringBuilder pszOutBuf, uint cchOutBuf,
// IntPtr ppvReserved);
2022-01-08 21:50:27 +00:00
2016-08-18 00:16:40 +00:00
}
2022-08-09 00:35:38 +00:00
}