Browse Source

Fix threading issues when accessing AssemblyList.Assemblies.

pull/10/head
Daniel Grunwald 15 years ago
parent
commit
a44673a170
  1. 22
      ILSpy/AssemblyList.cs
  2. 4
      ILSpy/MainWindow.xaml.cs
  3. 2
      ILSpy/TextView/DecompilerTextView.cs
  4. 34
      ILSpy/TreeNodes/AssemblyListTreeNode.cs
  5. 33
      ILSpy/TreeNodes/AssemblyTreeNode.cs
  6. 2
      ILSpy/TreeNodes/DerivedTypesTreeNode.cs

22
ILSpy/AssemblyList.cs

@ -41,8 +41,10 @@ namespace ICSharpCode.ILSpy
/// <summary> /// <summary>
/// The assemblies in this list. /// The assemblies in this list.
/// Needs locking for multi-threaded access!
/// Write accesses are allowed on the GUI thread only (but still need locking!)
/// </summary> /// </summary>
public readonly ObservableCollection<AssemblyTreeNode> Assemblies = new ObservableCollection<AssemblyTreeNode>(); internal readonly ObservableCollection<AssemblyTreeNode> assemblies = new ObservableCollection<AssemblyTreeNode>();
/// <summary> /// <summary>
/// Dictionary for quickly finding types (used in hyperlink navigation) /// Dictionary for quickly finding types (used in hyperlink navigation)
@ -52,7 +54,7 @@ namespace ICSharpCode.ILSpy
public AssemblyList(string listName) public AssemblyList(string listName)
{ {
this.listName = listName; this.listName = listName;
Assemblies.CollectionChanged += Assemblies_CollectionChanged; assemblies.CollectionChanged += Assemblies_CollectionChanged;
} }
/// <summary> /// <summary>
@ -67,6 +69,16 @@ namespace ICSharpCode.ILSpy
this.dirty = false; // OpenAssembly() sets dirty, so reset it afterwards this.dirty = false; // OpenAssembly() sets dirty, so reset it afterwards
} }
/// <summary>
/// Gets the loaded assemblies. This method is thread-safe.
/// </summary>
public AssemblyTreeNode[] GetAssemblies()
{
lock (assemblies) {
return assemblies.ToArray();
}
}
/// <summary> /// <summary>
/// Saves this assembly list to XML. /// Saves this assembly list to XML.
/// </summary> /// </summary>
@ -75,7 +87,7 @@ namespace ICSharpCode.ILSpy
return new XElement( return new XElement(
"List", "List",
new XAttribute("name", this.ListName), new XAttribute("name", this.ListName),
Assemblies.Select(asm => new XElement("Assembly", asm.FileName)) assemblies.Select(asm => new XElement("Assembly", asm.FileName))
); );
} }
@ -215,13 +227,13 @@ namespace ICSharpCode.ILSpy
file = Path.GetFullPath(file); file = Path.GetFullPath(file);
foreach (AssemblyTreeNode node in this.Assemblies) { foreach (AssemblyTreeNode node in this.assemblies) {
if (file.Equals(node.FileName, StringComparison.OrdinalIgnoreCase)) if (file.Equals(node.FileName, StringComparison.OrdinalIgnoreCase))
return node; return node;
} }
var newNode = new AssemblyTreeNode(file, this); var newNode = new AssemblyTreeNode(file, this);
this.Assemblies.Add(newNode); this.assemblies.Add(newNode);
return newNode; return newNode;
} }
} }

4
ILSpy/MainWindow.xaml.cs

