diff --git a/AppxPackagesManager/AppxPackagesManager.sln b/AppxPackagesManager/AppxPackagesManager.sln
index e01c51e..3b8ab72 100644
--- a/AppxPackagesManager/AppxPackagesManager.sln
+++ b/AppxPackagesManager/AppxPackagesManager.sln
@@ -1,9 +1,9 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
-VisualStudioVersion = 17.6.33801.468
+VisualStudioVersion = 17.10.35013.160
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppxPackagesManager", "AppxPackagesManager\AppxPackagesManager.csproj", "{A925F229-24FF-4A16-A2C2-110C9D1ECCD1}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppxPackagesManager", "AppxPackagesManager\AppxPackagesManager.csproj", "{6E6CA88B-CBF3-4A36-9D33-9312FC54392E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -11,15 +11,15 @@ Global
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {A925F229-24FF-4A16-A2C2-110C9D1ECCD1}.Debug|x64.ActiveCfg = Debug|x64
- {A925F229-24FF-4A16-A2C2-110C9D1ECCD1}.Debug|x64.Build.0 = Debug|x64
- {A925F229-24FF-4A16-A2C2-110C9D1ECCD1}.Release|x64.ActiveCfg = Release|x64
- {A925F229-24FF-4A16-A2C2-110C9D1ECCD1}.Release|x64.Build.0 = Release|x64
+ {6E6CA88B-CBF3-4A36-9D33-9312FC54392E}.Debug|x64.ActiveCfg = Debug|x64
+ {6E6CA88B-CBF3-4A36-9D33-9312FC54392E}.Debug|x64.Build.0 = Debug|x64
+ {6E6CA88B-CBF3-4A36-9D33-9312FC54392E}.Release|x64.ActiveCfg = Release|x64
+ {6E6CA88B-CBF3-4A36-9D33-9312FC54392E}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {1166BDA6-D8B8-46D7-BE13-5F4371ED3F08}
+ SolutionGuid = {46C4FC05-2279-4554-9512-E10B3ECB498D}
EndGlobalSection
EndGlobal
diff --git a/AppxPackagesManager/AppxPackagesManager/App.config b/AppxPackagesManager/AppxPackagesManager/App.config
index b4a8513..193aecc 100644
--- a/AppxPackagesManager/AppxPackagesManager/App.config
+++ b/AppxPackagesManager/AppxPackagesManager/App.config
@@ -1,18 +1,6 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/AppxPackagesManager/AppxPackagesManager/App.xaml b/AppxPackagesManager/AppxPackagesManager/App.xaml
new file mode 100644
index 0000000..33ee961
--- /dev/null
+++ b/AppxPackagesManager/AppxPackagesManager/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/AppxPackagesManager/AppxPackagesManager/App/App.xaml.cs b/AppxPackagesManager/AppxPackagesManager/App.xaml.cs
similarity index 86%
rename from AppxPackagesManager/AppxPackagesManager/App/App.xaml.cs
rename to AppxPackagesManager/AppxPackagesManager/App.xaml.cs
index 8bf2aaa..4e2e8df 100644
--- a/AppxPackagesManager/AppxPackagesManager/App/App.xaml.cs
+++ b/AppxPackagesManager/AppxPackagesManager/App.xaml.cs
@@ -1,4 +1,4 @@
-using System.Windows;
+using System.Windows;
namespace AppxPackagesManager {
///
diff --git a/AppxPackagesManager/AppxPackagesManager/App/App.xaml b/AppxPackagesManager/AppxPackagesManager/App/App.xaml
deleted file mode 100644
index 55f22b9..0000000
--- a/AppxPackagesManager/AppxPackagesManager/App/App.xaml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
diff --git a/AppxPackagesManager/AppxPackagesManager/AppxPackagesManager.csproj b/AppxPackagesManager/AppxPackagesManager/AppxPackagesManager.csproj
index 4c91f72..e628a4d 100644
--- a/AppxPackagesManager/AppxPackagesManager/AppxPackagesManager.csproj
+++ b/AppxPackagesManager/AppxPackagesManager/AppxPackagesManager.csproj
@@ -1,11 +1,10 @@
-
Debug
AnyCPU
- {A925F229-24FF-4A16-A2C2-110C9D1ECCD1}
+ {6E6CA88B-CBF3-4A36-9D33-9312FC54392E}
WinExe
AppxPackagesManager
AppxPackagesManager
@@ -15,23 +14,6 @@
4
true
true
-
-
- publish\
- true
- Disk
- false
- Foreground
- 7
- Days
- false
- false
- true
- 0
- 1.0.0.%2a
- false
- false
- true
x64
@@ -48,182 +30,43 @@
app.manifest
-
- ..\packages\Costura.Fody.5.7.0\lib\netstandard1.0\Costura.dll
-
-
- ..\packages\Microsoft.Win32.Primitives.4.3.0\lib\net46\Microsoft.Win32.Primitives.dll
- True
- True
-
-
- False
- ..\..\..\..\Windows\Microsoft.NET\assembly\GAC_MSIL\Microsoft.Windows.Appx.PackageManager.Commands\v4.0_10.0.0.0__31bf3856ad364e35\Microsoft.Windows.Appx.PackageManager.Commands.dll
-
-
- ..\packages\System.AppContext.4.3.0\lib\net463\System.AppContext.dll
- True
- True
-
-
-
- ..\packages\System.Console.4.3.0\lib\net46\System.Console.dll
- True
- True
-
-
- ..\packages\System.Diagnostics.DiagnosticSource.4.3.0\lib\net46\System.Diagnostics.DiagnosticSource.dll
-
-
- ..\packages\System.Diagnostics.Tracing.4.3.0\lib\net462\System.Diagnostics.Tracing.dll
- True
- True
-
-
- ..\packages\System.Globalization.Calendars.4.3.0\lib\net46\System.Globalization.Calendars.dll
- True
- True
-
-
- ..\packages\System.IO.4.3.0\lib\net462\System.IO.dll
- True
- True
-
-
- ..\packages\System.IO.Compression.4.3.0\lib\net46\System.IO.Compression.dll
- True
- True
-
-
-
- ..\packages\System.IO.Compression.ZipFile.4.3.0\lib\net46\System.IO.Compression.ZipFile.dll
- True
- True
-
-
- ..\packages\System.IO.FileSystem.4.3.0\lib\net46\System.IO.FileSystem.dll
- True
- True
-
-
- ..\packages\System.IO.FileSystem.Primitives.4.3.0\lib\net46\System.IO.FileSystem.Primitives.dll
- True
- True
-
-
- ..\packages\System.Linq.4.3.0\lib\net463\System.Linq.dll
- True
- True
-
-
- ..\packages\System.Linq.Expressions.4.3.0\lib\net463\System.Linq.Expressions.dll
- True
- True
-
-
- ..\packages\Microsoft.PowerShell.5.1.ReferenceAssemblies.1.0.0\lib\net461\System.Management.Automation.dll
-
-
- ..\packages\System.Net.Http.4.3.0\lib\net46\System.Net.Http.dll
- True
- True
-
-
- ..\packages\System.Net.Sockets.4.3.0\lib\net46\System.Net.Sockets.dll
- True
- True
-
-
-
- ..\packages\System.Reflection.4.3.0\lib\net462\System.Reflection.dll
- True
- True
-
-
- ..\packages\System.Runtime.4.3.0\lib\net462\System.Runtime.dll
- True
- True
-
-
- ..\packages\System.Runtime.Extensions.4.3.0\lib\net462\System.Runtime.Extensions.dll
- True
- True
-
-
- ..\packages\System.Runtime.InteropServices.4.3.0\lib\net463\System.Runtime.InteropServices.dll
- True
- True
-
-
- ..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll
- True
- True
-
-
-
- ..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net463\System.Security.Cryptography.Algorithms.dll
- True
- True
-
-
- ..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll
- True
- True
-
-
- ..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll
- True
- True
-
-
- ..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net461\System.Security.Cryptography.X509Certificates.dll
- True
- True
-
-
- ..\packages/System.Text.RegularExpressions.4.3.1/lib/net463/System.Text.RegularExpressions.dll
- True
-
-
+
4.0
-
- ..\packages\System.Xml.ReaderWriter.4.3.0\lib\net46\System.Xml.ReaderWriter.dll
- True
- True
-
-
- False
- .\Windows.winmd
- False
-
-
+
MSBuild:Compile
Designer
-
+
+
+
+
+
+
+
+
+
MSBuild:Compile
Designer
-
+
App.xaml
Code
-
-
+
MainWindow.xaml
Code
@@ -247,7 +90,6 @@
Resources.Designer.cs
-
SettingsSingleFileGenerator
Settings.Designer.cs
@@ -257,26 +99,12 @@
-
- False
- Microsoft .NET Framework 4.8 %28x86 and x64%29
- true
-
-
- False
- .NET Framework 3.5 SP1
- false
-
+
+
+
+
+ 10.0.26100.1
+
-
-
- This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/AppxPackagesManager/AppxPackagesManager/FodyWeavers.xml b/AppxPackagesManager/AppxPackagesManager/FodyWeavers.xml
deleted file mode 100644
index 5029e70..0000000
--- a/AppxPackagesManager/AppxPackagesManager/FodyWeavers.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/AppxPackagesManager/AppxPackagesManager/GridItem.cs b/AppxPackagesManager/AppxPackagesManager/GridItem.cs
deleted file mode 100644
index 916e470..0000000
--- a/AppxPackagesManager/AppxPackagesManager/GridItem.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using System.ComponentModel;
-
-namespace AppxPackagesManager {
- public class GridItem : INotifyPropertyChanged {
- private bool _uninstall;
-
- public bool Uninstall {
- get => _uninstall;
- set {
- if (_uninstall != value) {
- _uninstall = value;
- OnPropertyChanged(nameof(Uninstall));
- }
- }
- }
-
- public event PropertyChangedEventHandler PropertyChanged;
-
- private void OnPropertyChanged(string propertyName) {
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
- }
-
- public bool CanUninstall { get; set; }
-
- public string PackageName { get; set; }
- public string PackageFullName { get; set; }
- public string RequiredFor { get; set; }
- public string NonRemovable { get; set; }
- public string Framework { get; set; }
- }
-}
diff --git a/AppxPackagesManager/AppxPackagesManager/MainWindow.xaml b/AppxPackagesManager/AppxPackagesManager/MainWindow.xaml
new file mode 100644
index 0000000..46a61a7
--- /dev/null
+++ b/AppxPackagesManager/AppxPackagesManager/MainWindow.xaml
@@ -0,0 +1,119 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/AppxPackagesManager/AppxPackagesManager/MainWindow.xaml.cs b/AppxPackagesManager/AppxPackagesManager/MainWindow.xaml.cs
new file mode 100644
index 0000000..aa58ade
--- /dev/null
+++ b/AppxPackagesManager/AppxPackagesManager/MainWindow.xaml.cs
@@ -0,0 +1,11 @@
+using AppxPackagesManager.ViewModels;
+using System.Windows;
+
+namespace AppxPackagesManager {
+ public partial class MainWindow : Window {
+ public MainWindow() {
+ DataContext = new MainWindowViewModel();
+ InitializeComponent();
+ }
+ }
+}
diff --git a/AppxPackagesManager/AppxPackagesManager/MainWindow/MainWindow.xaml b/AppxPackagesManager/AppxPackagesManager/MainWindow/MainWindow.xaml
deleted file mode 100644
index 0529a8f..0000000
--- a/AppxPackagesManager/AppxPackagesManager/MainWindow/MainWindow.xaml
+++ /dev/null
@@ -1,134 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/AppxPackagesManager/AppxPackagesManager/MainWindow/MainWindow.xaml.cs b/AppxPackagesManager/AppxPackagesManager/MainWindow/MainWindow.xaml.cs
deleted file mode 100644
index 0ed6f0d..0000000
--- a/AppxPackagesManager/AppxPackagesManager/MainWindow/MainWindow.xaml.cs
+++ /dev/null
@@ -1,219 +0,0 @@
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Linq;
-using System.Management.Automation;
-using System.Reflection;
-using System.Windows;
-using System.Windows.Controls;
-using System.Xml.Linq;
-
-using Microsoft.Windows.Appx.PackageManager.Commands;
-
-using Windows.Management.Deployment;
-
-namespace AppxPackagesManager {
- public partial class MainWindow : Window {
- private readonly Dictionary> _appxPackages = new Dictionary>();
- private readonly ObservableCollection _packagesGridItems = new ObservableCollection();
- private readonly PackageManager _packageManager = new PackageManager();
-
- public MainWindow() {
- InitializeComponent();
-
- var version = Assembly.GetExecutingAssembly().GetName().Version;
- Title = $"AppxPackagesManager v{version.Major}.{version.Minor}.{version.Build}";
-
- GetPackageData();
- GetAppxPackages();
- }
-
- private void GetPackageData() {
- _appxPackages.Clear();
-
- using (var ps = PowerShell.Create()) {
- _ = ps.AddCommand("Get-AppxPackage");
- var results = ps.Invoke();
-
- foreach (var result in results) {
- var packageFullName = result.Properties["PackageFullName"].Value.ToString();
-
- foreach (var dependency in (AppxPackage[])result.Properties["Dependencies"].Value) {
- var dependencyName = dependency.PackageFullName;
-
- // dependency data might not already exist so we need to add it now
- if (_appxPackages.ContainsKey(dependencyName)) {
- (_appxPackages[dependencyName]["required_for"] as List).Add(packageFullName);
- } else {
- _appxPackages[dependencyName] = new Dictionary {
- { "required_for", new List { packageFullName } }
- };
- }
- }
-
- var friendlyName = result.Properties["Name"].Value.ToString();
- var isFramework = result.Properties["IsFramework"].Value;
-
- object isNonRemovable = null;
- try {
- isNonRemovable = result.Properties["NonRemovable"].Value;
- } catch {
- // ignore
- }
-
- try {
- var doc = XDocument.Load($"{result.Properties["InstallLocation"].Value}\\AppxManifest.xml");
- XNamespace ns = "http://schemas.microsoft.com/appx/manifest/foundation/windows10";
-
- var displayName = doc.Element(ns + "Package").Element(ns + "Properties").Element(ns + "DisplayName").Value;
-
- if (!displayName.StartsWith("ms-resource")) {
- friendlyName = displayName;
- }
- } catch {
- // ignore
- }
-
- // check if package exists in case it was added as a dependency already
- if (_appxPackages.ContainsKey(packageFullName)) {
- _appxPackages[packageFullName]["name"] = friendlyName;
- _appxPackages[packageFullName]["is_framework"] = isFramework;
- _appxPackages[packageFullName]["is_non_removable"] = isNonRemovable;
- } else {
- _appxPackages[packageFullName] = new Dictionary {
- { "name", friendlyName},
- { "required_for", new List() },
- { "is_framework", isFramework },
- { "is_non_removable", isNonRemovable },
- };
- }
- }
-
- if (ps.HadErrors) {
- //foreach (var error in ps.Streams.Error) {
- // Console.WriteLine(error.Exception.Message);
- //}
- }
- }
-
- // handle custom dependencies
- var packages = new Dictionary> {
- // example placeholder
- //{ "Microsoft.Todos_1.48.21892.0_x64__8wekyb3d8bbwe" , new List { "Microsoft.WindowsFileRecovery_0.1.20151.0_x64__8wekyb3d8bbwe" } }
- };
-
- foreach (var packageFullName in packages.Keys) {
- foreach (var dependencyName in packages[packageFullName]) {
- if (_appxPackages.ContainsKey(packageFullName) && _appxPackages.ContainsKey(dependencyName)) {
- (_appxPackages[dependencyName]["required_for"] as List).Add(packageFullName);
- }
- }
- }
- }
-
- private void GetAppxPackages() {
- // keep a note of what was checked before clearing
- var prevPackagesGridItemsChecked = new Dictionary();
-
- foreach (var item in _packagesGridItems) {
- prevPackagesGridItemsChecked.Add(item.PackageFullName, item.Uninstall);
- }
-
- // clear existing items
- _packagesGridItems.Clear();
-
- foreach (var packageFullName in _appxPackages.Keys) {
- var package = _appxPackages[packageFullName];
-
- var isFramework = GetValue(package, "is_framework", false);
- var isNonRemovable = GetValue(package, "is_non_removable", false);
-
-
- if ((hideFrameworkPackages.IsChecked is true && isFramework is true) || (hideNonRemovablePackages.IsChecked is true && isNonRemovable is true)) {
- continue;
- }
-
- var requiredFor = (List)GetValue(package, "required_for", new List());
-
- var packagesGridItem = new GridItem {
- Uninstall = (bool)GetValue(prevPackagesGridItemsChecked, packageFullName, false),
- CanUninstall = requiredFor.Count == 0,
- PackageName = GetValue(package, "name", "Unknown").ToString(),
- PackageFullName = packageFullName,
- RequiredFor = string.Join("\n", requiredFor),
- NonRemovable = isNonRemovable is null ? "Unknown" : (isNonRemovable is true).ToString(),
- Framework = GetValue(package, "is_framework", "Unknown").ToString(),
- };
-
- _packagesGridItems.Add(packagesGridItem);
- }
-
- // update the source for datagrid
- packagesDataGrid.ItemsSource = _packagesGridItems;
- }
-
- private void CheckAllPackages(bool isChecked) {
- foreach (GridItem package in packagesDataGrid.Items) {
- if (package.CanUninstall) {
- package.Uninstall = isChecked;
- }
- }
- }
-
- private object GetValue(Dictionary dict, string key, object defaultValue) {
- return dict.ContainsKey(key) ? dict[key] : defaultValue;
- }
-
- private void HideFrameworkPackagesClick(object sender, RoutedEventArgs e) {
- GetAppxPackages();
- }
-
- private void RefreshList() {
- GetPackageData();
- GetAppxPackages();
- }
-
- private void RefreshListClick(object sender, RoutedEventArgs e) {
- RefreshList();
- }
-
- private void SelectAllClick(object sender, RoutedEventArgs e) {
- CheckAllPackages(true);
- }
-
- private void ClearSelectionClick(object sender, RoutedEventArgs e) {
- CheckAllPackages(false);
- }
-
- private void HideNonRemovablePackagesClick(object sender, RoutedEventArgs e) {
- GetAppxPackages();
- }
-
- private void FilterDataGrid(string query) {
- var filteredPackages = _packagesGridItems.Where(p => p.PackageName.ToLower().Contains(query.ToLower())).ToList();
- packagesDataGrid.ItemsSource = filteredPackages;
- }
-
- private void SearchBoxTextChanged(object sender, TextChangedEventArgs e) {
- FilterDataGrid(searchBox.Text);
- }
-
- private void UninstallPackagesClick(object sender, RoutedEventArgs e) {
- var totalPackages = 0;
-
- // uninstall selected packages
- foreach (GridItem package in packagesDataGrid.Items) {
- if (package.CanUninstall && package.Uninstall) {
- var deploymentResult = _packageManager.RemovePackageAsync(package.PackageFullName).GetResults();
-
- totalPackages++;
- }
- }
-
- _ = MessageBox.Show(totalPackages == 0
- ? "No packages selected"
- : "Attempted to remove selected packages.\nPress OK to refresh list", "AppxPackagesManager", MessageBoxButton.OK, MessageBoxImage.Information);
-
- RefreshList();
- }
- }
-}
diff --git a/AppxPackagesManager/AppxPackagesManager/Models/PackageType.cs b/AppxPackagesManager/AppxPackagesManager/Models/PackageType.cs
new file mode 100644
index 0000000..4c6891e
--- /dev/null
+++ b/AppxPackagesManager/AppxPackagesManager/Models/PackageType.cs
@@ -0,0 +1,8 @@
+namespace AppxPackagesManager.Models {
+ internal class PackageType {
+ public static string AllPackages = "All Packages";
+ public static string Packages = "Packages";
+ public static string NonRemovablePackages = "Non-Removable";
+ public static string FrameworkPackages = "Framework";
+ }
+}
diff --git a/AppxPackagesManager/AppxPackagesManager/Models/PackagesGridItemModel.cs b/AppxPackagesManager/AppxPackagesManager/Models/PackagesGridItemModel.cs
new file mode 100644
index 0000000..def33f7
--- /dev/null
+++ b/AppxPackagesManager/AppxPackagesManager/Models/PackagesGridItemModel.cs
@@ -0,0 +1,24 @@
+using AppxPackagesManager.ViewModels;
+
+namespace AppxPackagesManager.Models {
+ internal class PackagesGridItemModel : ViewModelBase {
+ public bool CanUninstall { get; set; }
+ public string CheckBoxToolTip { get; set; }
+
+ private bool isUninstall;
+ public bool IsUninstall {
+ get => isUninstall;
+ set {
+ isUninstall = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public string FriendlyName { get; set; }
+ public string PackageName { get; set; }
+ public string RequiredByPackages { get; set; }
+ public string Version { get; set; }
+ public string IsNonRemovable { get; set; }
+ public string IsFramework { get; set; }
+ }
+}
diff --git a/AppxPackagesManager/AppxPackagesManager/NativeMethods.cs b/AppxPackagesManager/AppxPackagesManager/NativeMethods.cs
new file mode 100644
index 0000000..80f0e1f
--- /dev/null
+++ b/AppxPackagesManager/AppxPackagesManager/NativeMethods.cs
@@ -0,0 +1,8 @@
+using System.Runtime.InteropServices;
+
+namespace AppxPackagesManager {
+ internal class NativeMethods {
+ [DllImport("AppxAllUserStore.dll", CharSet = CharSet.Unicode)]
+ public static extern int IsPackageFamilyInUninstallBlocklist(string packageFamilyName, ref bool isPresent);
+ }
+}
diff --git a/AppxPackagesManager/AppxPackagesManager/PackageInfo.cs b/AppxPackagesManager/AppxPackagesManager/PackageInfo.cs
new file mode 100644
index 0000000..b923de1
--- /dev/null
+++ b/AppxPackagesManager/AppxPackagesManager/PackageInfo.cs
@@ -0,0 +1,11 @@
+using System.Collections.Generic;
+
+namespace AppxPackagesManager {
+ internal class PackageInfo {
+ public string FriendlyName { get; set; }
+ public HashSet RequiredByPackages { get; set; }
+ public string Version { get; set; }
+ public bool IsNonRemovable { get; set; }
+ public bool IsFramework { get; set; }
+ }
+}
diff --git a/AppxPackagesManager/AppxPackagesManager/Properties/AssemblyInfo.cs b/AppxPackagesManager/AppxPackagesManager/Properties/AssemblyInfo.cs
index a113215..915eee7 100644
--- a/AppxPackagesManager/AppxPackagesManager/Properties/AssemblyInfo.cs
+++ b/AppxPackagesManager/AppxPackagesManager/Properties/AssemblyInfo.cs
@@ -1,4 +1,4 @@
-using System.Reflection;
+using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows;
@@ -10,7 +10,7 @@
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("AppxPackagesManager")]
-[assembly: AssemblyCopyright("Copyright © 2023")]
+[assembly: AssemblyCopyright("Copyright © 2024")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@@ -49,5 +49,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
+[assembly: AssemblyVersion("1.1.0.0")]
+[assembly: AssemblyFileVersion("1.1.0.0")]
diff --git a/AppxPackagesManager/AppxPackagesManager/Utils.cs b/AppxPackagesManager/AppxPackagesManager/Utils.cs
new file mode 100644
index 0000000..e3550c9
--- /dev/null
+++ b/AppxPackagesManager/AppxPackagesManager/Utils.cs
@@ -0,0 +1,129 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading;
+using System.Windows;
+using System.Xml.Linq;
+using Windows.ApplicationModel;
+using Windows.Foundation;
+using Windows.Management.Deployment;
+
+namespace AppxPackagesManager {
+ internal class Utils {
+ private static readonly PackageManager packageManager = new PackageManager();
+ private static readonly XNamespace xdocNamespace = "http://schemas.microsoft.com/appx/manifest/foundation/windows10";
+
+ private static bool IsPackageFamilyInUninstallBlocklist(string packageFamilyName) {
+ var isPresent = false;
+
+ if (NativeMethods.IsPackageFamilyInUninstallBlocklist(packageFamilyName, ref isPresent) < 0) {
+ _ = MessageBox.Show($"Failed to determine whether package family {packageFamilyName} is in uninstall blocklist", "AppxPackagesManager", MessageBoxButton.OK, MessageBoxImage.Error);
+ Environment.Exit(1);
+ }
+
+ return isPresent;
+ }
+
+ private static string GetManifestFriendlyName(string manifestLocation) {
+ var xdoc = XDocument.Load(manifestLocation);
+
+ var displayName = xdoc.Element(xdocNamespace + "Package").Element(xdocNamespace + "Properties").Element(xdocNamespace + "DisplayName").Value;
+
+ return displayName;
+ }
+
+ private static void AddPackageToDatabase(Dictionary database, string packageFullName, Package package) {
+ var friendlyName = package.Id.Name;
+
+ // attempt to get the friendly name from the manifest
+ var manifestLocation = $"{package.InstalledLocation.Path}\\AppxManifest.xml";
+
+ if (File.Exists(manifestLocation)) {
+ try {
+ var manifestDisplayName = GetManifestFriendlyName(manifestLocation);
+
+ // ignore invalid display names
+ if (!manifestDisplayName.StartsWith("ms-resource")) {
+ friendlyName = manifestDisplayName;
+ }
+ } catch (NullReferenceException) {
+ // ignore
+ }
+ }
+
+ // add package to database
+ var packageVersion = package.Id.Version;
+
+ database.Add(packageFullName, new PackageInfo {
+ FriendlyName = friendlyName,
+ RequiredByPackages = new HashSet(),
+ Version = $"{packageVersion.Major}.{packageVersion.Minor}.{packageVersion.Build}",
+ IsNonRemovable = package.SignatureKind == PackageSignatureKind.System || IsPackageFamilyInUninstallBlocklist(package.Id.FamilyName),
+ IsFramework = package.IsFramework,
+ });
+ }
+
+ public static Dictionary GetPackagesDatabase() {
+ // holds information for all packages
+ var packagesDatabase = new Dictionary();
+
+ var packages = packageManager.FindPackages();
+
+ foreach (var package in packages) {
+ var packageFullName = package.Id.FullName;
+
+ // package may have been added already due to creating dependency entries
+ if (!packagesDatabase.ContainsKey(packageFullName)) {
+ AddPackageToDatabase(packagesDatabase, packageFullName, package);
+ }
+
+ foreach (var dependency in package.Dependencies) {
+ var dependencyFullName = dependency.Id.FullName;
+
+ // package may have been added already when creating main package entries
+ if (!packagesDatabase.ContainsKey(dependencyFullName)) {
+ AddPackageToDatabase(packagesDatabase, dependencyFullName, dependency);
+ }
+
+ // record that the dependency is needed for another main package
+ _ = packagesDatabase[dependencyFullName].RequiredByPackages.Add(packageFullName);
+ }
+ }
+
+ return packagesDatabase;
+ }
+
+ public static int UninstallPackage(string fullPackageName) {
+ // from Microsoft's example
+
+ var deploymentOperation = packageManager.RemovePackageAsync(fullPackageName);
+
+ // this event is signaled when the operation completes
+ var opCompletedEvent = new ManualResetEvent(false);
+
+ // define the delegate using a statement lambda
+ deploymentOperation.Completed = (depProgress, status) => { _ = opCompletedEvent.Set(); };
+
+ // wait until the operation completes
+ _ = opCompletedEvent.WaitOne();
+
+ if (deploymentOperation.Status == AsyncStatus.Error) {
+ var deploymentResult = deploymentOperation.GetResults();
+ Console.Error.WriteLine($"{fullPackageName} - {deploymentResult.ErrorText}");
+
+ return 1;
+ }
+
+ if (deploymentOperation.Status == AsyncStatus.Canceled) {
+ Console.Error.WriteLine($"{fullPackageName} - removal canceled");
+ return 1;
+ }
+
+ if (deploymentOperation.Status == AsyncStatus.Completed) {
+ return 0;
+ }
+
+ return 1;
+ }
+ }
+}
diff --git a/AppxPackagesManager/AppxPackagesManager/ViewModels/MainWindowViewModel.cs b/AppxPackagesManager/AppxPackagesManager/ViewModels/MainWindowViewModel.cs
new file mode 100644
index 0000000..09efbf4
--- /dev/null
+++ b/AppxPackagesManager/AppxPackagesManager/ViewModels/MainWindowViewModel.cs
@@ -0,0 +1,195 @@
+using AppxPackagesManager.Models;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Reflection;
+using System.Threading.Tasks;
+using System.Windows;
+
+
+namespace AppxPackagesManager.ViewModels {
+ internal class MainWindowViewModel : ViewModelBase {
+ private ObservableCollection InternalPackagesGridItems { get; set; }
+
+ public RelayCommand RefreshCommand => new RelayCommand(execute => PopulateInternalPackages());
+ public RelayCommand UninstallSelectedCommand => new RelayCommand(execute => { UninstallSelectedPackages(); }, canExecute => PackagesGridItems.Count(package => package.IsUninstall) > 0);
+ public RelayCommand SelectAllCommand => new RelayCommand(execute => { SelectAllPackages(true); }, canExecute => PackagesGridItems.Count(package => package.IsUninstall) != PackagesGridItems.Count(package => package.CanUninstall));
+ public RelayCommand SelectionClearCommand => new RelayCommand(execute => { SelectAllPackages(false); }, canExecute => PackagesGridItems.Count(package => package.IsUninstall) > 0);
+
+ public string Title { get; set; }
+
+ private string packagesCount;
+ public string PackagesCount {
+ get => packagesCount;
+ set {
+ packagesCount = value;
+ OnPropertyChanged();
+ }
+ }
+
+ private ObservableCollection packagesGridItems;
+ public ObservableCollection PackagesGridItems {
+ get => packagesGridItems;
+ set {
+ packagesGridItems = value;
+ OnPropertyChanged();
+ }
+ }
+
+ private string searchQuery;
+ public string SearchQuery {
+ get => searchQuery;
+ set {
+ searchQuery = value;
+ RefreshGridView();
+ }
+ }
+
+ public ObservableCollection PackageTypes { get; set; }
+
+ private string selectedPackagesType;
+ public string SelectedPackagesType {
+ get => selectedPackagesType;
+ set {
+ selectedPackagesType = value;
+ RefreshGridView();
+ }
+ }
+
+ private bool isWindowEnabled = true;
+ public bool IsWindowEnabled {
+ get => isWindowEnabled;
+ set {
+ isWindowEnabled = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public MainWindowViewModel() {
+ // set window title
+ var version = Assembly.GetExecutingAssembly().GetName().Version;
+ Title = $"AppxPackagesManager v{version.Major}.{version.Minor}.{version.Build}";
+
+ InitSelectedPackages();
+
+ PopulateInternalPackages();
+ }
+
+ private void InitSelectedPackages() {
+ PackageTypes = new ObservableCollection {
+ PackageType.AllPackages,
+ PackageType.Packages,
+ PackageType.NonRemovablePackages,
+ PackageType.FrameworkPackages
+ };
+
+ SelectedPackagesType = PackageTypes.FirstOrDefault(packageType => packageType == PackageType.Packages);
+ }
+
+ private void RefreshGridView() {
+ // gets called in SearchQuery setter but InternalPackagesGridItems is null initially
+ if (InternalPackagesGridItems == null) {
+ return;
+ }
+
+ PackagesGridItems = new ObservableCollection(InternalPackagesGridItems);
+
+ // handle search query
+ if (SearchQuery != null && searchQuery != "") {
+ var serachQuery = SearchQuery.ToLower();
+
+ PackagesGridItems = new ObservableCollection(
+ PackagesGridItems.Where(
+ package => package.PackageName.ToLower().Contains(searchQuery)
+ || package.FriendlyName.ToLower().Contains(serachQuery)
+ )
+ );
+ }
+
+ if (SelectedPackagesType == PackageType.Packages) {
+ PackagesGridItems = new ObservableCollection(
+ PackagesGridItems.Where(package => !bool.Parse(package.IsNonRemovable) && !bool.Parse(package.IsFramework))
+ );
+ } else if (SelectedPackagesType == PackageType.NonRemovablePackages) {
+ PackagesGridItems = new ObservableCollection(
+ PackagesGridItems.Where(package => bool.Parse(package.IsNonRemovable))
+ );
+ } else if (SelectedPackagesType == PackageType.FrameworkPackages) {
+ PackagesGridItems = new ObservableCollection(
+ PackagesGridItems.Where(package => bool.Parse(package.IsFramework))
+ );
+ }
+
+ PackagesCount = $"Packages: {PackagesGridItems.Count}/{InternalPackagesGridItems.Count}";
+ }
+
+ private void PopulateInternalPackages() {
+ // clear items for refresh
+ InternalPackagesGridItems = new ObservableCollection();
+
+ var packages = Utils.GetPackagesDatabase();
+
+ foreach (var package in packages) {
+ var hasDependencies = package.Value.RequiredByPackages.Count != 0;
+
+ InternalPackagesGridItems.Add(new PackagesGridItemModel {
+ CanUninstall = !hasDependencies && !package.Value.IsNonRemovable,
+ CheckBoxToolTip = hasDependencies || package.Value.IsNonRemovable ? "This package can't be uninstalled because it is required by other packages or because it is marked as non-removable" : "",
+ IsUninstall = false,
+ FriendlyName = package.Value.FriendlyName,
+ PackageName = package.Key,
+ RequiredByPackages = string.Join("\n", package.Value.RequiredByPackages),
+ Version = package.Value.Version,
+ IsNonRemovable = package.Value.IsNonRemovable.ToString(),
+ IsFramework = package.Value.IsFramework.ToString(),
+ });
+ }
+
+ RefreshGridView();
+ }
+
+ private void SelectAllPackages(bool isSelectAll) {
+ foreach (var package in PackagesGridItems) {
+ package.IsUninstall = isSelectAll && package.CanUninstall;
+ }
+ }
+
+ private void UninstallSelectedPackages() {
+ if (MessageBox.Show($"Are you sure you want to remove {PackagesGridItems.Count(package => package.IsUninstall)} package(s)?", "AppxPackagesManager", MessageBoxButton.YesNo) != MessageBoxResult.Yes) {
+ return;
+ }
+
+ var removalSucceeds = 0;
+ var failedPackages = new List();
+
+ // disallow user interaction while removing packages
+ IsWindowEnabled = false;
+
+ foreach (var package in PackagesGridItems) {
+ // only the uninstallable packages should be checked but we can check if it can be uninstalled again
+ if (package.IsUninstall && package.CanUninstall) {
+ if (Utils.UninstallPackage(package.PackageName) != 0) {
+ failedPackages.Add(package.PackageName);
+ } else {
+ removalSucceeds++;
+ }
+ }
+ }
+
+ var msg = $"Removed {removalSucceeds} package(s), failed to remove {failedPackages.Count} package(s):";
+
+ if (failedPackages.Count != 0) {
+ msg += $"\n\n{string.Join("\n", failedPackages)}";
+ }
+
+ _ = Task.Run(() => {
+ _ = MessageBox.Show(msg, "AppxPackagesManager", MessageBoxButton.OK, failedPackages.Count == 0 ? MessageBoxImage.Information : MessageBoxImage.Asterisk);
+ });
+
+ PopulateInternalPackages();
+
+ // reallow user interaction
+ IsWindowEnabled = true;
+ }
+ }
+}
diff --git a/AppxPackagesManager/AppxPackagesManager/ViewModels/RelayCommand.cs b/AppxPackagesManager/AppxPackagesManager/ViewModels/RelayCommand.cs
new file mode 100644
index 0000000..5f7e946
--- /dev/null
+++ b/AppxPackagesManager/AppxPackagesManager/ViewModels/RelayCommand.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Windows.Input;
+
+namespace AppxPackagesManager.ViewModels {
+ internal class RelayCommand : ICommand {
+ private readonly Action