diff --git a/ILSpy.AddIn/ILSpy.AddIn.csproj b/ILSpy.AddIn/ILSpy.AddIn.csproj
index 5a96857ac..1d1a3cea9 100644
--- a/ILSpy.AddIn/ILSpy.AddIn.csproj
+++ b/ILSpy.AddIn/ILSpy.AddIn.csproj
@@ -117,12 +117,14 @@
-
+
-
-
-
-
+
+
+
+
+
+
diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj
index 201719e62..a371180ea 100644
--- a/ILSpy/ILSpy.csproj
+++ b/ILSpy/ILSpy.csproj
@@ -56,6 +56,8 @@
+
+
diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs
index ef311f68a..e48569701 100644
--- a/ILSpy/MainWindow.xaml.cs
+++ b/ILSpy/MainWindow.xaml.cs
@@ -35,6 +35,7 @@ using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Threading;
+
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Documentation;
using ICSharpCode.Decompiler.Metadata;
@@ -46,7 +47,13 @@ using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.ILSpy.ViewModels;
using ICSharpCode.TreeView;
+
+using Microsoft.NET.HostModel.AppHost;
+using Microsoft.NET.HostModel.Bundle;
using Microsoft.Win32;
+
+using Ookii.Dialogs.Wpf;
+
using OSVersionHelper;
using Xceed.Wpf.AvalonDock.Layout.Serialization;
@@ -994,15 +1001,55 @@ namespace ICSharpCode.ILSpy
}
break;
default:
- var asm = assemblyList.OpenAssembly(file);
- if (asm != null) {
- if (loadedAssemblies != null)
- loadedAssemblies.Add(asm);
- else {
- var node = assemblyListTreeNode.FindAssemblyNode(asm);
- if (node != null && focusNode) {
- AssemblyTreeView.SelectedItems.Add(node);
- lastNode = node;
+ if (IsAppBundle(file, out var headerOffset)) {
+ if (MessageBox.Show(this, Properties.Resources.OpenSelfContainedExecutableMessage, "ILSpy", MessageBoxButton.YesNo) == MessageBoxResult.No)
+ break;
+ var dialog = new VistaFolderBrowserDialog();
+ if (dialog.ShowDialog() != true)
+ break;
+ DockWorkspace.Instance.RunWithCancellation(ct => Task.Factory.StartNew(() => {
+ var output = new AvalonEditTextOutput { Title = "Extracting " + file };
+ Stopwatch w = Stopwatch.StartNew();
+ output.WriteLine($"Extracting {file} to {dialog.SelectedPath}...");
+ var extractor = new Extractor(file, dialog.SelectedPath);
+ extractor.ExtractFiles();
+ output.WriteLine($"Done in {w.Elapsed}.");
+ return output;
+ }, ct)).Then(output => {
+ DockWorkspace.Instance.ShowText(output);
+
+ OpenFileDialog dlg = new OpenFileDialog();
+ dlg.Filter = ".NET assemblies|*.dll;*.exe;*.winmd";
+ dlg.Multiselect = true;
+ dlg.InitialDirectory = dialog.SelectedPath;
+ if (dlg.ShowDialog() == true) {
+ foreach (var item in dlg.FileNames) {
+ var asm = assemblyList.OpenAssembly(item);
+ if (asm != null) {
+ if (loadedAssemblies != null)
+ loadedAssemblies.Add(asm);
+ else {
+ var node = assemblyListTreeNode.FindAssemblyNode(asm);
+ if (node != null && focusNode) {
+ AssemblyTreeView.SelectedItems.Add(node);
+ lastNode = node;
+ }
+ }
+ }
+ }
+ }
+ }).HandleExceptions();
+ } else {
+ var asm = assemblyList.OpenAssembly(file);
+ if (asm != null) {
+ if (loadedAssemblies != null)
+ loadedAssemblies.Add(asm);
+ else {
+ var node = assemblyListTreeNode.FindAssemblyNode(asm);
+ if (node != null && focusNode) {
+ AssemblyTreeView.SelectedItems.Add(node);
+ lastNode = node;
+ }
}
}
}
@@ -1012,6 +1059,16 @@ namespace ICSharpCode.ILSpy
if (lastNode != null && focusNode)
AssemblyTreeView.FocusNode(lastNode);
}
+
+ bool IsAppBundle(string filename, out long bundleHeaderOffset)
+ {
+ try {
+ return HostWriter.IsBundle(filename, out bundleHeaderOffset);
+ } catch (Exception) {
+ bundleHeaderOffset = -1;
+ return false;
+ }
+ }
}
void RefreshCommandExecuted(object sender, ExecutedRoutedEventArgs e)
diff --git a/ILSpy/Properties/Resources.Designer.cs b/ILSpy/Properties/Resources.Designer.cs
index 10b87ca5c..51507cb90 100644
--- a/ILSpy/Properties/Resources.Designer.cs
+++ b/ILSpy/Properties/Resources.Designer.cs
@@ -1605,6 +1605,21 @@ namespace ICSharpCode.ILSpy.Properties {
}
}
+ ///
+ /// Looks up a localized string similar to You are trying to open a single-file executable (app bundle). In order to work with this type of program, ILSpy will
+ ///
+ ///(i) show you a dialog to select a folder to extract the bundle to
+ ///(ii) extract the assemblies (might take a few seconds)
+ ///(iii) show you a dialog to select one or more of those extracted assemblies to decompile
+ ///
+ ///Do you want to proceed?.
+ ///
+ public static string OpenSelfContainedExecutableMessage {
+ get {
+ return ResourceManager.GetString("OpenSelfContainedExecutableMessage", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Options.
///
diff --git a/ILSpy/Properties/Resources.resx b/ILSpy/Properties/Resources.resx
index d89524790..b8708bf57 100644
--- a/ILSpy/Properties/Resources.resx
+++ b/ILSpy/Properties/Resources.resx
@@ -867,4 +867,13 @@ Do you want to continue?
Culture
+
+ You are trying to open a single-file executable (app bundle). In order to work with this type of program, ILSpy will
+
+(i) show you a dialog to select a folder to extract the bundle to
+(ii) extract the assemblies (might take a few seconds)
+(iii) show you a dialog to select one or more of those extracted assemblies to decompile
+
+Do you want to proceed?
+
\ No newline at end of file