diff --git a/MSIXplainer/App.xaml.cs b/MSIXplainer/App.xaml.cs
index 1550b83..3bc68a9 100644
--- a/MSIXplainer/App.xaml.cs
+++ b/MSIXplainer/App.xaml.cs
@@ -4,6 +4,7 @@
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
+using System.Linq;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
@@ -59,12 +60,29 @@ private void OnUnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExce
e.Handled = true;
}
+ ///
+ /// File path provided via file activation (right-click → Open with
+ /// MSIXplainer). MainPage reads this on construction and loads the
+ /// package. Null when the app was launched normally.
+ ///
+ public static string? PendingFileActivationPath { get; private set; }
+
+ ///
+ /// Clears the pending file-activation path. Call after the path has been
+ /// loaded so it isn't re-applied on subsequent page reloads.
+ ///
+ public static void ConsumePendingFileActivationPath() => PendingFileActivationPath = null;
+
///
/// Invoked when the application is launched.
///
/// Details about the launch request and process.
protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
{
+ // Capture file-activation arg BEFORE MainWindow is constructed so
+ // MainPage can pick it up during its own construction. See #20.
+ PendingFileActivationPath = TryGetFileActivationPath();
+
Window = new MainWindow();
DispatcherQueue = Microsoft.UI.Dispatching.DispatcherQueue.GetForCurrentThread();
Window.Activate();
@@ -72,4 +90,41 @@ protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs ar
// root exists. ThemeService.Apply no-ops if Content is still null.
MSIXplainer.Services.ThemeService.Apply(MSIXplainer.Services.ThemeService.LoadPreference());
}
+
+ ///
+ /// If the app was launched via a file activation, return the first file's
+ /// path; otherwise return null. Safe to call early in OnLaunched.
+ ///
+ private static string? TryGetFileActivationPath()
+ {
+ try
+ {
+ var activated = Microsoft.Windows.AppLifecycle.AppInstance
+ .GetCurrent()
+ .GetActivatedEventArgs();
+
+ System.Diagnostics.Debug.WriteLine(
+ $"[MSIXplainer] Activation kind: {activated.Kind}");
+
+ if (activated.Kind != Microsoft.Windows.AppLifecycle.ExtendedActivationKind.File)
+ return null;
+
+ if (activated.Data is not Windows.ApplicationModel.Activation.IFileActivatedEventArgs fileArgs)
+ {
+ System.Diagnostics.Debug.WriteLine(
+ $"[MSIXplainer] Activation data is {activated.Data?.GetType().FullName ?? "null"}, not IFileActivatedEventArgs");
+ return null;
+ }
+
+ var firstFile = fileArgs.Files.FirstOrDefault();
+ System.Diagnostics.Debug.WriteLine(
+ $"[MSIXplainer] File activation path: {firstFile?.Path ?? ""}");
+ return firstFile?.Path;
+ }
+ catch (System.Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine($"[MSIXplainer] File activation failed: {ex.Message}");
+ return null;
+ }
+ }
}
diff --git a/MSIXplainer/MainPage.xaml.cs b/MSIXplainer/MainPage.xaml.cs
index 879ae5e..b61f4e4 100644
--- a/MSIXplainer/MainPage.xaml.cs
+++ b/MSIXplainer/MainPage.xaml.cs
@@ -30,6 +30,21 @@ public MainPage()
ViewModel.InstalledPackages.CollectionChanged += InstalledPackages_CollectionChanged;
ViewModel.PropertyChanged += ViewModel_PropertyChanged;
BuildStaticNavItems();
+ Loaded += MainPage_Loaded;
+ }
+
+ private void MainPage_Loaded(object sender, RoutedEventArgs e)
+ {
+ // File activation: if the app was launched by right-clicking a .msix /
+ // .msixbundle in Explorer (see App.PendingFileActivationPath / issue #20),
+ // load it now that the page is fully wired up. Consume the path so a
+ // page reload doesn't reopen it.
+ var path = App.PendingFileActivationPath;
+ if (!string.IsNullOrEmpty(path))
+ {
+ App.ConsumePendingFileActivationPath();
+ ViewModel.LoadPackageFromPath(path);
+ }
}
private void BuildStaticNavItems()
@@ -212,10 +227,9 @@ private void OnCloseFindingClick(object sender, RoutedEventArgs e)
}
///
- /// Copies a manifest property value to the clipboard. Wraps the clipboard
- /// call in try/catch so a transient clipboard failure never bubbles up as
- /// an unhandled exception. Bug fix for #10 — the built-in TextBlock
- /// context-menu Copy was crashing the app on some Windows builds.
+ /// Copies a manifest property value to the clipboard. The 3-line WinUI
+ /// pattern works as long as the process's Main is marked [STAThread] —
+ /// see Program.cs and the comment there about issue #21.
///
private void CopyPropertyValue_Click(object sender, RoutedEventArgs e)
{
@@ -224,13 +238,14 @@ private void CopyPropertyValue_Click(object sender, RoutedEventArgs e)
try
{
- var package = new Windows.ApplicationModel.DataTransfer.DataPackage();
- package.SetText(value);
- Windows.ApplicationModel.DataTransfer.Clipboard.SetContent(package);
+ var pkg = new Windows.ApplicationModel.DataTransfer.DataPackage();
+ pkg.SetText(value);
+ Windows.ApplicationModel.DataTransfer.Clipboard.SetContent(pkg);
}
catch (System.Exception ex)
{
- System.Diagnostics.Debug.WriteLine($"[MSIXplainer] Clipboard copy failed: {ex.Message}");
+ System.Diagnostics.Debug.WriteLine(
+ $"[MSIXplainer] Clipboard copy failed: {ex.GetType().Name} 0x{ex.HResult:X8} {ex.Message}");
}
}
diff --git a/MSIXplainer/Package.appxmanifest b/MSIXplainer/Package.appxmanifest
index 5f32ded..a67c5ff 100644
--- a/MSIXplainer/Package.appxmanifest
+++ b/MSIXplainer/Package.appxmanifest
@@ -11,7 +11,7 @@
+ Version="1.0.27.0" />
@@ -44,6 +44,20 @@
+
+
+
+ MSIX Package
+ Assets\Square44x44Logo.png
+
+ .msix
+ .msixbundle
+ .appx
+ .appxbundle
+
+
+
+
+ /// Loads and analyzes a package from a file path. Used by both the file
+ /// picker flow and the file-activation flow (right-click → Open with
+ /// MSIXplainer) — see issue #20.
+ ///
+ public void LoadPackageFromPath(string path)
+ {
+ try
+ {
+ PackageFilePath = path;
- if (ManifestParserService.IsBundleFile(result.Path))
+ if (ManifestParserService.IsBundleFile(path))
{
- var packages = ManifestParserService.ExtractFromBundle(result.Path);
- // Analyze the first package in the bundle (typically the current platform arch)
+ var packages = ManifestParserService.ExtractFromBundle(path);
var pkg = packages.First();
- PackageFilePath = $"{result.Path} ({pkg.Label})";
+ PackageFilePath = $"{path} ({pkg.Label})";
AnalyzeManifest(pkg.RawXml, pkg.Info, pkg.Manifest);
}
else
{
- var (manifest, rawXml, info) = ManifestParserService.ExtractFromPackage(result.Path);
+ var (manifest, rawXml, info) = ManifestParserService.ExtractFromPackage(path);
AnalyzeManifest(rawXml, info, manifest);
}
}