@ -93,7 +93,7 @@ namespace ICSharpCode.ILSpy
for (int i = 1; i < args.Length; i++) { for (int i = 1; i < args.Length; i++) {
assemblyList.OpenAssembly(args[i]); assemblyList.OpenAssembly(args[i]);
} }
if (assemblyList.Assemblies.Count == 0) if (assemblyList.GetAssemblies().Length == 0)
LoadInitialAssemblies(); LoadInitialAssemblies();
SharpTreeNode node = FindNodeByPath(sessionSettings.ActiveTreeViewPath, true); SharpTreeNode node = FindNodeByPath(sessionSettings.ActiveTreeViewPath, true);
@ -139,7 +139,7 @@ namespace ICSharpCode.ILSpy
history.Clear(); history.Clear();
this.assemblyList = assemblyList; this.assemblyList = assemblyList;
assemblyList.Assemblies.CollectionChanged += assemblyList_Assemblies_CollectionChanged; assemblyList.assemblies.CollectionChanged += assemblyList_Assemblies_CollectionChanged;
assemblyListTreeNode = new AssemblyListTreeNode(assemblyList); assemblyListTreeNode = new AssemblyListTreeNode(assemblyList);
assemblyListTreeNode.FilterSettings = sessionSettings.FilterSettings.Clone(); assemblyListTreeNode.FilterSettings = sessionSettings.FilterSettings.Clone();

2
ILSpy/TextView/DecompilerTextView.cs

@ -358,7 +358,7 @@ namespace ICSharpCode.ILSpy.TextView
} else if (reference is EventReference) { } else if (reference is EventReference) {
mainWindow.SelectNode(assemblyList.FindEventNode(((EventReference)reference).Resolve())); mainWindow.SelectNode(assemblyList.FindEventNode(((EventReference)reference).Resolve()));
} else if (reference is AssemblyDefinition) { } else if (reference is AssemblyDefinition) {
mainWindow.SelectNode(assemblyList.Assemblies.FirstOrDefault(node => node.AssemblyDefinition == reference)); mainWindow.SelectNode(assemblyList.GetAssemblies().FirstOrDefault(node => node.AssemblyDefinition == reference));
} }
} }
#endregion #endregion

34
ILSpy/TreeNodes/AssemblyListTreeNode.cs

@ -38,7 +38,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
} }
public AssemblyListTreeNode(AssemblyList assemblyList) public AssemblyListTreeNode(AssemblyList assemblyList)
: base(assemblyList.Assemblies) : base(assemblyList.assemblies)
{ {
if (assemblyList == null) if (assemblyList == null)
throw new ArgumentNullException("assemblyList"); throw new ArgumentNullException("assemblyList");
@ -61,20 +61,22 @@ namespace ICSharpCode.ILSpy.TreeNodes
if (files == null) if (files == null)
files = data.GetData(DataFormats.FileDrop) as string[]; files = data.GetData(DataFormats.FileDrop) as string[];
if (files != null) { if (files != null) {
var nodes = (from file in files lock (assemblyList.assemblies) {
where file != null var nodes = (from file in files
select assemblyList.OpenAssembly(file) into node where file != null
where node != null select assemblyList.OpenAssembly(file) into node
select node).Distinct().ToList(); where node != null
foreach (AssemblyTreeNode node in nodes) { select node).Distinct().ToList();
int nodeIndex = this.Children.IndexOf(node); foreach (AssemblyTreeNode node in nodes) {
if (nodeIndex < index) int nodeIndex = assemblyList.assemblies.IndexOf(node);
index--; if (nodeIndex < index)
this.Children.RemoveAt(nodeIndex); index--;
} assemblyList.assemblies.RemoveAt(nodeIndex);
nodes.Reverse(); }
foreach (AssemblyTreeNode node in nodes) { nodes.Reverse();
this.Children.Insert(index, node); foreach (AssemblyTreeNode node in nodes) {
assemblyList.assemblies.Insert(index, node);
}
} }
} }
} }
@ -85,7 +87,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
{ {
language.WriteCommentLine(output, "List: " + assemblyList.ListName); language.WriteCommentLine(output, "List: " + assemblyList.ListName);
output.WriteLine(); output.WriteLine();
foreach (AssemblyTreeNode asm in assemblyList.Assemblies) { foreach (AssemblyTreeNode asm in assemblyList.GetAssemblies()) {
language.WriteCommentLine(output, new string('-', 60)); language.WriteCommentLine(output, new string('-', 60));
output.WriteLine(); output.WriteLine();
asm.Decompile(language, output, options); asm.Decompile(language, output, options);

33
ILSpy/TreeNodes/AssemblyTreeNode.cs

@ -25,7 +25,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Threading;
using ICSharpCode.Decompiler; using ICSharpCode.Decompiler;
using ICSharpCode.TreeView; using ICSharpCode.TreeView;
using Mono.Cecil; using Mono.Cecil;
@ -128,18 +128,6 @@ namespace ICSharpCode.ILSpy.TreeNodes
} }
} }
MenuItem CreateRemoveAssemblyItem()
{
MenuItem item = new MenuItem() {
Header = "Remove assembly",
Icon = new Image() { Source = Images.Delete }
};
item.Click += delegate { Delete(); };
return item;
}
sealed class MyAssemblyResolver : IAssemblyResolver sealed class MyAssemblyResolver : IAssemblyResolver
{ {
readonly AssemblyTreeNode parent; readonly AssemblyTreeNode parent;
@ -178,7 +166,13 @@ namespace ICSharpCode.ILSpy.TreeNodes
{ {
// specific to AssemblyTreeNode // specific to AssemblyTreeNode
var menu = new ContextMenu(); var menu = new ContextMenu();
menu.Items.Add(CreateRemoveAssemblyItem());
MenuItem item = new MenuItem() {
Header = "Remove assembly",
Icon = new Image() { Source = Images.Delete }
};
item.Click += delegate { Delete(); };
menu.Items.Add(item);
return menu; return menu;
} }
@ -229,7 +223,9 @@ namespace ICSharpCode.ILSpy.TreeNodes
public override void DeleteCore() public override void DeleteCore()
{ {
assemblyList.Assemblies.Remove(this); lock (assemblyList.assemblies) {
assemblyList.assemblies.Remove(this);
}
} }
internal const string DataFormat = "ILSpyAssemblies"; internal const string DataFormat = "ILSpyAssemblies";
@ -243,11 +239,16 @@ namespace ICSharpCode.ILSpy.TreeNodes
public AssemblyTreeNode LookupReferencedAssembly(string fullName) public AssemblyTreeNode LookupReferencedAssembly(string fullName)
{ {
foreach (AssemblyTreeNode node in assemblyList.Assemblies) { foreach (AssemblyTreeNode node in assemblyList.GetAssemblies()) {
if (node.AssemblyDefinition != null && fullName.Equals(node.AssemblyDefinition.FullName, StringComparison.OrdinalIgnoreCase)) if (node.AssemblyDefinition != null && fullName.Equals(node.AssemblyDefinition.FullName, StringComparison.OrdinalIgnoreCase))
return node; return node;
} }
if (!App.Current.Dispatcher.CheckAccess()) {
// Call this method on the GUI thread.
return (AssemblyTreeNode)App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Func<string, AssemblyTreeNode>(LookupReferencedAssembly), fullName);
}
var name = AssemblyNameReference.Parse(fullName); var name = AssemblyNameReference.Parse(fullName);
string file = GacInterop.FindAssemblyInNetGac(name); string file = GacInterop.FindAssemblyInNetGac(name);
if (file == null) { if (file == null) {

2
ILSpy/TreeNodes/DerivedTypesTreeNode.cs

@ -37,7 +37,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
protected override IEnumerable<ILSpyTreeNodeBase> FetchChildren(CancellationToken cancellationToken) protected override IEnumerable<ILSpyTreeNodeBase> FetchChildren(CancellationToken cancellationToken)
{ {
// FetchChildren() runs on the main thread; but the enumerator will be consumed on a background thread // FetchChildren() runs on the main thread; but the enumerator will be consumed on a background thread
var assemblies = list.Assemblies.Select(node => node.AssemblyDefinition).Where(asm => asm != null).ToArray(); var assemblies = list.GetAssemblies().Select(node => node.AssemblyDefinition).Where(asm => asm != null).ToArray();
return FindDerivedTypes(type, assemblies, cancellationToken); return FindDerivedTypes(type, assemblies, cancellationToken);
} }

Loading…
Cancel
Save