diff --git a/IL.View/IL.View.csproj b/IL.View/IL.View.csproj
index 76ddbcb..275d5ed 100644
--- a/IL.View/IL.View.csproj
+++ b/IL.View/IL.View.csproj
@@ -130,6 +130,7 @@
+
diff --git a/IL.View/Model/FileService.cs b/IL.View/Model/FileService.cs
new file mode 100644
index 0000000..80697c8
--- /dev/null
+++ b/IL.View/Model/FileService.cs
@@ -0,0 +1,169 @@
+/*
+ * The MIT License
+ *
+ * Copyright © 2011, Denys Vuika
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ * */
+
+using System.Diagnostics;
+using System.IO;
+using System.IO.IsolatedStorage;
+using System.Linq;
+using System.Runtime.InteropServices.Automation;
+using System.Windows;
+using System.Windows.Threading;
+using Mono.Cecil;
+
+namespace IL.View.Model
+{
+ internal static class FileService
+ {
+ private const string ReferenceCachePrefix = "REF_";
+
+ // TODO: add possibility to clear cache from UI
+ public static AssemblyDefinition FindExternalAssembly(AssemblyNameReference reference, Dispatcher dispatcher)
+ {
+ // TODO: add support User folders
+ /*
+ * User folders can be accessed without Automation Factory so it should be possible to
+ * define a reference path based on User folders hierarchy. In this case this feature will work
+ * for MacOs platforms.
+ *
+ * Possible ways to implement:
+ *
+ * 1. Allow "Reference Paths" tab to be visible when running on Macs
+ * 2. When adding reference path a check should be performed (whether path belongs to User folder hierarchy)
+ * 3. The paths that do not belong to User folder hierachy should not be added (Macs only, provide some warning/notification)
+ *
+ * Reference caching should also work fine for User folders.
+ */
+ if (!Application.Current.HasElevatedPermissions) return null;
+ if (!AutomationFactory.IsAvailable) return null;
+ if (reference == null) return null;
+
+ var referencePaths = ReferencePathsSettings.Current.Folders.ToArray();
+ var targetName = string.Format("{0}.dll", reference.Name);
+
+ try
+ {
+ var savedPath = GetCachedPath(reference);
+ // TODO: optimize code and remove duplication
+ if (!string.IsNullOrEmpty(savedPath))
+ {
+ using (var stream = LoadFile(savedPath))
+ {
+ var definition = AssemblyDefinition.ReadAssembly(stream);
+
+ if (definition != null && definition.FullName == reference.FullName)
+ {
+ Debug.WriteLine("Successfully resolved external assembly from cache: {0}", savedPath);
+ var assemblyStream = new AssemblyMemoryStream(targetName, stream);
+ dispatcher.BeginInvoke(() => ApplicationModel.Current.AssemblyCache.LoadAssembly(assemblyStream, definition));
+ return definition;
+ }
+ }
+ }
+
+ var fso = AutomationFactory.CreateObject("Scripting.FileSystemObject");
+
+ foreach (var referenceFolder in referencePaths)
+ {
+ var file = FindFile(fso, referenceFolder.Path, targetName, referenceFolder.RecursiveSearch);
+ if (file == null) continue;
+
+ string path = file.Path;
+ using (var stream = LoadFile(path))
+ {
+ var definition = AssemblyDefinition.ReadAssembly(stream);
+
+ if (definition == null) continue;
+ if (definition.FullName != reference.FullName) continue;
+
+ Debug.WriteLine("Successfully resolved external assembly: {0}", path);
+
+ AddCachedPath(reference, path);
+
+ var assemblyStream = new AssemblyMemoryStream(targetName, stream);
+ dispatcher.BeginInvoke(() => ApplicationModel.Current.AssemblyCache.LoadAssembly(assemblyStream, definition));
+ return definition;
+ }
+ }
+ }
+ catch
+ {
+ if (Debugger.IsAttached) Debugger.Break();
+ return null;
+ }
+
+ return null;
+ }
+
+ private static dynamic FindFile(dynamic fso, string rootPath, string fileName, bool recursive)
+ {
+ if (!fso.FolderExists(rootPath)) return null;
+ var folder = fso.GetFolder(rootPath);
+
+ var targetPath = Path.Combine(rootPath, fileName);
+ if (fso.FileExists(targetPath)) return fso.GetFile(targetPath);
+
+ if (!recursive) return null;
+
+ foreach (var subFolder in folder.SubFolders)
+ {
+ var assembly = FindFile(fso, subFolder.Path, fileName, true);
+ if (assembly != null) return assembly;
+ }
+
+ return null;
+ }
+
+ private static string GetCachedPath(AssemblyNameReference reference)
+ {
+ if (reference == null) return null;
+ var cachedName = ReferenceCachePrefix + reference.FullName;
+ string cachedPath;
+ return IsolatedStorageSettings.ApplicationSettings.TryGetValue(cachedName, out cachedPath) ? cachedPath : null;
+ }
+
+ private static void AddCachedPath(AssemblyNameReference reference, string path)
+ {
+ if (reference == null) return;
+ if (string.IsNullOrWhiteSpace(path)) return;
+ IsolatedStorageSettings.ApplicationSettings[ReferenceCachePrefix + reference.FullName] = path;
+ }
+
+ // http://stackoverflow.com/questions/3462039/scripting-filesystemobject-write-method-fails
+ private static Stream LoadFile(string path)
+ {
+ byte[] data;
+ const int adTypeBinary = 1;
+
+ using (var adoCom = AutomationFactory.CreateObject("ADODB.Stream"))
+ {
+ adoCom.Type = adTypeBinary;
+ adoCom.Open();
+ adoCom.LoadFromFile(path);
+ data = adoCom.Read();
+ }
+
+ return data == null ? Stream.Null : new MemoryStream(data);
+ }
+ }
+}
diff --git a/IL.View/Model/ReferencePathsSettings.cs b/IL.View/Model/ReferencePathsSettings.cs
index 75a8079..fcbf218 100644
--- a/IL.View/Model/ReferencePathsSettings.cs
+++ b/IL.View/Model/ReferencePathsSettings.cs
@@ -35,15 +35,42 @@
namespace IL.View.Model
{
[DebuggerDisplay("{Path}")]
- public sealed class ReferenceFolder
+ public sealed class ReferenceFolder : INotifyPropertyChanged
{
+ private readonly ReferencePathsSettings _settings;
+ private bool _recursiveSearch;
+
public string Path { get; set; }
- public ReferenceFolder(string path)
+ public bool RecursiveSearch
+ {
+ get { return _recursiveSearch; }
+ set
+ {
+ if (_recursiveSearch == value) return;
+ _recursiveSearch = value;
+ OnPropertyChanged("RecursiveSearch");
+ if (_settings.AutoSave) _settings.Save();
+ }
+ }
+
+ internal ReferenceFolder(ReferencePathsSettings settings, string path, bool recursiveSearch = false)
{
+ if (settings == null) throw new ArgumentNullException("settings");
if (string.IsNullOrWhiteSpace(path)) throw new ArgumentNullException("path");
+
+ _settings = settings;
+ _recursiveSearch = recursiveSearch;
Path = path;
}
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ private void OnPropertyChanged(string propertyName)
+ {
+ var handler = PropertyChanged;
+ if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
+ }
}
public sealed class ReferencePathsSettings : INotifyPropertyChanged
@@ -54,7 +81,7 @@ public static ReferencePathsSettings Current
}
public bool AutoSave { get; set; }
-
+
private ObservableCollection _folders = new ObservableCollection();
public ObservableCollection Folders
@@ -86,7 +113,7 @@ public bool AddFolder(string path)
if (!Folders.Any(f => f.Path.Equals(path, StringComparison.OrdinalIgnoreCase)))
{
- Folders.Add(new ReferenceFolder(path));
+ Folders.Add(new ReferenceFolder(this, path));
return true;
}
@@ -102,9 +129,10 @@ public void Save()
{
var data = new XElement("ReferencePaths");
- foreach (var address in Folders.Select(d => d.Path).Distinct())
+ foreach (var folder in Folders)
data.Add(new XElement("ReferenceFolder",
- new XAttribute("Path", address)));
+ new XAttribute("Path", folder.Path),
+ new XAttribute("RecursiveSearch", folder.RecursiveSearch)));
IsolatedStorageSettings.ApplicationSettings["ReferencePaths"] = data.ToString();
IsolatedStorageSettings.ApplicationSettings.Save();
@@ -116,7 +144,7 @@ public void Load()
OnPropertyChanged("Folders");
}
- private static IEnumerable LoadSettings()
+ private IEnumerable LoadSettings()
{
string settings;
@@ -128,9 +156,9 @@ private static IEnumerable LoadSettings()
{
foreach (var element in data.Elements())
{
- var path = element.Attribute("Path");
- if (path != null)
- yield return new ReferenceFolder(path.Value);
+ var path = (string)element.Attribute("Path");
+ var recursiveSearch = (bool)element.Attribute("RecursiveSearch");
+ yield return new ReferenceFolder(this, path, recursiveSearch);
}
}
}
diff --git a/IL.View/Views/Home.xaml.cs b/IL.View/Views/Home.xaml.cs
index 42b9f71..2d80d8a 100644
--- a/IL.View/Views/Home.xaml.cs
+++ b/IL.View/Views/Home.xaml.cs
@@ -373,6 +373,10 @@ private AssemblyDefinition TryResolveAssembly(object sender, AssemblyNameReferen
if (definition == null)
definition = TryResolveHigherVersionAssembly(_decompileTask.CallingAssembly, reference);
+ // try to resolve assembly from user-defined reference paths
+ if (definition == null)
+ definition = FileService.FindExternalAssembly(reference, Dispatcher);
+
// ask user to resolve assembly manually
if (definition == null)
{
diff --git a/IL.View/Views/Settings.xaml b/IL.View/Views/Settings.xaml
index 3daedca..d3f600c 100644
--- a/IL.View/Views/Settings.xaml
+++ b/IL.View/Views/Settings.xaml
@@ -40,6 +40,7 @@
+
@@ -51,9 +52,11 @@
-
+ Margin="0,5,0,0"
+ SelectionMode="Single"
+ SelectionChanged="OnReferenceFolderSelected">
@@ -67,6 +70,11 @@
+
+
diff --git a/IL.View/Views/Settings.xaml.cs b/IL.View/Views/Settings.xaml.cs
index f61a121..de1109e 100644
--- a/IL.View/Views/Settings.xaml.cs
+++ b/IL.View/Views/Settings.xaml.cs
@@ -24,6 +24,8 @@
using System.Windows;
using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using System.Windows.Data;
using System.Windows.Navigation;
using IL.View.Model;
using System.Runtime.InteropServices.Automation;
@@ -82,5 +84,24 @@ private void OnRemoveEntryClick(object sender, RoutedEventArgs e)
if (referenceFolder != null) _referencePathsSettings.Folders.Remove(referenceFolder);
}
+ private void OnReferenceFolderSelected(object sender, SelectionChangedEventArgs e)
+ {
+ if (e.AddedItems.Count > 0)
+ {
+ RecursiveSearch.IsEnabled = true;
+ RecursiveSearch.SetBinding(ToggleButton.IsCheckedProperty, new Binding("RecursiveSearch")
+ {
+ Mode = BindingMode.TwoWay,
+ Source = e.AddedItems[0]
+ });
+ }
+ else
+ {
+ RecursiveSearch.IsEnabled = false;
+ RecursiveSearch.DataContext = null;
+ RecursiveSearch.ClearValue(ToggleButton.IsCheckedProperty);
+ }
+ }
+
}
}