mirror of https://github.com/icsharpcode/ILSpy.git
Browse Source
- AssemblyList, AssemblyListManager, AssemblyListSnapshot - LoadedAssembly - LoadedPackage and friends - PDB providerspull/2656/head
83 changed files with 869 additions and 712 deletions
@ -1,19 +0,0 @@ |
|||||||
<Project Sdk="Microsoft.NET.Sdk"> |
|
||||||
|
|
||||||
<PropertyGroup> |
|
||||||
<TargetFramework>netstandard2.0</TargetFramework> |
|
||||||
<LangVersion>8.0</LangVersion> |
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> |
|
||||||
</PropertyGroup> |
|
||||||
|
|
||||||
<Import Project="..\packages.props" /> |
|
||||||
|
|
||||||
<ItemGroup> |
|
||||||
<PackageReference Include="Mono.Cecil" Version="$(MonoCecilVersion)" /> |
|
||||||
</ItemGroup> |
|
||||||
|
|
||||||
<ItemGroup> |
|
||||||
<ProjectReference Include="..\ICSharpCode.Decompiler\ICSharpCode.Decompiler.csproj" /> |
|
||||||
</ItemGroup> |
|
||||||
|
|
||||||
</Project> |
|
@ -0,0 +1,324 @@ |
|||||||
|
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
|
||||||
|
//
|
||||||
|
// 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; |
||||||
|
using System.Collections.ObjectModel; |
||||||
|
using System.IO; |
||||||
|
using System.Linq; |
||||||
|
using System.Xml.Linq; |
||||||
|
|
||||||
|
using ICSharpCode.Decompiler.Metadata; |
||||||
|
|
||||||
|
namespace ICSharpCode.ILSpyX |
||||||
|
{ |
||||||
|
public interface ISettingsProvider |
||||||
|
{ |
||||||
|
XElement this[XName section] { get; } |
||||||
|
|
||||||
|
void Update(Action<XElement> action); |
||||||
|
ISettingsProvider Load(); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Manages the available assembly lists.
|
||||||
|
///
|
||||||
|
/// Contains the list of list names; and provides methods for loading/saving and creating/deleting lists.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class AssemblyListManager |
||||||
|
{ |
||||||
|
public const string DotNet4List = ".NET 4 (WPF)"; |
||||||
|
public const string DotNet35List = ".NET 3.5"; |
||||||
|
public const string ASPDotNetMVC3List = "ASP.NET (MVC3)"; |
||||||
|
|
||||||
|
private ISettingsProvider settingsProvider; |
||||||
|
|
||||||
|
public AssemblyListManager(ISettingsProvider settingsProvider) |
||||||
|
{ |
||||||
|
this.settingsProvider = settingsProvider; |
||||||
|
XElement doc = this.settingsProvider["AssemblyLists"]; |
||||||
|
foreach (var list in doc.Elements("List")) |
||||||
|
{ |
||||||
|
AssemblyLists.Add((string)list.Attribute("name")); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public bool ApplyWinRTProjections { get; set; } |
||||||
|
public bool UseDebugSymbols { get; set; } |
||||||
|
|
||||||
|
public ObservableCollection<string> AssemblyLists { get; } = new ObservableCollection<string>(); |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads an assembly list from the ILSpySettings.
|
||||||
|
/// If no list with the specified name is found, the default list is loaded instead.
|
||||||
|
/// </summary>
|
||||||
|
public AssemblyList LoadList(string listName) |
||||||
|
{ |
||||||
|
this.settingsProvider = this.settingsProvider.Load(); |
||||||
|
AssemblyList list = DoLoadList(listName); |
||||||
|
if (!AssemblyLists.Contains(list.ListName)) |
||||||
|
AssemblyLists.Add(list.ListName); |
||||||
|
return list; |
||||||
|
} |
||||||
|
|
||||||
|
AssemblyList DoLoadList(string listName) |
||||||
|
{ |
||||||
|
XElement doc = this.settingsProvider["AssemblyLists"]; |
||||||
|
if (listName != null) |
||||||
|
{ |
||||||
|
foreach (var list in doc.Elements("List")) |
||||||
|
{ |
||||||
|
if ((string)list.Attribute("name") == listName) |
||||||
|
{ |
||||||
|
return new AssemblyList(this, list) { |
||||||
|
UseDebugSymbols = UseDebugSymbols, |
||||||
|
ApplyWinRTProjections = ApplyWinRTProjections |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return new AssemblyList(this, listName ?? DefaultListName) { |
||||||
|
UseDebugSymbols = UseDebugSymbols, |
||||||
|
ApplyWinRTProjections = ApplyWinRTProjections |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
public bool CloneList(string selectedAssemblyList, string newListName) |
||||||
|
{ |
||||||
|
var list = DoLoadList(selectedAssemblyList); |
||||||
|
var newList = new AssemblyList(list, newListName) { |
||||||
|
UseDebugSymbols = UseDebugSymbols, |
||||||
|
ApplyWinRTProjections = ApplyWinRTProjections |
||||||
|
}; |
||||||
|
return AddListIfNotExists(newList); |
||||||
|
} |
||||||
|
|
||||||
|
public bool RenameList(string selectedAssemblyList, string newListName) |
||||||
|
{ |
||||||
|
var list = DoLoadList(selectedAssemblyList); |
||||||
|
var newList = new AssemblyList(list, newListName) { |
||||||
|
UseDebugSymbols = UseDebugSymbols, |
||||||
|
ApplyWinRTProjections = ApplyWinRTProjections |
||||||
|
}; |
||||||
|
return DeleteList(selectedAssemblyList) && AddListIfNotExists(newList); |
||||||
|
} |
||||||
|
|
||||||
|
public const string DefaultListName = "(Default)"; |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Saves the specified assembly list into the config file.
|
||||||
|
/// </summary>
|
||||||
|
public void SaveList(AssemblyList list) |
||||||
|
{ |
||||||
|
this.settingsProvider.Update( |
||||||
|
delegate (XElement root) { |
||||||
|
XElement doc = root.Element("AssemblyLists"); |
||||||
|
if (doc == null) |
||||||
|
{ |
||||||
|
doc = new XElement("AssemblyLists"); |
||||||
|
root.Add(doc); |
||||||
|
} |
||||||
|
XElement listElement = doc.Elements("List").FirstOrDefault(e => (string)e.Attribute("name") == list.ListName); |
||||||
|
if (listElement != null) |
||||||
|
listElement.ReplaceWith(list.SaveAsXml()); |
||||||
|
else |
||||||
|
doc.Add(list.SaveAsXml()); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
public bool AddListIfNotExists(AssemblyList list) |
||||||
|
{ |
||||||
|
if (!AssemblyLists.Contains(list.ListName)) |
||||||
|
{ |
||||||
|
AssemblyLists.Add(list.ListName); |
||||||
|
SaveList(list); |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public bool DeleteList(string Name) |
||||||
|
{ |
||||||
|
if (AssemblyLists.Remove(Name)) |
||||||
|
{ |
||||||
|
this.settingsProvider.Update( |
||||||
|
delegate (XElement root) { |
||||||
|
XElement doc = root.Element("AssemblyLists"); |
||||||
|
if (doc == null) |
||||||
|
{ |
||||||
|
return; |
||||||
|
} |
||||||
|
XElement listElement = doc.Elements("List").FirstOrDefault(e => (string)e.Attribute("name") == Name); |
||||||
|
if (listElement != null) |
||||||
|
listElement.Remove(); |
||||||
|
}); |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public void ClearAll() |
||||||
|
{ |
||||||
|
AssemblyLists.Clear(); |
||||||
|
this.settingsProvider.Update( |
||||||
|
delegate (XElement root) { |
||||||
|
XElement doc = root.Element("AssemblyLists"); |
||||||
|
if (doc == null) |
||||||
|
{ |
||||||
|
return; |
||||||
|
} |
||||||
|
doc.Remove(); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
public void CreateDefaultAssemblyLists() |
||||||
|
{ |
||||||
|
if (AssemblyLists.Count > 0) |
||||||
|
return; |
||||||
|
|
||||||
|
if (!AssemblyLists.Contains(DotNet4List)) |
||||||
|
{ |
||||||
|
AssemblyList dotnet4 = CreateDefaultList(DotNet4List); |
||||||
|
if (dotnet4.Count > 0) |
||||||
|
{ |
||||||
|
AddListIfNotExists(dotnet4); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (!AssemblyLists.Contains(DotNet35List)) |
||||||
|
{ |
||||||
|
AssemblyList dotnet35 = CreateDefaultList(DotNet35List); |
||||||
|
if (dotnet35.Count > 0) |
||||||
|
{ |
||||||
|
AddListIfNotExists(dotnet35); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (!AssemblyLists.Contains(ASPDotNetMVC3List)) |
||||||
|
{ |
||||||
|
AssemblyList mvc = CreateDefaultList(ASPDotNetMVC3List); |
||||||
|
if (mvc.Count > 0) |
||||||
|
{ |
||||||
|
AddListIfNotExists(mvc); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public AssemblyList CreateList(string name) |
||||||
|
{ |
||||||
|
return new AssemblyList(this, name); |
||||||
|
} |
||||||
|
|
||||||
|
public AssemblyList CreateDefaultList(string name, string? path = null, string? newName = null) |
||||||
|
{ |
||||||
|
var list = new AssemblyList(this, newName ?? name); |
||||||
|
switch (name) |
||||||
|
{ |
||||||
|
case DotNet4List: |
||||||
|
AddToListFromGAC("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); |
||||||
|
AddToListFromGAC("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); |
||||||
|
AddToListFromGAC("System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); |
||||||
|
AddToListFromGAC("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); |
||||||
|
AddToListFromGAC("System.Data.DataSetExtensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); |
||||||
|
AddToListFromGAC("System.Xaml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); |
||||||
|
AddToListFromGAC("System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); |
||||||
|
AddToListFromGAC("System.Xml.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); |
||||||
|
AddToListFromGAC("Microsoft.CSharp, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"); |
||||||
|
AddToListFromGAC("PresentationCore, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); |
||||||
|
AddToListFromGAC("PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); |
||||||
|
AddToListFromGAC("WindowsBase, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); |
||||||
|
break; |
||||||
|
case DotNet35List: |
||||||
|
AddToListFromGAC("mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); |
||||||
|
AddToListFromGAC("System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); |
||||||
|
AddToListFromGAC("System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); |
||||||
|
AddToListFromGAC("System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); |
||||||
|
AddToListFromGAC("System.Data.DataSetExtensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); |
||||||
|
AddToListFromGAC("System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); |
||||||
|
AddToListFromGAC("System.Xml.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); |
||||||
|
AddToListFromGAC("PresentationCore, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); |
||||||
|
AddToListFromGAC("PresentationFramework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); |
||||||
|
AddToListFromGAC("WindowsBase, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); |
||||||
|
break; |
||||||
|
case ASPDotNetMVC3List: |
||||||
|
AddToListFromGAC("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); |
||||||
|
AddToListFromGAC("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); |
||||||
|
AddToListFromGAC("System.ComponentModel.DataAnnotations, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); |
||||||
|
AddToListFromGAC("System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"); |
||||||
|
AddToListFromGAC("System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); |
||||||
|
AddToListFromGAC("System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); |
||||||
|
AddToListFromGAC("System.Data.DataSetExtensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); |
||||||
|
AddToListFromGAC("System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); |
||||||
|
AddToListFromGAC("System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"); |
||||||
|
AddToListFromGAC("System.EnterpriseServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"); |
||||||
|
AddToListFromGAC("System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"); |
||||||
|
AddToListFromGAC("System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); |
||||||
|
AddToListFromGAC("System.Web.ApplicationServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); |
||||||
|
AddToListFromGAC("System.Web.DynamicData, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); |
||||||
|
AddToListFromGAC("System.Web.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); |
||||||
|
AddToListFromGAC("System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); |
||||||
|
AddToListFromGAC("System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); |
||||||
|
AddToListFromGAC("System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); |
||||||
|
AddToListFromGAC("System.Web.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"); |
||||||
|
AddToListFromGAC("System.Web.WebPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); |
||||||
|
AddToListFromGAC("System.Web.Helpers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); |
||||||
|
AddToListFromGAC("System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); |
||||||
|
AddToListFromGAC("System.Xml.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); |
||||||
|
AddToListFromGAC("Microsoft.CSharp, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"); |
||||||
|
break; |
||||||
|
case object _ when path != null: |
||||||
|
foreach (var file in Directory.GetFiles(path, "*.dll")) |
||||||
|
{ |
||||||
|
var dllname = Path.GetFileName(file); |
||||||
|
if (DoIncludeFile(dllname)) |
||||||
|
AddToListFromDirectory(file); |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
return list; |
||||||
|
|
||||||
|
void AddToListFromGAC(string fullName) |
||||||
|
{ |
||||||
|
AssemblyNameReference reference = AssemblyNameReference.Parse(fullName); |
||||||
|
string? file = UniversalAssemblyResolver.GetAssemblyInGac(reference); |
||||||
|
if (file != null) |
||||||
|
list.OpenAssembly(file); |
||||||
|
} |
||||||
|
|
||||||
|
void AddToListFromDirectory(string file) |
||||||
|
{ |
||||||
|
if (File.Exists(file)) |
||||||
|
list.OpenAssembly(file); |
||||||
|
} |
||||||
|
|
||||||
|
bool DoIncludeFile(string fileName) |
||||||
|
{ |
||||||
|
if (fileName == "Microsoft.DiaSymReader.Native.amd64.dll") |
||||||
|
return false; |
||||||
|
if (fileName.EndsWith("_cor3.dll", StringComparison.OrdinalIgnoreCase)) |
||||||
|
return false; |
||||||
|
if (char.IsUpper(fileName[0])) |
||||||
|
return true; |
||||||
|
if (fileName == "netstandard.dll") |
||||||
|
return true; |
||||||
|
if (fileName == "mscorlib.dll") |
||||||
|
return true; |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,122 @@ |
|||||||
|
// Copyright (c) 2022 Siegfried Pammer
|
||||||
|
//
|
||||||
|
// 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; |
||||||
|
using System.Collections; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Linq; |
||||||
|
|
||||||
|
using ICSharpCode.Decompiler.Util; |
||||||
|
|
||||||
|
namespace ICSharpCode.ILSpyX.Extensions |
||||||
|
{ |
||||||
|
public static class CollectionExtensions |
||||||
|
{ |
||||||
|
public static void AddRange<T>(this ICollection<T> list, IEnumerable<T> items) |
||||||
|
{ |
||||||
|
foreach (T item in items) |
||||||
|
if (!list.Contains(item)) |
||||||
|
list.Add(item); |
||||||
|
} |
||||||
|
|
||||||
|
public static T? PeekOrDefault<T>(this Stack<T> stack) |
||||||
|
{ |
||||||
|
if (stack.Count == 0) |
||||||
|
return default(T); |
||||||
|
return stack.Peek(); |
||||||
|
} |
||||||
|
|
||||||
|
public static int BinarySearch<T>(this IList<T> list, T item, int start, int count, IComparer<T> comparer) |
||||||
|
{ |
||||||
|
if (list == null) |
||||||
|
throw new ArgumentNullException(nameof(list)); |
||||||
|
if (start < 0 || start >= list.Count) |
||||||
|
throw new ArgumentOutOfRangeException(nameof(start), start, "Value must be between 0 and " + (list.Count - 1)); |
||||||
|
if (count < 0 || count > list.Count - start) |
||||||
|
throw new ArgumentOutOfRangeException(nameof(count), count, "Value must be between 0 and " + (list.Count - start)); |
||||||
|
int end = start + count - 1; |
||||||
|
while (start <= end) |
||||||
|
{ |
||||||
|
int pivot = (start + end) / 2; |
||||||
|
int result = comparer.Compare(item, list[pivot]); |
||||||
|
if (result == 0) |
||||||
|
return pivot; |
||||||
|
if (result < 0) |
||||||
|
end = pivot - 1; |
||||||
|
else |
||||||
|
start = pivot + 1; |
||||||
|
} |
||||||
|
return ~start; |
||||||
|
} |
||||||
|
|
||||||
|
public static int BinarySearch<T, TKey>(this IList<T> instance, TKey itemKey, Func<T, TKey> keySelector) |
||||||
|
where TKey : IComparable<TKey>, IComparable |
||||||
|
{ |
||||||
|
if (instance == null) |
||||||
|
throw new ArgumentNullException(nameof(instance)); |
||||||
|
if (keySelector == null) |
||||||
|
throw new ArgumentNullException(nameof(keySelector)); |
||||||
|
|
||||||
|
int start = 0; |
||||||
|
int end = instance.Count - 1; |
||||||
|
|
||||||
|
while (start <= end) |
||||||
|
{ |
||||||
|
int m = (start + end) / 2; |
||||||
|
TKey key = keySelector(instance[m]); |
||||||
|
int result = key.CompareTo(itemKey); |
||||||
|
if (result == 0) |
||||||
|
return m; |
||||||
|
if (result < 0) |
||||||
|
start = m + 1; |
||||||
|
else |
||||||
|
end = m - 1; |
||||||
|
} |
||||||
|
return ~start; |
||||||
|
} |
||||||
|
|
||||||
|
public static void InsertSorted<T>(this IList<T> list, T item, IComparer<T> comparer) |
||||||
|
{ |
||||||
|
if (list == null) |
||||||
|
throw new ArgumentNullException(nameof(list)); |
||||||
|
if (comparer == null) |
||||||
|
throw new ArgumentNullException(nameof(comparer)); |
||||||
|
|
||||||
|
if (list.Count == 0) |
||||||
|
{ |
||||||
|
list.Add(item); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
int index = list.BinarySearch(item, 0, list.Count, comparer); |
||||||
|
list.Insert(index < 0 ? ~index : index, item); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
internal static void Deconstruct<TKey, TValue>(this KeyValuePair<TKey, TValue> pair, out TKey key, out TValue value) |
||||||
|
{ |
||||||
|
key = pair.Key; |
||||||
|
value = pair.Value; |
||||||
|
} |
||||||
|
|
||||||
|
internal static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T>? inst) => inst ?? Enumerable.Empty<T>(); |
||||||
|
internal static IEnumerable EmptyIfNull(this IEnumerable? inst) => inst ?? Enumerable.Empty<object>(); |
||||||
|
internal static IList<T> EmptyIfNull<T>(this IList<T>? inst) => inst ?? EmptyList<T>.Instance; |
||||||
|
internal static IList EmptyIfNull(this IList? inst) => inst ?? Array.Empty<object>(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,41 @@ |
|||||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||||
|
|
||||||
|
<PropertyGroup> |
||||||
|
<TargetFramework>net6.0</TargetFramework> |
||||||
|
<Nullable>enable</Nullable> |
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> |
||||||
|
|
||||||
|
<NeutralLanguage>en-US</NeutralLanguage> |
||||||
|
<GenerateAssemblyVersionAttribute>False</GenerateAssemblyVersionAttribute> |
||||||
|
<GenerateAssemblyFileVersionAttribute>False</GenerateAssemblyFileVersionAttribute> |
||||||
|
<GenerateAssemblyInformationalVersionAttribute>False</GenerateAssemblyInformationalVersionAttribute> |
||||||
|
</PropertyGroup> |
||||||
|
|
||||||
|
<Import Project="..\packages.props" /> |
||||||
|
|
||||||
|
<ItemGroup> |
||||||
|
<Compile Remove="Properties\AssemblyInfo.template.cs" /> |
||||||
|
</ItemGroup> |
||||||
|
|
||||||
|
<ItemGroup> |
||||||
|
<PackageReference Include="System.IO.Compression" Version="4.3.0" /> |
||||||
|
<PackageReference Include="System.Reflection.Metadata" Version="$(SystemReflectionMetadataVersion)" /> |
||||||
|
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="$(SystemCompilerServicesUnsafeVersion)" /> |
||||||
|
<PackageReference Include="System.Composition" Version="$(SystemCompositionVersion)" /> |
||||||
|
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" /> |
||||||
|
<PackageReference Include="Mono.Cecil" Version="$(MonoCecilVersion)" /> |
||||||
|
<PackageReference Include="K4os.Compression.LZ4" Version="1.2.16" /> |
||||||
|
<PackageReference Include="System.Buffers" Version="4.5.1" /> |
||||||
|
</ItemGroup> |
||||||
|
|
||||||
|
<ItemGroup> |
||||||
|
<ProjectReference Include="..\ICSharpCode.Decompiler\ICSharpCode.Decompiler.csproj" /> |
||||||
|
</ItemGroup> |
||||||
|
|
||||||
|
<Target Name="ILSpyUpdateAssemblyInfo" BeforeTargets="BeforeBuild"> |
||||||
|
<ReadLinesFromFile ContinueOnError="true" File="..\VERSION"> |
||||||
|
<Output TaskParameter="Lines" PropertyName="PackageVersion" /> |
||||||
|
</ReadLinesFromFile> |
||||||
|
</Target> |
||||||
|
|
||||||
|
</Project> |
@ -0,0 +1,29 @@ |
|||||||
|
#region Using directives
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Diagnostics.CodeAnalysis; |
||||||
|
using System.Reflection; |
||||||
|
using System.Resources; |
||||||
|
using System.Runtime.CompilerServices; |
||||||
|
using System.Runtime.InteropServices; |
||||||
|
using System.Runtime.Versioning; |
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
// This sets the default COM visibility of types in the assembly to invisible.
|
||||||
|
// If you need to expose a type to COM, use [ComVisible(true)] on that type.
|
||||||
|
[assembly: ComVisible(false)] |
||||||
|
|
||||||
|
[assembly: AssemblyVersion(DecompilerVersionInfo.Major + "." + DecompilerVersionInfo.Minor + "." + DecompilerVersionInfo.Build + "." + DecompilerVersionInfo.Revision)] |
||||||
|
[assembly: AssemblyInformationalVersion(DecompilerVersionInfo.FullVersionWithShortCommitHash)] |
||||||
|
|
||||||
|
[assembly: SupportedOSPlatform("Windows7.0")] |
||||||
|
[assembly: TargetPlatform("Windows10.0")] |
||||||
|
|
||||||
|
[assembly: SuppressMessage("Microsoft.Usage", "CA2243:AttributeStringLiteralsShouldParseCorrectly", |
||||||
|
Justification = "AssemblyInformationalVersion does not need to be a parsable version")] |
||||||
|
|
||||||
|
[assembly: InternalsVisibleTo("ILSpy, PublicKey=00240000048000009400000006020000002400005253413100040000010001004dcf3979c4e902efa4dd2163a039701ed5822e6f1134d77737296abbb97bf0803083cfb2117b4f5446a217782f5c7c634f9fe1fc60b4c11d62c5b3d33545036706296d31903ddcf750875db38a8ac379512f51620bb948c94d0831125fbc5fe63707cbb93f48c1459c4d1749eb7ac5e681a2f0d6d7c60fa527a3c0b8f92b02bf")] |
||||||
|
[assembly: InternalsVisibleTo("ILSpy.Tests")] |
||||||
|
|
||||||
|
|
@ -1,36 +0,0 @@ |
|||||||
// Copyright (c) 2020 Siegfried Pammer
|
|
||||||
//
|
|
||||||
// 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.Windows; |
|
||||||
|
|
||||||
namespace ICSharpCode.ILSpy.Tests |
|
||||||
{ |
|
||||||
public static class Stub |
|
||||||
{ |
|
||||||
static readonly object sync = new object(); |
|
||||||
|
|
||||||
internal static void SetupApplication() |
|
||||||
{ |
|
||||||
lock (sync) |
|
||||||
{ |
|
||||||
if (Application.Current == null) |
|
||||||
new Application(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,192 +0,0 @@ |
|||||||
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
|
|
||||||
//
|
|
||||||
// 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.Collections.ObjectModel; |
|
||||||
using System.Linq; |
|
||||||
using System.Xml.Linq; |
|
||||||
|
|
||||||
using ICSharpCode.ILSpy.ViewModels; |
|
||||||
|
|
||||||
namespace ICSharpCode.ILSpy |
|
||||||
{ |
|
||||||
/// <summary>
|
|
||||||
/// Manages the available assembly lists.
|
|
||||||
///
|
|
||||||
/// Contains the list of list names; and provides methods for loading/saving and creating/deleting lists.
|
|
||||||
/// </summary>
|
|
||||||
sealed class AssemblyListManager |
|
||||||
{ |
|
||||||
ILSpySettings spySettings; |
|
||||||
|
|
||||||
public AssemblyListManager(ILSpySettings spySettings) |
|
||||||
{ |
|
||||||
this.spySettings = spySettings; |
|
||||||
XElement doc = spySettings["AssemblyLists"]; |
|
||||||
foreach (var list in doc.Elements("List")) |
|
||||||
{ |
|
||||||
AssemblyLists.Add((string)list.Attribute("name")); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public ObservableCollection<string> AssemblyLists { get; } = new ObservableCollection<string>(); |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Loads an assembly list from the ILSpySettings.
|
|
||||||
/// If no list with the specified name is found, the default list is loaded instead.
|
|
||||||
/// </summary>
|
|
||||||
public AssemblyList LoadList(ILSpySettings settings, string listName) |
|
||||||
{ |
|
||||||
this.spySettings = settings; |
|
||||||
AssemblyList list = DoLoadList(spySettings, listName); |
|
||||||
if (!AssemblyLists.Contains(list.ListName)) |
|
||||||
AssemblyLists.Add(list.ListName); |
|
||||||
return list; |
|
||||||
} |
|
||||||
|
|
||||||
AssemblyList DoLoadList(ILSpySettings spySettings, string listName) |
|
||||||
{ |
|
||||||
XElement doc = spySettings["AssemblyLists"]; |
|
||||||
if (listName != null) |
|
||||||
{ |
|
||||||
foreach (var list in doc.Elements("List")) |
|
||||||
{ |
|
||||||
if ((string)list.Attribute("name") == listName) |
|
||||||
{ |
|
||||||
return new AssemblyList(list); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
return new AssemblyList(listName ?? DefaultListName); |
|
||||||
} |
|
||||||
|
|
||||||
public bool CloneList(string selectedAssemblyList, string newListName) |
|
||||||
{ |
|
||||||
var list = DoLoadList(spySettings, selectedAssemblyList); |
|
||||||
var newList = new AssemblyList(list, newListName); |
|
||||||
return CreateList(newList); |
|
||||||
} |
|
||||||
|
|
||||||
public bool RenameList(string selectedAssemblyList, string newListName) |
|
||||||
{ |
|
||||||
var list = DoLoadList(spySettings, selectedAssemblyList); |
|
||||||
var newList = new AssemblyList(list, newListName); |
|
||||||
return DeleteList(selectedAssemblyList) && CreateList(newList); |
|
||||||
} |
|
||||||
|
|
||||||
public const string DefaultListName = "(Default)"; |
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Saves the specifies assembly list into the config file.
|
|
||||||
/// </summary>
|
|
||||||
public static void SaveList(AssemblyList list) |
|
||||||
{ |
|
||||||
ILSpySettings.Update( |
|
||||||
delegate (XElement root) { |
|
||||||
XElement doc = root.Element("AssemblyLists"); |
|
||||||
if (doc == null) |
|
||||||
{ |
|
||||||
doc = new XElement("AssemblyLists"); |
|
||||||
root.Add(doc); |
|
||||||
} |
|
||||||
XElement listElement = doc.Elements("List").FirstOrDefault(e => (string)e.Attribute("name") == list.ListName); |
|
||||||
if (listElement != null) |
|
||||||
listElement.ReplaceWith(list.SaveAsXml()); |
|
||||||
else |
|
||||||
doc.Add(list.SaveAsXml()); |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
public bool CreateList(AssemblyList list) |
|
||||||
{ |
|
||||||
if (!AssemblyLists.Contains(list.ListName)) |
|
||||||
{ |
|
||||||
AssemblyLists.Add(list.ListName); |
|
||||||
SaveList(list); |
|
||||||
return true; |
|
||||||
} |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
public bool DeleteList(string Name) |
|
||||||
{ |
|
||||||
if (AssemblyLists.Remove(Name)) |
|
||||||
{ |
|
||||||
ILSpySettings.Update( |
|
||||||
delegate (XElement root) { |
|
||||||
XElement doc = root.Element("AssemblyLists"); |
|
||||||
if (doc == null) |
|
||||||
{ |
|
||||||
return; |
|
||||||
} |
|
||||||
XElement listElement = doc.Elements("List").FirstOrDefault(e => (string)e.Attribute("name") == Name); |
|
||||||
if (listElement != null) |
|
||||||
listElement.Remove(); |
|
||||||
}); |
|
||||||
return true; |
|
||||||
} |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
public void ClearAll() |
|
||||||
{ |
|
||||||
AssemblyLists.Clear(); |
|
||||||
ILSpySettings.Update( |
|
||||||
delegate (XElement root) { |
|
||||||
XElement doc = root.Element("AssemblyLists"); |
|
||||||
if (doc == null) |
|
||||||
{ |
|
||||||
return; |
|
||||||
} |
|
||||||
doc.Remove(); |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
public void CreateDefaultAssemblyLists() |
|
||||||
{ |
|
||||||
if (AssemblyLists.Count > 0) |
|
||||||
return; |
|
||||||
|
|
||||||
if (!AssemblyLists.Contains(ManageAssemblyListsViewModel.DotNet4List)) |
|
||||||
{ |
|
||||||
AssemblyList dotnet4 = ManageAssemblyListsViewModel.CreateDefaultList(ManageAssemblyListsViewModel.DotNet4List); |
|
||||||
if (dotnet4.Count > 0) |
|
||||||
{ |
|
||||||
CreateList(dotnet4); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if (!AssemblyLists.Contains(ManageAssemblyListsViewModel.DotNet35List)) |
|
||||||
{ |
|
||||||
AssemblyList dotnet35 = ManageAssemblyListsViewModel.CreateDefaultList(ManageAssemblyListsViewModel.DotNet35List); |
|
||||||
if (dotnet35.Count > 0) |
|
||||||
{ |
|
||||||
CreateList(dotnet35); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if (!AssemblyLists.Contains(ManageAssemblyListsViewModel.ASPDotNetMVC3List)) |
|
||||||
{ |
|
||||||
AssemblyList mvc = ManageAssemblyListsViewModel.CreateDefaultList(ManageAssemblyListsViewModel.ASPDotNetMVC3List); |
|
||||||
if (mvc.Count > 0) |
|
||||||
{ |
|
||||||
CreateList(mvc); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,101 @@ |
|||||||
|
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
|
||||||
|
//
|
||||||
|
// 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.ComponentModel; |
||||||
|
using System.Linq; |
||||||
|
using System.Reflection; |
||||||
|
using System.Runtime.CompilerServices; |
||||||
|
|
||||||
|
using ICSharpCode.ILSpy.Properties; |
||||||
|
using ICSharpCode.ILSpy.TreeNodes; |
||||||
|
|
||||||
|
namespace ICSharpCode.ILSpy.Options |
||||||
|
{ |
||||||
|
public class DecompilerSettingsViewModel : INotifyPropertyChanged |
||||||
|
{ |
||||||
|
public CSharpDecompilerSetting[] Settings { get; set; } |
||||||
|
|
||||||
|
public DecompilerSettingsViewModel(Decompiler.DecompilerSettings settings) |
||||||
|
{ |
||||||
|
Settings = typeof(Decompiler.DecompilerSettings).GetProperties() |
||||||
|
.Where(p => p.GetCustomAttribute<BrowsableAttribute>()?.Browsable != false) |
||||||
|
.Select(p => new CSharpDecompilerSetting(p) { IsEnabled = (bool)p.GetValue(settings) }) |
||||||
|
.OrderBy(item => item.Category, NaturalStringComparer.Instance) |
||||||
|
.ThenBy(item => item.Description) |
||||||
|
.ToArray(); |
||||||
|
} |
||||||
|
|
||||||
|
public event PropertyChangedEventHandler PropertyChanged; |
||||||
|
|
||||||
|
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) |
||||||
|
{ |
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); |
||||||
|
} |
||||||
|
|
||||||
|
public Decompiler.DecompilerSettings ToDecompilerSettings() |
||||||
|
{ |
||||||
|
var settings = new Decompiler.DecompilerSettings(); |
||||||
|
foreach (var item in Settings) |
||||||
|
{ |
||||||
|
item.Property.SetValue(settings, item.IsEnabled); |
||||||
|
} |
||||||
|
return settings; |
||||||
|
} |
||||||
|
} |
||||||
|
public class CSharpDecompilerSetting : INotifyPropertyChanged |
||||||
|
{ |
||||||
|
bool isEnabled; |
||||||
|
|
||||||
|
public CSharpDecompilerSetting(PropertyInfo p) |
||||||
|
{ |
||||||
|
this.Property = p; |
||||||
|
this.Category = GetResourceString(p.GetCustomAttribute<CategoryAttribute>()?.Category ?? Resources.Other); |
||||||
|
this.Description = GetResourceString(p.GetCustomAttribute<DescriptionAttribute>()?.Description ?? p.Name); |
||||||
|
} |
||||||
|
|
||||||
|
public PropertyInfo Property { get; } |
||||||
|
|
||||||
|
public bool IsEnabled { |
||||||
|
get => isEnabled; |
||||||
|
set { |
||||||
|
if (value != isEnabled) |
||||||
|
{ |
||||||
|
isEnabled = value; |
||||||
|
OnPropertyChanged(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public string Description { get; set; } |
||||||
|
|
||||||
|
public string Category { get; set; } |
||||||
|
|
||||||
|
public event PropertyChangedEventHandler PropertyChanged; |
||||||
|
|
||||||
|
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) |
||||||
|
{ |
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); |
||||||
|
} |
||||||
|
|
||||||
|
static string GetResourceString(string key) |
||||||
|
{ |
||||||
|
var str = !string.IsNullOrEmpty(key) ? Resources.ResourceManager.GetString(key) : null; |
||||||
|
return string.IsNullOrEmpty(key) || string.IsNullOrEmpty(str) ? key : str; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue