#develop (short for SharpDevelop) is a free IDE for .NET programming languages.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

458 lines
14 KiB

// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Threading;
using System.Windows.Forms;
using Mono.Cecil;
using ICSharpCode.Build.Tasks;
using ICSharpCode.Core;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Project;
namespace ICSharpCode.SharpDevelop.Gui
{
public class GacReferencePanel : UserControl, IReferencePanel
{
class ColumnSorter : System.Collections.IComparer
{
private int column = 0;
bool asc = true;
public int CurrentColumn
{
get
{
return column;
}
set
{
if(column == value) asc = !asc;
else column = value;
}
}
public int Compare(object x, object y)
{
ListViewItem rowA = (ListViewItem)x;
ListViewItem rowB = (ListViewItem)y;
int result = String.Compare(rowA.SubItems[CurrentColumn].Text, rowB.SubItems[CurrentColumn].Text);
if(asc) return result;
else return result * -1;
}
}
protected ListView listView;
CheckBox chooseSpecificVersionCheckBox;
TextBox filterTextBox;
Button searchButton;
ToolTip toolTip = new ToolTip();
ToolTip filterTextboxToolTip = new ToolTip();
ISelectReferenceDialog selectDialog;
ColumnSorter sorter;
BackgroundWorker worker;
List<ListViewItem> resultList = new List<ListViewItem>();
Dictionary<string, AssemblyDefinition> assembliesCache = new Dictionary<string, AssemblyDefinition>();
DefaultAssemblyResolver resolver = new DefaultAssemblyResolver();
public GacReferencePanel(ISelectReferenceDialog selectDialog)
{
listView = new ListView();
sorter = new ColumnSorter();
listView.ListViewItemSorter = sorter;
this.selectDialog = selectDialog;
ColumnHeader referenceHeader = new ColumnHeader();
referenceHeader.Text = ResourceService.GetString("Dialog.SelectReferenceDialog.GacReferencePanel.ReferenceHeader");
referenceHeader.Width = 240;
listView.Columns.Add(referenceHeader);
listView.Sorting = SortOrder.Ascending;
ColumnHeader versionHeader = new ColumnHeader();
versionHeader.Text = ResourceService.GetString("Dialog.SelectReferenceDialog.GacReferencePanel.VersionHeader");
versionHeader.Width = 120;
listView.Columns.Add(versionHeader);
listView.View = View.Details;
listView.FullRowSelect = true;
listView.ItemActivate += delegate { AddReference(); };
listView.ColumnClick += new ColumnClickEventHandler(columnClick);
listView.Dock = DockStyle.Fill;
this.Dock = DockStyle.Fill;
this.Controls.Add(listView);
Panel upperPanel = new Panel { Dock = DockStyle.Top, Height = 20 };
chooseSpecificVersionCheckBox = new CheckBox();
chooseSpecificVersionCheckBox.Dock = DockStyle.Left;
chooseSpecificVersionCheckBox.Text = StringParser.Parse("${res:Dialog.SelectReferenceDialog.GacReferencePanel.ChooseSpecificAssemblyVersion}");
chooseSpecificVersionCheckBox.CheckedChanged += delegate {
listView.Items.Clear();
if (chooseSpecificVersionCheckBox.Checked)
listView.Items.AddRange(fullItemList);
else
listView.Items.AddRange(shortItemList);
};
filterTextBox = new TextBox { Width = 100, Dock = DockStyle.Right };
searchButton = new Button { Dock = DockStyle.Right, Width = 50, Text = "Search" };
toolTip.SetToolTip(searchButton, searchButton.Text);
filterTextboxToolTip.SetToolTip(filterTextBox, "Search by type name");
searchButton.Click += searchButton_Click;
upperPanel.Controls.Add(chooseSpecificVersionCheckBox);
upperPanel.Controls.Add(filterTextBox);
upperPanel.Controls.Add(searchButton);
this.Controls.Add(upperPanel);
PrintCache();
worker = new BackgroundWorker { WorkerSupportsCancellation = true, WorkerReportsProgress = true };
worker.DoWork += searchTask_DoWork;
worker.RunWorkerCompleted += searchTask_RunWorkerCompleted;
worker.ProgressChanged += searchTask_ProgressChanged;
}
#region Search by types
void searchTask_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
searchButton.Text = string.Format("{0} %", e.ProgressPercentage);
}
void searchTask_DoWork(object sender, DoWorkEventArgs e)
{
e.Cancel = !SearchTypesName(
chooseSpecificVersionCheckBox.Checked ? fullItemList : shortItemList, filterTextBox.Text);
}
void searchButton_Click(object sender, EventArgs e)
{
string text;
if(!worker.IsBusy) {
filterTextBox.ReadOnly = true;
worker.RunWorkerAsync();
text = "Cancel";
}
else {
worker.CancelAsync();
text = "Search";
filterTextBox.ReadOnly = false;
}
searchButton.Text = text;
this.toolTip.SetToolTip(searchButton, text);
}
void searchTask_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (this.IsDisposed) {
// avoid crash when dialog is closed before search is completed
return;
}
searchButton.Text = "Search"; this.toolTip.SetToolTip(searchButton, searchButton.Text);
filterTextBox.ReadOnly = false;
if (resultList != null && resultList.Count > 0) {
listView.Items.Clear();
listView.Items.AddRange(resultList.ToArray());
}
}
/// <summary>
/// Search for type name.
/// </summary>
/// <param name="list">Array of items where to search.</param>
/// <param name="filter">Filter to search.</param>
/// <returns><c>true</c>, if call succeded, <c>false</c> otherwise.</returns>
bool SearchTypesName(ListViewItem[] list, string filter)
{
// return null if list is null
if (list == null) return false;
// return if filter is empty
if (string.IsNullOrEmpty(filter)) {
resultList = list.ToList();
return true;
}
// clear result
resultList.Clear();
// scan the list
for (int i = 0; i < list.Length; ++i) {
ListViewItem item = list[i];
DomAssemblyName asm = item.Tag as DomAssemblyName;
// search path
if (asm.FullName.IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0)
resultList.Add(item);
else {
if (worker.CancellationPending)
return false;
// search using Mono.Cecil the class/interface/structs names
AssemblyDefinition currentAssembly;
if(!assembliesCache.ContainsKey(asm.FullName)) {
try {
currentAssembly = resolver.Resolve(asm.FullName);
}
catch {
continue;
}
assembliesCache.Add(asm.FullName, currentAssembly);
}
else
currentAssembly = assembliesCache[asm.FullName];
// search types in modules
if (currentAssembly != null) {
foreach(var module in currentAssembly.Modules)
foreach (var type in module.Types)
if (type.Name.IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0 &&
!resultList.Contains(item))
resultList.Add(item);
}
// report
worker.ReportProgress((int)(((i * 1.0) / list.Length) * 100));
}
}
return true;
}
/// <summary>
/// Clear all resources used.
/// </summary>
new void Dispose()
{
// cancel the worker
if (worker != null && worker.IsBusy && !worker.CancellationPending)
worker.CancelAsync();
worker = null;
// clear all cached data
if (assembliesCache.Count > 0)
assembliesCache.Clear();
assembliesCache = null;
if (resultList.Count > 0)
resultList.Clear();
resultList = null;
selectDialog = null;
resolver = null;
if (fullItemList.Length > 0)
Array.Clear(fullItemList, 0, fullItemList.Length);
fullItemList = null;
// force a collection to reclam memory
GC.Collect();
}
#endregion
void columnClick(object sender, ColumnClickEventArgs e)
{
if(e.Column < 2) {
sorter.CurrentColumn = e.Column;
listView.Sort();
}
}
public void AddReference()
{
foreach (ListViewItem item in listView.SelectedItems) {
string include = chooseSpecificVersionCheckBox.Checked ? item.Tag.ToString() : item.Text;
ReferenceProjectItem rpi = new ReferenceProjectItem(selectDialog.ConfigureProject, include);
string requiredFrameworkVersion;
if (chooseSpecificVersionCheckBox.Checked) {
if (KnownFrameworkAssemblies.TryGetRequiredFrameworkVersion(item.Tag.ToString(), out requiredFrameworkVersion)) {
rpi.SetMetadata("RequiredTargetFramework", requiredFrameworkVersion);
}
} else {
// find the lowest version of the assembly and use its RequiredTargetFramework
ListViewItem lowestVersion = item;
foreach (ListViewItem item2 in fullItemList) {
if (item2.Text == item.Text) {
if (new Version(item2.SubItems[1].Text) < ((DomAssemblyName)lowestVersion.Tag).Version) {
lowestVersion = item2;
}
}
}
if (KnownFrameworkAssemblies.TryGetRequiredFrameworkVersion(lowestVersion.Tag.ToString(), out requiredFrameworkVersion)) {
rpi.SetMetadata("RequiredTargetFramework", requiredFrameworkVersion);
}
}
selectDialog.AddReference(
item.Text, "Gac", rpi.Include,
rpi
);
}
}
ListViewItem[] fullItemList;
/// <summary>
/// Item list where older versions are filtered out.
/// </summary>
ListViewItem[] shortItemList;
void PrintCache()
{
IList<DomAssemblyName> cacheContent = GetCacheContent();
List<ListViewItem> itemList = new List<ListViewItem>();
// Create full item list
foreach (DomAssemblyName asm in cacheContent) {
ListViewItem item = new ListViewItem(new string[] {asm.ShortName, asm.Version.ToString()});
item.Tag = asm;
itemList.Add(item);
}
fullItemList = itemList.ToArray();
// Create short item list (without multiple versions)
itemList.Clear();
for (int i = 0; i < cacheContent.Count; i++) {
DomAssemblyName asm = cacheContent[i];
bool isDuplicate = false;
for (int j = 0; j < itemList.Count; j++) {
if (string.Equals(asm.ShortName, itemList[j].Text, StringComparison.OrdinalIgnoreCase)) {
itemList[j].SubItems[1].Text += "/" + asm.Version.ToString();
isDuplicate = true;
break;
}
}
if (!isDuplicate) {
ListViewItem item = new ListViewItem(new string[] {asm.ShortName, asm.Version.ToString()});
item.Tag = asm;
itemList.Add(item);
}
}
shortItemList = itemList.ToArray();
listView.Items.AddRange(shortItemList);
Thread resolveVersionsThread = new Thread(ResolveVersionsThread);
resolveVersionsThread.IsBackground = true;
resolveVersionsThread.Name = "resolveVersionsThread";
resolveVersionsThread.Priority = ThreadPriority.BelowNormal;
resolveVersionsThread.Start();
}
void ResolveVersionsThread()
{
try {
ResolveVersionsWorker();
//CreateReferenceToFrameworkTable();
} catch (Exception ex) {
MessageService.ShowException(ex);
}
}
void ResolveVersionsWorker()
{
MSBuildBasedProject project = selectDialog.ConfigureProject as MSBuildBasedProject;
if (project == null)
return;
List<ListViewItem> itemsToResolveVersion = new List<ListViewItem>();
List<ReferenceProjectItem> referenceItems = new List<ReferenceProjectItem>();
WorkbenchSingleton.SafeThreadCall(
delegate {
foreach (ListViewItem item in shortItemList) {
if (item.SubItems[1].Text.Contains("/")) {
itemsToResolveVersion.Add(item);
referenceItems.Add(new ReferenceProjectItem(project, item.Text));
}
}
});
string mscorlibPath;
MSBuildInternals.ResolveAssemblyReferences(project, referenceItems.ToArray(), out mscorlibPath);
WorkbenchSingleton.SafeThreadAsyncCall(
delegate {
if (IsDisposed) return;
for (int i = 0; i < itemsToResolveVersion.Count; i++) {
if (referenceItems[i].Version != null) {
itemsToResolveVersion[i].SubItems[1].Text = referenceItems[i].Version.ToString();
}
}
});
}
#if DEBUG
/// <summary>
/// run this method with a .net 3.5 and .net 4.0 project to generate the table above.
/// </summary>
void CreateReferenceToFrameworkTable()
{
LoggingService.Warn("Running CreateReferenceToFrameworkTable()");
MSBuildBasedProject project = selectDialog.ConfigureProject as MSBuildBasedProject;
if (project == null)
return;
var redistNameToRequiredFramework = new Dictionary<string, string> {
{ "Framework", null },
{ "Microsoft-Windows-CLRCoreComp", null },
{ "Microsoft.VisualStudio.Primary.Interop.Assemblies.8.0", null },
{ "Microsoft-WinFX-Runtime", "3.0" },
{ "Microsoft-Windows-CLRCoreComp.3.0", "3.0" },
{ "Microsoft-Windows-CLRCoreComp-v3.5", "3.5" },
{ "Microsoft-Windows-CLRCoreComp.4.0", "4.0" },
};
using (StreamWriter w = new StreamWriter("c:\\temp\\references.txt")) {
List<ReferenceProjectItem> referenceItems = new List<ReferenceProjectItem>();
WorkbenchSingleton.SafeThreadCall(
delegate {
foreach (ListViewItem item in fullItemList) {
referenceItems.Add(new ReferenceProjectItem(project, item.Tag.ToString()));
}
});
string mscorlibPath;
MSBuildInternals.ResolveAssemblyReferences(project, referenceItems.ToArray(), out mscorlibPath);
foreach (ReferenceProjectItem rpi in referenceItems) {
if (string.IsNullOrEmpty(rpi.Redist)) continue;
if (!redistNameToRequiredFramework.ContainsKey(rpi.Redist)) {
LoggingService.Error("unknown redist: " + rpi.Redist);
} else if (redistNameToRequiredFramework[rpi.Redist] != null) {
w.Write("\t\t\t{ \"");
w.Write(rpi.Include);
w.Write("\", \"");
w.Write(redistNameToRequiredFramework[rpi.Redist]);
w.WriteLine("\" },");
}
}
}
}
#endif
protected virtual IList<DomAssemblyName> GetCacheContent()
{
List<DomAssemblyName> list = GacInterop.GetAssemblyList();
list.RemoveAll(name => name.ShortName.EndsWith(".resources", StringComparison.OrdinalIgnoreCase));
return list;
}
protected override void Dispose(bool disposing)
{
Dispose();
base.Dispose(disposing);
}
}
}