├── .gitignore ├── Docs ├── Releases │ └── Release notes.md └── Screenshots │ ├── output.png │ └── tool.png ├── LICENSE ├── README.md ├── ReferenceConflictAnalyser.VSExtension ├── Properties │ └── AssemblyInfo.cs ├── ReferenceConflictAnalyser.VSExtension.csproj ├── ReferenceConflictAnalyserPackage.cs ├── ReferenceConflictAnalyserPackage.vsct ├── Resources │ ├── Command1.png │ ├── Package.ico │ ├── VSPackage.resx │ ├── icon-175x175.png │ └── icon-90x90.png ├── UI │ ├── MenuCommand.cs │ ├── SelectAssemblyWindow.cs │ ├── SelectAssemblyWindowControl.xaml │ ├── SelectAssemblyWindowControl.xaml.cs │ ├── SelectAssemblyWindowViewModel.cs │ └── Utils │ │ ├── DTEHelper.cs │ │ └── GenericCommand.cs ├── dev.snk └── source.extension.vsixmanifest ├── ReferenceConflictAnalyser.sln ├── ReferenceConflictAnalyser ├── ConfigurationHelper.cs ├── DataStructures │ ├── BindingData.cs │ ├── BindingRedirectData.cs │ ├── Category.cs │ ├── ExtraNodeProperty.cs │ ├── Reference.cs │ ├── ReferenceList.cs │ └── ReferencedAssembly.cs ├── GraphBuilder.cs ├── Properties │ └── AssemblyInfo.cs ├── ReferenceAnalyser.cs ├── ReferenceConflictAnalyser.csproj ├── ReferenceReader.cs ├── Utils │ └── EnumHelper.cs ├── Workflow.cs └── dev.snk └── ReferenceConflictAnalyzer.CommandLine ├── App.config ├── GenerateDgmlFileCommand.cs ├── Program.cs ├── Properties └── AssemblyInfo.cs └── ReferenceConflictAnalyzer.CommandLine.csproj /.gitignore: -------------------------------------------------------------------------------- 1 | Debug 2 | Release 3 | /packages 4 | /.vs/ReferenceConflictAnalyser/v16/*.suo 5 | /.vs/ReferenceConflictAnalyser 6 | /ReferenceConflictAnalyser.VSExtension/obj 7 | /ReferenceConflictAnalyser.VSExtension/*.user 8 | -------------------------------------------------------------------------------- /Docs/Releases/Release notes.md: -------------------------------------------------------------------------------- 1 | # Release 1.0.3 2 | 3 | - Added support of Visual Studio 2017 4 | - Added detailed information about referenced assemblies to the graph 5 | - Added friedly names for categories 6 | - Fixed minor issue -------------------------------------------------------------------------------- /Docs/Screenshots/output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marss19/reference-conflicts-analyzer/8df0ec71315d988731c2225e71fc3445350abe3a/Docs/Screenshots/output.png -------------------------------------------------------------------------------- /Docs/Screenshots/tool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marss19/reference-conflicts-analyzer/8df0ec71315d988731c2225e71fc3445350abe3a/Docs/Screenshots/tool.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Mykola Tarasyuk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Reference Conflicts Analyzer - Visual Studio Extension 2 | 3 | This is an extension to Visual Studio for easy visual analysis of the "Could not load file or assembly or one of its dependencies" problem and issues related to referenced assemblies. The tool allows selecting an .Net assembly (.ddl or .exe file) and get a graph of all referenced assemblies with hightlighted conflicting references. 4 | 5 | After installation it is available in the main menu: Tools -> Analyze Assembly Dependencies. 6 | 7 | Download the latest release from Visual Studio Marketplace: https://marketplace.visualstudio.com/vsgallery/051172f3-4b30-4bbc-8da6-d55f70402734 8 | 9 | 10 | ##### Screenshot 1. Example of output 11 | ![alt tag](https://github.com/marss19/reference-conflicts-analyzer/blob/master/Docs/Screenshots/output.png) 12 | 13 | 14 | There is also a command line version of the analyzer. It is intended to be used on production servers without Visual Studio installed. It generates a DGML file which can be opened on a different machine where Visual Studio with DGML viewer is installed. 15 | 16 | Example of usage: 17 | ``` 18 | ReferenceConflictAnalyzer.CommandLine.exe -file="C:\Program Files\Some App\someapp.exe" -config="C:\Program Files\Some App\someapp.exe.config" -output="C:\temp" 19 | ``` 20 | 21 | More details on parameters: 22 | ``` 23 | ReferenceConflictAnalyzer.CommandLine.exe help 24 | ``` 25 | Download the latest release of the command line utility: https://github.com/marss19/reference-conflicts-analyzer/releases 26 | 27 | #### Troubleshooting. 28 | Note: The extension relies on the built-in DGML editor. In case you see the raw XML instead of the diagram run Visual Studio Installer, then: Modify -> Individual Components -> Code Tools -> Install DGML editor. 29 | -------------------------------------------------------------------------------- /ReferenceConflictAnalyser.VSExtension/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("ReferenceConflictAnalyser.VSExtension")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ReferenceConflictAnalyser.VSExtension")] 13 | [assembly: AssemblyCopyright("Copyright © 2018 Mykola Tarasyuk")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // Version information for an assembly consists of the following four values: 23 | // 24 | // Major Version 25 | // Minor Version 26 | // Build Number 27 | // Revision 28 | // 29 | // You can specify all the values or you can default the Build and Revision Numbers 30 | // by using the '*' as shown below: 31 | // [assembly: AssemblyVersion("1.0.*")] 32 | [assembly: AssemblyVersion("2.0.0.0")] 33 | [assembly: AssemblyFileVersion("2.0.0.0")] 34 | -------------------------------------------------------------------------------- /ReferenceConflictAnalyser.VSExtension/ReferenceConflictAnalyser.VSExtension.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 15.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | true 9 | 10 | 11 | 12 | 13 | 14.0 14 | 15 | 16 | 17 | true 18 | 19 | 20 | dev.snk 21 | 22 | 23 | 24 | Debug 25 | AnyCPU 26 | 2.0 27 | {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 28 | {F947F475-F430-47F3-AC07-34870DFA3996} 29 | Library 30 | Properties 31 | ReferenceConflictAnalyser.VSExtension 32 | ReferenceConflictAnalyser.VSExtension 33 | v4.7.2 34 | true 35 | true 36 | true 37 | true 38 | true 39 | false 40 | 41 | 42 | true 43 | full 44 | false 45 | bin\Debug\ 46 | DEBUG;TRACE 47 | prompt 48 | 4 49 | 50 | 51 | pdbonly 52 | true 53 | bin\Release\ 54 | TRACE 55 | prompt 56 | 4 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | SelectAssemblyWindowControl.xaml 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | Resources\LICENSE 73 | true 74 | 75 | 76 | 77 | Designer 78 | 79 | 80 | 81 | 82 | Menus.ctmenu 83 | 84 | 85 | 86 | true 87 | 88 | 89 | true 90 | 91 | 92 | 93 | 94 | 95 | {28335add-45de-44a3-97ff-0640330554ec} 96 | ReferenceConflictAnalyser 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | true 114 | VSPackage 115 | Designer 116 | 117 | 118 | 119 | 120 | Designer 121 | MSBuild:Compile 122 | 123 | 124 | 125 | 126 | 17.0.31902.203 127 | 128 | 129 | 130 | 131 | 138 | -------------------------------------------------------------------------------- /ReferenceConflictAnalyser.VSExtension/ReferenceConflictAnalyserPackage.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // Copyright (c) Company. All rights reserved. 4 | // 5 | //------------------------------------------------------------------------------ 6 | 7 | using System; 8 | using System.ComponentModel.Design; 9 | using System.Diagnostics; 10 | using System.Diagnostics.CodeAnalysis; 11 | using System.Globalization; 12 | using System.Runtime.InteropServices; 13 | using Microsoft.VisualStudio; 14 | using Microsoft.VisualStudio.OLE.Interop; 15 | using Microsoft.VisualStudio.Shell; 16 | using Microsoft.VisualStudio.Shell.Interop; 17 | using Microsoft.Win32; 18 | using EnvDTE; 19 | using ReferenceConflictAnalyser.VSExtension.UI.Utils; 20 | using System.Threading; 21 | using System.Threading.Tasks; 22 | 23 | namespace ReferenceConflictAnalyser.VSExtension 24 | { 25 | /// 26 | /// This is the class that implements the package exposed by this assembly. 27 | /// 28 | /// 29 | /// 30 | /// The minimum requirement for a class to be considered a valid package for Visual Studio 31 | /// is to implement the IVsPackage interface and register itself with the shell. 32 | /// This package uses the helper classes defined inside the Managed Package Framework (MPF) 33 | /// to do it: it derives from the Package class that provides the implementation of the 34 | /// IVsPackage interface and uses the registration attributes defined in the framework to 35 | /// register itself and its components with the shell. These attributes tell the pkgdef creation 36 | /// utility what data to put into .pkgdef file. 37 | /// 38 | /// 39 | /// To get loaded into VS, the package must be referred by <Asset Type="Microsoft.VisualStudio.VsPackage" ...> in .vsixmanifest file. 40 | /// 41 | /// 42 | [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] 43 | [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)] // Info on this package for Help/About 44 | [ProvideMenuResource("Menus.ctmenu", 1)] 45 | [Guid(ReferenceConflictAnalyserPackage.PackageGuidString)] 46 | [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "pkgdef, VS and vsixmanifest are valid VS terms")] 47 | [ProvideToolWindow(typeof(UI.SelectAssemblyWindow), Style = VsDockStyle.Float, Height = 210, Width = 600, MultiInstances = false, Transient = true)] 48 | public sealed class ReferenceConflictAnalyserPackage : AsyncPackage 49 | { 50 | /// 51 | /// ReferenceConflictAnalyserPackage GUID string. 52 | /// 53 | public const string PackageGuidString = "23edc301-292e-4c85-a285-2b65941bb8ab"; 54 | 55 | /// 56 | /// Initializes a new instance of the class. 57 | /// 58 | public ReferenceConflictAnalyserPackage() 59 | { 60 | // Inside this method you can place any initialization code that does not require 61 | // any Visual Studio service because at this point the package object is created but 62 | // not sited yet inside Visual Studio environment. The place to do all the other 63 | // initialization is the Initialize method. 64 | } 65 | 66 | #region Package Members 67 | 68 | /// 69 | /// Initialization of the package; this method is called right after the package is sited, so this is the place 70 | /// where you can put all the initialization code that rely on services provided by VisualStudio. 71 | /// 72 | protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress progress) 73 | { 74 | base.Initialize(); 75 | UI.MenuCommand.Initialize(this); 76 | 77 | DTEHelper.CurrentDTE = (DTE)((System.IServiceProvider)this).GetService(typeof(DTE)); 78 | } 79 | 80 | #endregion 81 | 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /ReferenceConflictAnalyser.VSExtension/ReferenceConflictAnalyserPackage.vsct: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 9 | 10 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 25 | 26 | 33 | 34 | 38 | 39 | 40 | 42 | 43 | 50 | 57 | 58 | 59 | 60 | 61 | 62 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /ReferenceConflictAnalyser.VSExtension/Resources/Command1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marss19/reference-conflicts-analyzer/8df0ec71315d988731c2225e71fc3445350abe3a/ReferenceConflictAnalyser.VSExtension/Resources/Command1.png -------------------------------------------------------------------------------- /ReferenceConflictAnalyser.VSExtension/Resources/Package.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marss19/reference-conflicts-analyzer/8df0ec71315d988731c2225e71fc3445350abe3a/ReferenceConflictAnalyser.VSExtension/Resources/Package.ico -------------------------------------------------------------------------------- /ReferenceConflictAnalyser.VSExtension/Resources/VSPackage.resx: -------------------------------------------------------------------------------- 1 |  2 | 12 | 13 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | text/microsoft-resx 120 | 121 | 122 | 2.0 123 | 124 | 125 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 126 | 127 | 128 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 129 | 130 | 131 | 132 | Reference Conflict Analyser Extension 133 | 134 | 135 | A tool for analysis of the "Could not load file or assembly or one of its dependencies" problem. 136 | 137 | 138 | Package.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 139 | 140 | -------------------------------------------------------------------------------- /ReferenceConflictAnalyser.VSExtension/Resources/icon-175x175.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marss19/reference-conflicts-analyzer/8df0ec71315d988731c2225e71fc3445350abe3a/ReferenceConflictAnalyser.VSExtension/Resources/icon-175x175.png -------------------------------------------------------------------------------- /ReferenceConflictAnalyser.VSExtension/Resources/icon-90x90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marss19/reference-conflicts-analyzer/8df0ec71315d988731c2225e71fc3445350abe3a/ReferenceConflictAnalyser.VSExtension/Resources/icon-90x90.png -------------------------------------------------------------------------------- /ReferenceConflictAnalyser.VSExtension/UI/MenuCommand.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // Copyright (c) Company. All rights reserved. 4 | // 5 | //------------------------------------------------------------------------------ 6 | 7 | using System; 8 | using System.ComponentModel.Design; 9 | using System.Globalization; 10 | using Microsoft.VisualStudio.Shell; 11 | using Microsoft.VisualStudio.Shell.Interop; 12 | 13 | namespace ReferenceConflictAnalyser.VSExtension.UI 14 | { 15 | /// 16 | /// Command handler 17 | /// 18 | internal sealed class MenuCommand 19 | { 20 | /// 21 | /// Command ID. 22 | /// 23 | public const int CommandId = 0x0100; 24 | 25 | /// 26 | /// Command menu group (command set GUID). 27 | /// 28 | public static readonly Guid CommandSet = new Guid("f016c470-17a2-4db5-9e3f-4177c3396288"); 29 | 30 | /// 31 | /// VS Package that provides this command, not null. 32 | /// 33 | private readonly Package package; 34 | 35 | /// 36 | /// Initializes a new instance of the class. 37 | /// Adds our command handlers for menu (commands must exist in the command table file) 38 | /// 39 | /// Owner package, not null. 40 | private MenuCommand(Package package) 41 | { 42 | if (package == null) 43 | { 44 | throw new ArgumentNullException("package"); 45 | } 46 | 47 | this.package = package; 48 | 49 | OleMenuCommandService commandService = this.ServiceProvider.GetService(typeof(IMenuCommandService)) as OleMenuCommandService; 50 | if (commandService != null) 51 | { 52 | var menuCommandID = new CommandID(CommandSet, CommandId); 53 | var menuItem = new System.ComponentModel.Design.MenuCommand(this.MenuItemCallback, menuCommandID); 54 | commandService.AddCommand(menuItem); 55 | } 56 | } 57 | 58 | /// 59 | /// Gets the instance of the command. 60 | /// 61 | public static MenuCommand Instance 62 | { 63 | get; 64 | private set; 65 | } 66 | 67 | /// 68 | /// Gets the service provider from the owner package. 69 | /// 70 | private IServiceProvider ServiceProvider 71 | { 72 | get 73 | { 74 | return this.package; 75 | } 76 | } 77 | 78 | /// 79 | /// Initializes the singleton instance of the command. 80 | /// 81 | /// Owner package, not null. 82 | public static void Initialize(Package package) 83 | { 84 | Instance = new MenuCommand(package); 85 | } 86 | 87 | /// 88 | /// This function is the callback used to execute the command when the menu item is clicked. 89 | /// See the constructor to see how the menu item is associated with this function using 90 | /// OleMenuCommandService service and MenuCommand class. 91 | /// 92 | /// Event sender. 93 | /// Event args. 94 | private void MenuItemCallback(object sender, EventArgs e) 95 | { 96 | // Get the instance number 0 of this tool window. This window is single instance so this instance 97 | // is actually the only one. 98 | // The last flag is set to true so that if the tool window does not exists it will be created. 99 | ToolWindowPane window = this.package.FindToolWindow(typeof(SelectAssemblyWindow), 0, true); 100 | if ((null == window) || (null == window.Frame)) 101 | { 102 | throw new NotSupportedException("Cannot create tool window"); 103 | } 104 | 105 | IVsWindowFrame windowFrame = (IVsWindowFrame)window.Frame; 106 | Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(windowFrame.Show()); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /ReferenceConflictAnalyser.VSExtension/UI/SelectAssemblyWindow.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // Copyright (c) Company. All rights reserved. 4 | // 5 | //------------------------------------------------------------------------------ 6 | 7 | namespace ReferenceConflictAnalyser.VSExtension.UI 8 | { 9 | using System; 10 | using System.Runtime.InteropServices; 11 | using Microsoft.VisualStudio.Shell; 12 | 13 | /// 14 | /// This class implements the tool window exposed by this package and hosts a user control. 15 | /// 16 | /// 17 | /// In Visual Studio tool windows are composed of a frame (implemented by the shell) and a pane, 18 | /// usually implemented by the package implementer. 19 | /// 20 | /// This class derives from the ToolWindowPane class provided from the MPF in order to use its 21 | /// implementation of the IVsUIElementPane interface. 22 | /// 23 | /// 24 | [Guid("64f83bf2-678c-4b70-97e1-dc1f21dd29d1")] 25 | public class SelectAssemblyWindow : ToolWindowPane 26 | { 27 | /// 28 | /// Initializes a new instance of the class. 29 | /// 30 | public SelectAssemblyWindow() : base(null) 31 | { 32 | this.Caption = "Select Assembly"; 33 | 34 | // This is the user control hosted by the tool window; Note that, even if this class implements IDisposable, 35 | // we are not calling Dispose on this object. This is because ToolWindowPane calls Dispose on 36 | // the object returned by the Content property. 37 | this.Content = new SelectAssemblyWindowControl(this); 38 | 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ReferenceConflictAnalyser.VSExtension/UI/SelectAssemblyWindowControl.xaml: -------------------------------------------------------------------------------- 1 |  11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | Assembly to analyze 29 | 30 | 31 | 32 | 33 | Related config file 34 | 35 | 36 | 37 | Ignore system assemblies 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /ReferenceConflictAnalyser.VSExtension/UI/SelectAssemblyWindowControl.xaml.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // Copyright (c) Company. All rights reserved. 4 | // 5 | //------------------------------------------------------------------------------ 6 | 7 | namespace ReferenceConflictAnalyser.VSExtension.UI 8 | { 9 | using Microsoft.VisualStudio.Shell; 10 | using System.Diagnostics.CodeAnalysis; 11 | using System.Windows; 12 | using System.Windows.Controls; 13 | 14 | /// 15 | /// Interaction logic for SelectAssemblyWindowControl. 16 | /// 17 | public partial class SelectAssemblyWindowControl : UserControl 18 | { 19 | /// 20 | /// Initializes a new instance of the class. 21 | /// 22 | public SelectAssemblyWindowControl(ToolWindowPane parentWindow) 23 | { 24 | this.InitializeComponent(); 25 | this.DataContext = new SelectAssemblyWindowViewModel(parentWindow); 26 | } 27 | 28 | /// 29 | /// Handles click on the button by displaying a message box. 30 | /// 31 | /// The event sender. 32 | /// The event args. 33 | [SuppressMessage("Microsoft.Globalization", "CA1300:SpecifyMessageBoxOptions", Justification = "Sample code")] 34 | [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:ElementMustBeginWithUpperCaseLetter", Justification = "Default event handler naming pattern")] 35 | private void button1_Click(object sender, RoutedEventArgs e) 36 | { 37 | MessageBox.Show( 38 | string.Format(System.Globalization.CultureInfo.CurrentUICulture, "Invoked '{0}'", this.ToString()), 39 | "SelectAssemblyWindow"); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /ReferenceConflictAnalyser.VSExtension/UI/SelectAssemblyWindowViewModel.cs: -------------------------------------------------------------------------------- 1 |  2 | using Microsoft.VisualStudio.Shell; 3 | using Microsoft.VisualStudio.Shell.Interop; 4 | using ReferenceConflictAnalyser.VSExtension.UI.Utils; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.ComponentModel; 8 | using System.Diagnostics; 9 | using System.IO; 10 | using System.Linq; 11 | using System.Text; 12 | using System.Threading.Tasks; 13 | using System.Windows.Forms; 14 | using System.Windows.Input; 15 | 16 | namespace ReferenceConflictAnalyser.VSExtension.UI 17 | { 18 | public class SelectAssemblyWindowViewModel : INotifyPropertyChanged 19 | { 20 | 21 | public SelectAssemblyWindowViewModel(ToolWindowPane window) 22 | { 23 | SelectAssemblyCommand = new GenericCommand(this, SelectAssembly, (vm, p) => true); 24 | SelectConfigCommand = new GenericCommand(this, SelectConfig, CanSelectConfig); 25 | AnalyzeConfigCommand = new GenericCommand(this, Analyze, CanAnalyze); 26 | 27 | IgnoreSystemAssemblies = true; 28 | 29 | Warning = "*The extension relies on the built-in DGML editor. In case you see a raw XML instead of a diagram run Visual Studio Installer, then: Modify -> Individual Components -> Code Tools -> Install DGML editor."; 30 | 31 | _window = window; 32 | } 33 | 34 | public ICommand SelectAssemblyCommand { get; private set; } 35 | public ICommand SelectConfigCommand { get; private set; } 36 | public ICommand AnalyzeConfigCommand { get; private set; } 37 | 38 | public string AssemblyPath 39 | { 40 | get { return _assemblyPath; } 41 | set { SetProperty(ref _assemblyPath, value, "AssemblyPath"); } 42 | } 43 | 44 | public string ConfigPath 45 | { 46 | get { return _configPath; } 47 | set { SetProperty(ref _configPath, value, "ConfigPath"); } 48 | } 49 | 50 | public bool IgnoreSystemAssemblies 51 | { 52 | get { return _ignoreSystemAssemblies; } 53 | set { SetProperty(ref _ignoreSystemAssemblies, value, "IgnoreSystemAssemblies"); } 54 | } 55 | 56 | public string Warning 57 | { 58 | get { return _warning; } 59 | set { SetProperty(ref _warning, value, "Warning"); } 60 | } 61 | 62 | public event PropertyChangedEventHandler PropertyChanged; 63 | 64 | #region private 65 | 66 | private string _assemblyPath; 67 | private string _configPath; 68 | private bool _ignoreSystemAssemblies; 69 | private string _warning; 70 | private ToolWindowPane _window; 71 | 72 | private void SetProperty(ref T field, T newValue, string propertyName) 73 | where T : IComparable 74 | { 75 | if ((field == null && newValue != null) 76 | || field.CompareTo(newValue) != 0) 77 | { 78 | field = newValue; 79 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 80 | } 81 | } 82 | 83 | private void SelectAssembly(SelectAssemblyWindowViewModel vm, object parameter) 84 | { 85 | var dlg = new Microsoft.Win32.OpenFileDialog(); 86 | dlg.Filter = "DLL, executable|*.dll;*.exe"; 87 | if (dlg.ShowDialog() == true) 88 | { 89 | AssemblyPath = dlg.FileName; 90 | 91 | string configPath; 92 | if (ConfigurationHelper.TrySuggestConfigFile(AssemblyPath, out configPath)) 93 | ConfigPath = configPath; 94 | else 95 | ConfigPath = ""; 96 | } 97 | } 98 | 99 | private void SelectConfig(SelectAssemblyWindowViewModel vm, object parameter) 100 | { 101 | var dlg = new Microsoft.Win32.OpenFileDialog(); 102 | dlg.Filter = "Configuration files|*.config"; 103 | dlg.InitialDirectory = Path.GetDirectoryName(AssemblyPath); 104 | if (dlg.ShowDialog() == true) 105 | { 106 | ConfigPath = dlg.FileName; 107 | } 108 | } 109 | 110 | private bool CanSelectConfig(SelectAssemblyWindowViewModel vm, object parameter) 111 | { 112 | return !string.IsNullOrWhiteSpace(AssemblyPath); 113 | } 114 | 115 | private void Analyze(SelectAssemblyWindowViewModel vm, object parameter) 116 | { 117 | RunAnalysis(); 118 | CloseWindow(); 119 | } 120 | 121 | private void CloseWindow() 122 | { 123 | ((IVsWindowFrame)_window.Frame).Hide(); 124 | } 125 | 126 | private void RunAnalysis() 127 | { 128 | try 129 | { 130 | var graphDgml = Workflow.CreateDependenciesGraph(AssemblyPath, ConfigPath, IgnoreSystemAssemblies); 131 | 132 | var path = Path.GetTempFileName(); 133 | path = Path.ChangeExtension(path, ".dgml"); 134 | File.WriteAllText(path, graphDgml); 135 | 136 | DTEHelper.OpenFile(path); 137 | } 138 | catch (Exception ex) 139 | { 140 | MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 141 | } 142 | } 143 | 144 | private bool CanAnalyze(SelectAssemblyWindowViewModel vm, object parameter) 145 | { 146 | return !string.IsNullOrWhiteSpace(AssemblyPath); 147 | } 148 | 149 | 150 | 151 | 152 | 153 | #endregion 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /ReferenceConflictAnalyser.VSExtension/UI/Utils/DTEHelper.cs: -------------------------------------------------------------------------------- 1 | using EnvDTE; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace ReferenceConflictAnalyser.VSExtension.UI.Utils 9 | { 10 | public class DTEHelper 11 | { 12 | public static DTE CurrentDTE { get; set; } 13 | 14 | public static void OpenFile(string filePath) 15 | { 16 | CurrentDTE.ItemOperations.OpenFile(filePath); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ReferenceConflictAnalyser.VSExtension/UI/Utils/GenericCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows.Input; 8 | 9 | namespace ReferenceConflictAnalyser.VSExtension.UI.Utils 10 | { 11 | public class GenericCommand : ICommand 12 | where TViewModel : INotifyPropertyChanged 13 | { 14 | Func _predicate; 15 | Action _execute; 16 | TViewModel _viewModel; 17 | 18 | public GenericCommand(TViewModel viewModel, Action execute, Func canExecute) 19 | { 20 | _viewModel = viewModel; 21 | _predicate = canExecute; 22 | _execute = execute; 23 | 24 | _viewModel.PropertyChanged += _viewModel_PropertyChanged; 25 | } 26 | 27 | private void _viewModel_PropertyChanged(object sender, PropertyChangedEventArgs e) 28 | { 29 | CanExecuteChanged?.Invoke(this, e); 30 | } 31 | 32 | public event EventHandler CanExecuteChanged; 33 | 34 | public bool CanExecute(object parameter) 35 | { 36 | return _predicate(_viewModel, (TCommandParameter)parameter); 37 | } 38 | 39 | public void Execute(object parameter) 40 | { 41 | _execute(_viewModel, (TCommandParameter)parameter); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /ReferenceConflictAnalyser.VSExtension/dev.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marss19/reference-conflicts-analyzer/8df0ec71315d988731c2225e71fc3445350abe3a/ReferenceConflictAnalyser.VSExtension/dev.snk -------------------------------------------------------------------------------- /ReferenceConflictAnalyser.VSExtension/source.extension.vsixmanifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Reference Conflicts Analyser 2022 6 | A tool for analysis of the "Could not load file or assembly or one of its dependencies" problem and issues related to conflicts of referenced assemblies. 7 | https://marketplace.visualstudio.com/items?itemName=MykolaTarasyuk.ReferenceConflictsAnalyserVS2022 8 | Resources\LICENSE 9 | Resources\icon-90x90.png 10 | Resources\icon-175x175.png 11 | 12 | 13 | 14 | amd64 15 | 16 | 17 | amd64 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /ReferenceConflictAnalyser.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26430.12 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReferenceConflictAnalyser", "ReferenceConflictAnalyser\ReferenceConflictAnalyser.csproj", "{28335ADD-45DE-44A3-97FF-0640330554EC}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReferenceConflictAnalyser.VSExtension", "ReferenceConflictAnalyser.VSExtension\ReferenceConflictAnalyser.VSExtension.csproj", "{F947F475-F430-47F3-AC07-34870DFA3996}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReferenceConflictAnalyzer.CommandLine", "ReferenceConflictAnalyzer.CommandLine\ReferenceConflictAnalyzer.CommandLine.csproj", "{41D0F74E-963C-49DC-8757-324E2B5FC995}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {28335ADD-45DE-44A3-97FF-0640330554EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {28335ADD-45DE-44A3-97FF-0640330554EC}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {28335ADD-45DE-44A3-97FF-0640330554EC}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {28335ADD-45DE-44A3-97FF-0640330554EC}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {F947F475-F430-47F3-AC07-34870DFA3996}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {F947F475-F430-47F3-AC07-34870DFA3996}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {F947F475-F430-47F3-AC07-34870DFA3996}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {F947F475-F430-47F3-AC07-34870DFA3996}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {41D0F74E-963C-49DC-8757-324E2B5FC995}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {41D0F74E-963C-49DC-8757-324E2B5FC995}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {41D0F74E-963C-49DC-8757-324E2B5FC995}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {41D0F74E-963C-49DC-8757-324E2B5FC995}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | EndGlobal 35 | -------------------------------------------------------------------------------- /ReferenceConflictAnalyser/ConfigurationHelper.cs: -------------------------------------------------------------------------------- 1 | using ReferenceConflictAnalyser.DataStructures; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Configuration; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Text.RegularExpressions; 9 | using System.Threading.Tasks; 10 | using System.Xml; 11 | using System.Xml.Linq; 12 | 13 | namespace ReferenceConflictAnalyser 14 | { 15 | public class ConfigurationHelper 16 | { 17 | public static bool TrySuggestConfigFile(string entryAssemblyFilePath, out string configFilePath) 18 | { 19 | configFilePath = null; 20 | 21 | if (entryAssemblyFilePath == null) 22 | return false; 23 | 24 | var fileExtension = Path.GetExtension(entryAssemblyFilePath).ToLower(); 25 | switch (fileExtension) 26 | { 27 | case ".exe": 28 | { 29 | var temp = entryAssemblyFilePath + ".config"; 30 | if (File.Exists(temp)) 31 | configFilePath = temp; 32 | } 33 | break; 34 | 35 | case ".dll": 36 | { 37 | var directory = Path.GetDirectoryName(entryAssemblyFilePath); 38 | var re = new Regex(@"\\bin\\?$", RegexOptions.IgnoreCase); 39 | directory = re.Replace(directory, ""); 40 | var temp = Path.Combine(directory, "web.config"); 41 | if (File.Exists(temp)) 42 | configFilePath = temp; 43 | } 44 | break; 45 | } 46 | 47 | return configFilePath != null; 48 | } 49 | 50 | public static BindingData GetBindingRedirects(string configFilePath) 51 | { 52 | if (string.IsNullOrEmpty(configFilePath) || !File.Exists(configFilePath)) 53 | return new BindingData(); 54 | 55 | var redirects = new List(); 56 | string[] subFolders = null; 57 | try 58 | { 59 | var doc = new XmlDocument(); 60 | doc.Load(configFilePath); 61 | 62 | var nsmgr = new XmlNamespaceManager(doc.NameTable); 63 | nsmgr.AddNamespace("bind", "urn:schemas-microsoft-com:asm.v1"); 64 | 65 | var probingNode = doc.SelectSingleNode("//bind:probing", nsmgr); 66 | if (probingNode != null) 67 | { 68 | var str = probingNode.Attributes["privatePath"]?.Value; 69 | if (!string.IsNullOrEmpty(str)) 70 | subFolders = str.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); 71 | } 72 | 73 | var nodes = doc.SelectNodes("//bind:dependentAssembly", nsmgr); 74 | foreach(XmlElement node in nodes) 75 | { 76 | var assemblyIdentityElem = node.GetElementsByTagName("assemblyIdentity")[0]; 77 | var bindingRedirectElem = node.GetElementsByTagName("bindingRedirect")[0]; 78 | if (assemblyIdentityElem == null || bindingRedirectElem == null) 79 | continue; 80 | 81 | var data = new BindingRedirectData() 82 | { 83 | AssemblyName = assemblyIdentityElem.Attributes["name"].Value, 84 | PublicKeyToken = assemblyIdentityElem.Attributes["publicKeyToken"]?.Value, 85 | NewVersion = new Version(bindingRedirectElem.Attributes["newVersion"].Value) 86 | }; 87 | 88 | var oldVersions = bindingRedirectElem.Attributes["oldVersion"].Value.Split('-'); 89 | data.OldVersionLowerBound = GetMainVersion(oldVersions[0]); 90 | data.OldVersionUpperBound = oldVersions.Count() > 1 ? GetMainVersion(oldVersions[1]) : data.OldVersionLowerBound; 91 | 92 | redirects.Add(data); 93 | } 94 | } 95 | catch (ConfigurationErrorsException) 96 | { 97 | } 98 | return new BindingData(redirects, subFolders); 99 | } 100 | 101 | private static Version GetMainVersion(string versionStr) 102 | { 103 | var temp = new Version(versionStr); 104 | return new Version(temp.Major, temp.Minor); 105 | } 106 | 107 | 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /ReferenceConflictAnalyser/DataStructures/BindingData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ReferenceConflictAnalyser.DataStructures 8 | { 9 | public class BindingData 10 | { 11 | public BindingData(IEnumerable bindingRedirects = null, IEnumerable subFolders = null) 12 | { 13 | BindingRedirects = bindingRedirects ?? Enumerable.Empty(); 14 | SubFolders = subFolders ?? Enumerable.Empty(); ; 15 | } 16 | 17 | public IEnumerable SubFolders { get; private set; } 18 | 19 | public IEnumerable BindingRedirects { get; private set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ReferenceConflictAnalyser/DataStructures/BindingRedirectData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ReferenceConflictAnalyser.DataStructures 8 | { 9 | public class BindingRedirectData 10 | { 11 | public string AssemblyName { get; set; } 12 | public string PublicKeyToken { get; set; } 13 | public Version OldVersionLowerBound { get; set; } 14 | public Version OldVersionUpperBound { get; set; } 15 | public Version NewVersion { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ReferenceConflictAnalyser/DataStructures/Category.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace ReferenceConflictAnalyser.DataStructures 9 | { 10 | public enum Category 11 | { 12 | [Description("Entry point for analysis")] 13 | EntryPoint, 14 | 15 | [Description("Normal reference")] 16 | Normal, 17 | 18 | [Description("Versions conflict")] 19 | VersionsConflicted, 20 | 21 | [Description("Other conflict")] 22 | OtherConflict, 23 | 24 | [Description("Versions conflict is resolved by config file")] 25 | VersionsConflictResolved, 26 | 27 | [Description("Assembly is missed or failed to load")] 28 | Missed, 29 | 30 | [Description("Detailed information")] 31 | Comment, 32 | 33 | [Description("Unused assemblies")] 34 | UnusedAssembly 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /ReferenceConflictAnalyser/DataStructures/ExtraNodeProperty.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace ReferenceConflictAnalyser.DataStructures 9 | { 10 | public enum ExtraNodeProperty 11 | { 12 | [Description("Source Node Details")] 13 | SourceNodeDetails, 14 | 15 | [Description("Target Node Details")] 16 | TargetNodeDetails, 17 | 18 | [Description("Platform Target (Processor Architecture)")] 19 | ProcessorArchitecture 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ReferenceConflictAnalyser/DataStructures/Reference.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace ReferenceConflictAnalyser.DataStructures 9 | { 10 | public class Reference 11 | { 12 | public Reference(ReferencedAssembly assembly, ReferencedAssembly referencedAssembly) 13 | { 14 | Assembly = assembly.AssemblyName; 15 | ReferencedAssembly = referencedAssembly.AssemblyName; 16 | 17 | _stringPresentation = string.Concat(Assembly.FullName, " -> ", ReferencedAssembly.FullName); 18 | } 19 | 20 | public AssemblyName Assembly { get; private set; } 21 | public AssemblyName ReferencedAssembly { get; private set; } 22 | 23 | private string _stringPresentation; 24 | 25 | public override string ToString() 26 | { 27 | return _stringPresentation; 28 | } 29 | 30 | public override bool Equals(object obj) 31 | { 32 | return _stringPresentation.Equals(obj.ToString(), StringComparison.OrdinalIgnoreCase); 33 | } 34 | 35 | public override int GetHashCode() 36 | { 37 | return _stringPresentation.GetHashCode(); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /ReferenceConflictAnalyser/DataStructures/ReferenceList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace ReferenceConflictAnalyser.DataStructures 9 | { 10 | public class ReferenceList 11 | { 12 | public void AddEntryPoint(ReferencedAssembly referencedAssembly) 13 | { 14 | _assemblies.Add(referencedAssembly); 15 | } 16 | 17 | public bool AddReference(ReferencedAssembly assembly, ReferencedAssembly referencedAssembly) 18 | { 19 | _assemblies.Add(referencedAssembly); 20 | 21 | var reference = new Reference(assembly, referencedAssembly); 22 | return _references.Add(reference); 23 | } 24 | 25 | public HashSet References => _references; 26 | public HashSet Assemblies => _assemblies; 27 | 28 | #region private members 29 | 30 | private readonly HashSet _references = new HashSet(); 31 | private readonly HashSet _assemblies = new HashSet(); 32 | 33 | #endregion 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ReferenceConflictAnalyser/DataStructures/ReferencedAssembly.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace ReferenceConflictAnalyser.DataStructures 9 | { 10 | public class ReferencedAssembly 11 | { 12 | public ReferencedAssembly(AssemblyName assemblyName) 13 | : this(assemblyName, null) 14 | { 15 | } 16 | 17 | public ReferencedAssembly(AssemblyName assemblyName, Exception loadingError) 18 | { 19 | AssemblyName = assemblyName; 20 | 21 | Name = assemblyName.Name; 22 | ProcessorArchitecture = assemblyName.ProcessorArchitecture; 23 | //PublicKeyToken = Encoding.UTF8.GetString(assemblyName.GetPublicKeyToken()).ToLowerInvariant(); 24 | Version = assemblyName.Version; 25 | 26 | 27 | Category = loadingError == null ? Category.Normal : Category.Missed; 28 | LoadingError = loadingError; 29 | PossibleLoadingErrorCauses = new List(); 30 | 31 | GenerateHashCode(); 32 | } 33 | 34 | 35 | public string Name { get; private set; } 36 | //public string PublicKeyToken { get; private set; } 37 | public Version Version { get; private set; } 38 | public AssemblyName AssemblyName { get; private set; } 39 | public Exception LoadingError { get; private set; } 40 | public List PossibleLoadingErrorCauses { get; private set; } 41 | public ProcessorArchitecture ProcessorArchitecture { get; set; } 42 | public Category Category { get; set; } 43 | public bool ProcessorArchitectureMismatch { get; set; } 44 | 45 | private int _hashCode; 46 | 47 | private void GenerateHashCode() 48 | { 49 | //TODO: add token and culture 50 | _hashCode = new { Name, Version }.GetHashCode(); 51 | } 52 | 53 | public override bool Equals(object obj) 54 | { 55 | var other = obj as ReferencedAssembly; 56 | if (other == null) 57 | return false; 58 | 59 | //TODO: add token and culture check 60 | return Name.Equals(other.Name, StringComparison.OrdinalIgnoreCase) && Version == Version; 61 | } 62 | 63 | public override int GetHashCode() 64 | { 65 | return _hashCode; 66 | } 67 | 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /ReferenceConflictAnalyser/GraphBuilder.cs: -------------------------------------------------------------------------------- 1 | using ReferenceConflictAnalyser.DataStructures; 2 | using ReferenceConflictAnalyser.Utils; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.ComponentModel; 6 | using System.Drawing; 7 | using System.IO; 8 | using System.Linq; 9 | using System.Reflection; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | using System.Xml; 13 | 14 | namespace ReferenceConflictAnalyser 15 | { 16 | public class GraphBuilder 17 | { 18 | 19 | public XmlDocument BuildDgml(ReferenceList referenceList) 20 | { 21 | _referenceList = referenceList; 22 | 23 | _doc = new XmlDocument(); 24 | 25 | var root = AddRootElement(); 26 | AddNodes(root); 27 | AddLinks(root); 28 | AddCategories(root); 29 | AddStyles(root); 30 | AddProperties(root); 31 | 32 | return _doc; 33 | } 34 | 35 | #region private members 36 | 37 | 38 | 39 | private const string XmlNamespace = "http://schemas.microsoft.com/vs/2009/dgml"; 40 | 41 | private readonly Dictionary _categories = new Dictionary() 42 | { 43 | { Category.EntryPoint, Color.LightGreen }, 44 | { Category.Normal , Color.MintCream }, 45 | { Category.VersionsConflicted, Color.LightSalmon }, 46 | { Category.OtherConflict, Color.Coral }, 47 | { Category.VersionsConflictResolved, Color.Khaki }, 48 | { Category.Missed, Color.Crimson }, 49 | { Category.Comment, Color.White }, 50 | { Category.UnusedAssembly, Color.Gray } 51 | }; 52 | private Color PlatformTargetMismatchBorder = Color.DarkRed; 53 | private ReferenceList _referenceList; 54 | private XmlDocument _doc; 55 | 56 | private const string UnusedGroupNodeId = "UnusedGroupNodeId"; 57 | private const string UnusedGroupNodeCommentId = "UnusedGroupNodeCommentId"; 58 | 59 | 60 | private XmlNode AddRootElement() 61 | { 62 | var root = _doc.AppendChild(CreateXmlElement("DirectedGraph", new Dictionary 63 | { 64 | { "GraphDirection", "BottomToTop"}, 65 | { "Layout", "Sugiyama"} 66 | })); 67 | return root; 68 | } 69 | 70 | private void AddNodes(XmlNode parent) 71 | { 72 | var nodesElement = parent.AppendChild(_doc.CreateElement("Nodes", XmlNamespace)); 73 | foreach (var referencedAssembly in _referenceList.Assemblies) 74 | { 75 | var nodeId = referencedAssembly.Name.ToLower(); 76 | 77 | var attributes = new Dictionary 78 | { 79 | { "Id", nodeId}, 80 | { "Label", referencedAssembly.Name}, 81 | { "Category", referencedAssembly.Category.ToString()}, 82 | { "Icon", "CodeSchema_Assembly"} 83 | }; 84 | 85 | if (referencedAssembly.ProcessorArchitecture != ProcessorArchitecture.None) 86 | { 87 | attributes.Add(ExtraNodeProperty.ProcessorArchitecture.ToString(), referencedAssembly.ProcessorArchitecture.ToString()); 88 | } 89 | nodesElement.AppendChild(CreateXmlElement("Node", attributes)); 90 | 91 | if (HasCommentNode(referencedAssembly)) 92 | { 93 | nodesElement.AppendChild(CreateXmlElement("Node", new Dictionary 94 | { 95 | { "Id", GetCommentNodeId(nodeId)}, 96 | { "Label", BuildComment(referencedAssembly)}, 97 | { "Category", Category.Comment.ToString()} 98 | })); 99 | } 100 | } 101 | 102 | // add container for unused assemblies and comment for it 103 | if (_referenceList.Assemblies.Any(x => x.Category == Category.UnusedAssembly)) 104 | { 105 | nodesElement.AppendChild(CreateXmlElement("Node", new Dictionary 106 | { 107 | { "Id", UnusedGroupNodeId }, 108 | { "Label", "Unused assemblies?" }, 109 | { "Group", "Expanded" } 110 | })); 111 | nodesElement.AppendChild(CreateXmlElement("Node", new Dictionary 112 | { 113 | { "Id", UnusedGroupNodeCommentId }, 114 | { "Label", "These assemblies are not referended by any assembly from the graph explicitly. However, they can be loaded in runtime by Assembly and AppDomain methods." }, 115 | { "Category", Category.Comment.ToString() } 116 | })); 117 | } 118 | } 119 | 120 | private string BuildComment(ReferencedAssembly referencedAssembly) 121 | { 122 | var comment = new StringBuilder(); 123 | 124 | if (referencedAssembly.LoadingError != null) 125 | { 126 | comment.AppendLine($"Error message: {referencedAssembly.LoadingError.Message}"); 127 | comment.AppendLine($"Error type: {referencedAssembly.LoadingError.GetType().Name}."); 128 | } 129 | 130 | if (referencedAssembly.PossibleLoadingErrorCauses != null && referencedAssembly.PossibleLoadingErrorCauses.Any()) 131 | { 132 | comment.AppendLine($"Details: {string.Join("; ", referencedAssembly.PossibleLoadingErrorCauses)}"); 133 | } 134 | 135 | return comment.ToString(); 136 | } 137 | 138 | private bool HasCommentNode(ReferencedAssembly referencedAssembly) 139 | { 140 | return referencedAssembly.LoadingError != null || referencedAssembly.PossibleLoadingErrorCauses.Any(); 141 | } 142 | 143 | private string GetCommentNodeId(string assemblyNodeId) 144 | { 145 | return $"{assemblyNodeId}..comment"; 146 | } 147 | 148 | private void AddLinks(XmlNode parent) 149 | { 150 | var linksElement = parent.AppendChild(_doc.CreateElement("Links", XmlNamespace)); 151 | 152 | //link assemblies 153 | foreach (var reference in _referenceList.References) 154 | linksElement.AppendChild(CreateXmlElement("Link", new Dictionary 155 | { 156 | { "Source", reference.Assembly.Name.ToLower()}, 157 | { "Target", reference.ReferencedAssembly.Name.ToLower()}, 158 | { "Label", reference.ReferencedAssembly.Version.ToString()}, 159 | { ExtraNodeProperty.SourceNodeDetails.ToString(), reference.Assembly.FullName }, 160 | { ExtraNodeProperty.TargetNodeDetails.ToString(), reference.ReferencedAssembly.FullName } 161 | })); 162 | 163 | //link comments to assemblies 164 | var assembliesWithComments = _referenceList.Assemblies.Where(HasCommentNode); 165 | foreach (var referencedAssembly in assembliesWithComments) 166 | { 167 | var nodeId = referencedAssembly.Name.ToLower(); 168 | linksElement.AppendChild(CreateXmlElement("Link", new Dictionary 169 | { 170 | { "Source", nodeId}, 171 | { "Target", GetCommentNodeId(nodeId)} 172 | })); 173 | } 174 | 175 | //group unused assemblies 176 | var unusedAssemblies = _referenceList.Assemblies.Where(x => x.Category == Category.UnusedAssembly); 177 | if (unusedAssemblies.Any()) 178 | { 179 | linksElement.AppendChild(CreateXmlElement("Link", new Dictionary 180 | { 181 | { "Source", UnusedGroupNodeId}, 182 | { "Target", UnusedGroupNodeCommentId} 183 | })); 184 | 185 | foreach (var assembly in unusedAssemblies) 186 | { 187 | linksElement.AppendChild(CreateXmlElement("Link", new Dictionary 188 | { 189 | { "Source", UnusedGroupNodeId}, 190 | { "Target", assembly.Name.ToLower()}, 191 | { "Category", "Contains" } 192 | })); 193 | 194 | if (HasCommentNode(assembly)) 195 | { 196 | linksElement.AppendChild(CreateXmlElement("Link", new Dictionary 197 | { 198 | { "Source", UnusedGroupNodeId}, 199 | { "Target", GetCommentNodeId(assembly.Name.ToLower())}, 200 | { "Category", "Contains" } 201 | })); 202 | } 203 | } 204 | } 205 | } 206 | 207 | private void AddCategories(XmlNode parent) 208 | { 209 | var categoriesElement = parent.AppendChild(_doc.CreateElement("Categories", XmlNamespace)); 210 | foreach (var category in _categories) 211 | categoriesElement.AppendChild(CreateXmlElement("Category", new Dictionary 212 | { 213 | { "Id", category.Key.ToString() }, 214 | { "Label", EnumHelper.GetDescription(category.Key)} 215 | })); 216 | } 217 | 218 | private void AddProperties(XmlNode parent) 219 | { 220 | var propertiesElement = parent.AppendChild(_doc.CreateElement("Properties", XmlNamespace)); 221 | 222 | var properties = EnumHelper.GetValuesWithDescriptions(); 223 | foreach(var property in properties) 224 | { 225 | propertiesElement.AppendChild(CreateXmlElement("Property", new Dictionary 226 | { 227 | { "Id", property.Key.ToString() }, 228 | { "DataType", "System.String" }, 229 | { "Label", property.Value } 230 | })); 231 | } 232 | } 233 | 234 | private void AddStyles(XmlNode parent) 235 | { 236 | var stylesElement = parent.AppendChild(_doc.CreateElement("Styles", XmlNamespace)); 237 | foreach (var category in _categories) 238 | { 239 | var properties = new Dictionary 240 | { 241 | { "Background", ColorTranslator.ToHtml(category.Value) } 242 | }; 243 | if (category.Key == Category.Comment) 244 | { 245 | properties.Add("MaxWidth", "300"); 246 | properties.Add("NodeRadius", "15"); 247 | properties.Add("Foreground", ColorTranslator.ToHtml(Color.Gray)); 248 | } 249 | 250 | 251 | stylesElement.AppendChild(CreateStyleElement("Node", 252 | EnumHelper.GetDescription(category.Key), 253 | $"HasCategory('{category.Key}')", 254 | properties 255 | )); 256 | } 257 | 258 | stylesElement.AppendChild(CreateStyleElement("Link", 259 | "Link to conflicted reference", 260 | $"Target.HasCategory('{Category.VersionsConflicted}')", 261 | new Dictionary 262 | { 263 | { "Stroke", ColorTranslator.ToHtml(_categories[Category.VersionsConflicted]) }, 264 | { "StrokeThickness", "3" } 265 | })); 266 | 267 | stylesElement.AppendChild(CreateStyleElement("Link", 268 | "Link to missed reference", 269 | $"Target.HasCategory('{Category.Missed}')", 270 | new Dictionary 271 | { 272 | { "Stroke", ColorTranslator.ToHtml(_categories[Category.Missed]) }, 273 | { "StrokeThickness", "3" } 274 | })); 275 | stylesElement.AppendChild(CreateStyleElement("Link", 276 | "Link to detailed information", 277 | $"Target.HasCategory('{Category.Comment}')", 278 | new Dictionary 279 | { 280 | { "StrokeDashArray", "2 2" } 281 | })); 282 | 283 | } 284 | 285 | private XmlElement CreateXmlElement(string elementName, Dictionary attributes) 286 | { 287 | var elem = _doc.CreateElement(elementName, XmlNamespace); 288 | foreach (var attibute in attributes) 289 | elem.Attributes.Append(CreateXmlAtribute(attibute.Key, attibute.Value)); 290 | return elem; 291 | } 292 | 293 | private XmlAttribute CreateXmlAtribute(string name, string value) 294 | { 295 | var attribute = _doc.CreateAttribute(name); 296 | attribute.Value = value; 297 | return attribute; 298 | } 299 | 300 | private XmlElement CreateStyleElement(string targetType, string groupLabel, string condition, Dictionary properties) 301 | { 302 | var styleElement = _doc.CreateElement("Style", XmlNamespace); 303 | styleElement.Attributes.Append(CreateXmlAtribute("TargetType", targetType)); 304 | styleElement.Attributes.Append(CreateXmlAtribute("GroupLabel", groupLabel)); 305 | 306 | var conditionElement = _doc.CreateElement("Condition", XmlNamespace); 307 | conditionElement.Attributes.Append(CreateXmlAtribute("Expression", condition)); 308 | styleElement.AppendChild(conditionElement); 309 | 310 | foreach (var property in properties) 311 | styleElement.AppendChild(CreateXmlElement("Setter", new Dictionary 312 | { 313 | { "Property", property.Key }, 314 | { "Value", property.Value } 315 | })); 316 | 317 | return styleElement; 318 | } 319 | 320 | #endregion 321 | } 322 | } 323 | -------------------------------------------------------------------------------- /ReferenceConflictAnalyser/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("ReferenceConflictAnalyser")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ReferenceConflictAnalyser")] 13 | [assembly: AssemblyCopyright("Copyright © 2018 Mykola Tarasyuk")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("28335add-45de-44a3-97ff-0640330554ec")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("2.0.0.0")] 36 | [assembly: AssemblyFileVersion("2.0.0.0")] 37 | -------------------------------------------------------------------------------- /ReferenceConflictAnalyser/ReferenceAnalyser.cs: -------------------------------------------------------------------------------- 1 | using ReferenceConflictAnalyser.DataStructures; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace ReferenceConflictAnalyser 11 | { 12 | public class ReferenceAnalyser 13 | { 14 | public ReferenceList AnalyzeReferences(ReferenceList list, IEnumerable bindingRedirects) 15 | { 16 | _referenceList = list; 17 | 18 | FindConflicts(); 19 | FindResolvedConflicts(bindingRedirects); 20 | AddExplanationToUnresolvedConflicts(); 21 | AddExplanationToLoadingErrors(); 22 | FindProcessorArchitectureMismatch(); 23 | 24 | return _referenceList; 25 | } 26 | 27 | 28 | #region private 29 | 30 | private ReferenceList _referenceList { get; set; } 31 | 32 | private void FindConflicts() 33 | { 34 | var referencedVersions = new Dictionary(); 35 | 36 | foreach (var reference in _referenceList.References) 37 | { 38 | if (referencedVersions.ContainsKey(reference.ReferencedAssembly.Name)) 39 | { 40 | if (!AreVersionCompatible(referencedVersions[reference.ReferencedAssembly.Name], reference.ReferencedAssembly.Version)) 41 | { 42 | var conflicts = _referenceList.Assemblies.Where(x => x.Name == reference.ReferencedAssembly.Name && x.Category == Category.Normal).ToArray(); 43 | foreach (var conflict in conflicts) 44 | conflict.Category = Category.VersionsConflicted; 45 | } 46 | } 47 | else 48 | { 49 | referencedVersions.Add(reference.ReferencedAssembly.Name, reference.ReferencedAssembly.Version); 50 | } 51 | } 52 | } 53 | 54 | private void FindResolvedConflicts(IEnumerable bindingRedirects) 55 | { 56 | if (bindingRedirects == null || !bindingRedirects.Any()) 57 | return; 58 | 59 | var conflicts = _referenceList.Assemblies.Where(x => x.Category == Category.VersionsConflicted).ToArray(); 60 | foreach (var conflict in conflicts) 61 | { 62 | var bindingRedirect = bindingRedirects.FirstOrDefault(x => x.AssemblyName == conflict.Name); 63 | if (bindingRedirect != null) 64 | { 65 | var mainVersion = new Version(conflict.Version.Major, conflict.Version.Minor); 66 | 67 | if (mainVersion >= bindingRedirect.OldVersionLowerBound 68 | && mainVersion <= bindingRedirect.OldVersionUpperBound) 69 | { 70 | conflict.Category = Category.VersionsConflictResolved; 71 | continue; 72 | } 73 | } 74 | } 75 | } 76 | 77 | private bool AreVersionCompatible(Version version1, Version version2) 78 | { 79 | //versions are considered compatible if they differ in build or revision only 80 | return version1.Major == version2.Major 81 | && version1.Minor == version2.Minor; 82 | } 83 | 84 | private void AddExplanationToLoadingErrors() 85 | { 86 | var failedToLoad = _referenceList.Assemblies.Where(x => x.LoadingError != null); 87 | foreach (var referencedAssembly in failedToLoad) 88 | { 89 | if (referencedAssembly.LoadingError is FileNotFoundException) 90 | { 91 | referencedAssembly.PossibleLoadingErrorCauses.Add("The assembly is missed."); 92 | } 93 | else if (referencedAssembly.LoadingError is FileLoadException) 94 | { 95 | referencedAssembly.PossibleLoadingErrorCauses.Add("The assembly file is found but cannot be loaded."); 96 | } 97 | else if (referencedAssembly.LoadingError is BadImageFormatException) 98 | { 99 | referencedAssembly.PossibleLoadingErrorCauses.AddRange(new[] 100 | { 101 | "Either the assembly was developed with a later version of the .NET Framework then one which is used to load the assembly.", 102 | "Or the assembly is not a .NET Framework assembly but an unmanaged dynamic link library or executable (such as a Windows system DLL).", 103 | "Or the assembly built as a 32-bit assembly is loaded as a 64-bit assembly, and vice versa." 104 | }); 105 | } 106 | } 107 | } 108 | 109 | private void AddExplanationToUnresolvedConflicts() 110 | { 111 | var conflicts = _referenceList.Assemblies.Where(x => x.Category == Category.VersionsConflicted).ToArray(); 112 | foreach (var conflict in conflicts) 113 | { 114 | conflict.PossibleLoadingErrorCauses.Add($"Different versions of this assembly are referenced by other assemblies. See reference links for details."); 115 | } 116 | } 117 | 118 | 119 | private void FindProcessorArchitectureMismatch() 120 | { 121 | var processorArchitecture = _referenceList.Assemblies.First(x => x.Category == Category.EntryPoint).ProcessorArchitecture; 122 | if (processorArchitecture == ProcessorArchitecture.None) 123 | return; 124 | 125 | var mismatched = Enumerable.Empty(); 126 | switch (processorArchitecture) 127 | { 128 | case ProcessorArchitecture.MSIL: 129 | mismatched = _referenceList.Assemblies 130 | .Where(x => x.ProcessorArchitecture != ProcessorArchitecture.None && x.ProcessorArchitecture != ProcessorArchitecture.MSIL); 131 | break; 132 | 133 | case ProcessorArchitecture.Amd64: 134 | case ProcessorArchitecture.Arm: 135 | case ProcessorArchitecture.IA64: 136 | case ProcessorArchitecture.X86: 137 | mismatched = _referenceList.Assemblies 138 | .Where(x => x.ProcessorArchitecture != ProcessorArchitecture.None && x.ProcessorArchitecture != ProcessorArchitecture.MSIL && x.ProcessorArchitecture != processorArchitecture); 139 | break; 140 | } 141 | 142 | foreach (var referencedAssembly in mismatched) 143 | { 144 | referencedAssembly.PossibleLoadingErrorCauses.Add($"The assembly platform target ({referencedAssembly.ProcessorArchitecture}) differs from the entry point assembly platform target ({processorArchitecture})."); 145 | if (referencedAssembly.Category == Category.Normal || referencedAssembly.Category == Category.VersionsConflictResolved) 146 | referencedAssembly.Category = Category.OtherConflict; 147 | } 148 | } 149 | 150 | 151 | #endregion 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /ReferenceConflictAnalyser/ReferenceConflictAnalyser.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {28335ADD-45DE-44A3-97FF-0640330554EC} 8 | Library 9 | Properties 10 | ReferenceConflictAnalyser 11 | ReferenceConflictAnalyser 12 | v4.7.2 13 | 512 14 | 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | true 35 | 36 | 37 | dev.snk 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 78 | -------------------------------------------------------------------------------- /ReferenceConflictAnalyser/ReferenceReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Reflection; 7 | using System.IO; 8 | using ReferenceConflictAnalyser.DataStructures; 9 | using System.Configuration; 10 | 11 | namespace ReferenceConflictAnalyser 12 | { 13 | 14 | public class ReferenceReader 15 | { 16 | public ReferenceList Read(string entryAssemblyFilePath, IEnumerable subDirectories, bool skipSystemAssemblies) 17 | { 18 | if (!File.Exists(entryAssemblyFilePath)) 19 | throw new ArgumentException(string.Format("File does not exist: {0}", entryAssemblyFilePath)); 20 | 21 | _skipSystemAssemblies = skipSystemAssemblies; 22 | _result = new ReferenceList(); 23 | _cache = new Dictionary(); 24 | 25 | var workingDirectory = Path.GetDirectoryName(entryAssemblyFilePath); 26 | _searchDirectories = new List() { workingDirectory }; 27 | try 28 | { 29 | if (subDirectories != null) 30 | _searchDirectories.AddRange(subDirectories 31 | .Select(x => Path.Combine(workingDirectory, x)) 32 | .Where(x => Directory.Exists(x))); 33 | } 34 | catch (Exception e) 35 | { 36 | throw new Exception($"\"runtime/assemblyBinding/probing\" element in the config file contains invalid paths. {e.Message}"); 37 | } 38 | 39 | AssemblyName[] entryPointReferences; 40 | var entryPoint = LoadEntryPoint(entryAssemblyFilePath, out entryPointReferences); 41 | _result.AddEntryPoint(entryPoint); 42 | 43 | ReadReferencesRecursively(entryPoint, entryPointReferences); 44 | ReadUnusedAssemblies(); 45 | 46 | return _result; 47 | } 48 | 49 | #region private members 50 | 51 | private bool _skipSystemAssemblies; 52 | private ReferenceList _result; 53 | private List _searchDirectories; 54 | private Dictionary _cache; 55 | 56 | private void ReadReferencesRecursively(ReferencedAssembly assembly, AssemblyName[] references) 57 | { 58 | foreach (var reference in references) 59 | { 60 | if (_skipSystemAssemblies 61 | && 62 | (reference.Name == "mscorlib" 63 | || reference.Name == "System" 64 | || reference.Name.StartsWith("System.")) 65 | ) 66 | continue; 67 | 68 | if (_cache.ContainsKey(reference.FullName)) 69 | { 70 | _result.AddReference(assembly, _cache[reference.FullName]); 71 | continue; 72 | } 73 | 74 | AssemblyName[] referencedAssemblyReferences; 75 | var referencedAssembly = LoadReferencedAssembly(reference, out referencedAssemblyReferences); 76 | if (referencedAssembly.Category != Category.Missed) 77 | { 78 | var isNewReference = _result.AddReference(assembly, referencedAssembly); 79 | if (isNewReference) 80 | ReadReferencesRecursively(referencedAssembly, referencedAssemblyReferences); 81 | } 82 | else 83 | { 84 | _result.AddReference(assembly, referencedAssembly); 85 | } 86 | } 87 | 88 | } 89 | 90 | private ReferencedAssembly LoadEntryPoint(string filePath, out AssemblyName[] assemblyReferences) 91 | { 92 | assemblyReferences = Assembly.ReflectionOnlyLoadFrom(filePath).GetReferencedAssemblies(); 93 | var assembly = AssemblyName.GetAssemblyName(filePath); 94 | 95 | var referencedAssembly = new ReferencedAssembly(assembly) 96 | { 97 | Category = Category.EntryPoint, 98 | }; 99 | 100 | _cache.Add(assembly.FullName, referencedAssembly); 101 | return referencedAssembly; 102 | } 103 | 104 | 105 | private ReferencedAssembly LoadReferencedAssembly(AssemblyName reference, out AssemblyName[] referencedAssemblyReferences) 106 | { 107 | referencedAssemblyReferences = null; 108 | ReferencedAssembly referencedAssembly; 109 | try 110 | { 111 | string file = null; 112 | foreach(var dir in _searchDirectories) 113 | { 114 | var files = Directory.GetFiles(dir, reference.Name + ".???", SearchOption.TopDirectoryOnly); 115 | file = files.FirstOrDefault(x => x.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) || x.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)); 116 | if (file != null) 117 | break; 118 | } 119 | 120 | if (file != null) 121 | { 122 | referencedAssemblyReferences = Assembly.ReflectionOnlyLoadFrom(file).GetReferencedAssemblies(); 123 | 124 | //read additional information from file as the assembly name loaded by the ReflectionOnly load is not complete. 125 | var temp = AssemblyName.GetAssemblyName(file); 126 | if (temp.FullName == reference.FullName) 127 | reference.ProcessorArchitecture = temp.ProcessorArchitecture; 128 | } 129 | else 130 | { 131 | referencedAssemblyReferences = Assembly.ReflectionOnlyLoad(reference.FullName).GetReferencedAssemblies(); 132 | } 133 | referencedAssembly = new ReferencedAssembly(reference); 134 | } 135 | catch (Exception e) 136 | { 137 | referencedAssembly = new ReferencedAssembly(reference, e); 138 | } 139 | 140 | if (!_cache.ContainsKey(reference.FullName)) 141 | _cache.Add(reference.FullName, referencedAssembly); 142 | 143 | return referencedAssembly; 144 | } 145 | 146 | private void ReadUnusedAssemblies() 147 | { 148 | var loadedAssemblies = _result.Assemblies.Select(x => x.Name); 149 | 150 | var allFiles = new List(); 151 | foreach(var dir in _searchDirectories) 152 | { 153 | allFiles.AddRange(Directory 154 | .GetFiles(dir, "*.exe", SearchOption.TopDirectoryOnly) 155 | .Concat(Directory 156 | .GetFiles(dir, "*.dll", SearchOption.TopDirectoryOnly))); 157 | } 158 | 159 | foreach(var filePath in allFiles) 160 | { 161 | var fileName = Path.GetFileNameWithoutExtension(filePath); 162 | 163 | if (!loadedAssemblies.Contains(fileName, StringComparer.OrdinalIgnoreCase)) 164 | { 165 | try 166 | { 167 | var assemblyName = AssemblyName.GetAssemblyName(filePath); 168 | _result.Assemblies.Add(new ReferencedAssembly(assemblyName) 169 | { 170 | Category = Category.UnusedAssembly 171 | }); 172 | } 173 | catch(Exception e) 174 | { 175 | 176 | } 177 | } 178 | } 179 | } 180 | 181 | #endregion 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /ReferenceConflictAnalyser/Utils/EnumHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace ReferenceConflictAnalyser.Utils 9 | { 10 | public class EnumHelper 11 | { 12 | public static string GetDescription(T value) 13 | { 14 | var type = typeof(T); 15 | var memInfo = type.GetMember(value.ToString()); 16 | var attributes = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false); 17 | return attributes.Count() > 0 18 | ? ((DescriptionAttribute)attributes[0]).Description 19 | : value.ToString(); 20 | } 21 | 22 | public static Dictionary GetValuesWithDescriptions() 23 | { 24 | return Enum.GetValues(typeof(T)) 25 | .Cast() 26 | .ToDictionary(x => x, x => GetDescription(x)); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ReferenceConflictAnalyser/Workflow.cs: -------------------------------------------------------------------------------- 1 | using ReferenceConflictAnalyser.DataStructures; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace ReferenceConflictAnalyser 11 | { 12 | public class Workflow 13 | { 14 | public static string CreateDependenciesGraph(string entryAssemblyFilePath, string configPath, bool skipSystemAssemblies = true) 15 | { 16 | var info = new AppDomainSetup() 17 | { 18 | ApplicationBase = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) 19 | }; 20 | 21 | AppDomain tempDomain = null; 22 | try 23 | { 24 | tempDomain = AppDomain.CreateDomain("TempAppDomain", null, info); 25 | 26 | tempDomain.SetData(EntryAssemblyFilePathKey, entryAssemblyFilePath); 27 | tempDomain.SetData(SkipSystemAssembliesKey, skipSystemAssemblies); 28 | tempDomain.SetData(ConfigPathKey, configPath); 29 | 30 | tempDomain.DoCallBack(DoWorkInTempAppDomain); 31 | 32 | var graphDgml = tempDomain.GetData(GraphKey); 33 | 34 | return (string)graphDgml; 35 | } 36 | catch (Exception e) 37 | { 38 | throw; 39 | } 40 | finally 41 | { 42 | AppDomain.Unload(tempDomain); 43 | } 44 | } 45 | 46 | #region private 47 | 48 | private const string EntryAssemblyFilePathKey = "EntryAssemblyFilePath"; 49 | private const string SkipSystemAssembliesKey = "SkipSystemAssemblies"; 50 | private const string ConfigPathKey = "configPath"; 51 | private const string GraphKey = "GraphKey"; 52 | 53 | private static void DoWorkInTempAppDomain() 54 | { 55 | var entryAssemblyFilePath = AppDomain.CurrentDomain.GetData(EntryAssemblyFilePathKey).ToString(); 56 | var configPath = AppDomain.CurrentDomain.GetData(ConfigPathKey)?.ToString(); 57 | var skipSystemAssemblies = (bool)AppDomain.CurrentDomain.GetData(SkipSystemAssembliesKey); 58 | 59 | var bindingData = ConfigurationHelper.GetBindingRedirects(configPath); 60 | 61 | var reader = new ReferenceReader(); 62 | var result = reader.Read(entryAssemblyFilePath, bindingData.SubFolders, skipSystemAssemblies); 63 | 64 | var analyser = new ReferenceAnalyser(); 65 | result = analyser.AnalyzeReferences(result, bindingData.BindingRedirects); 66 | 67 | var builder = new GraphBuilder(); 68 | var doc = builder.BuildDgml(result); 69 | 70 | AppDomain.CurrentDomain.SetData(GraphKey, doc.OuterXml); 71 | } 72 | 73 | #endregion 74 | 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /ReferenceConflictAnalyser/dev.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marss19/reference-conflicts-analyzer/8df0ec71315d988731c2225e71fc3445350abe3a/ReferenceConflictAnalyser/dev.snk -------------------------------------------------------------------------------- /ReferenceConflictAnalyzer.CommandLine/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /ReferenceConflictAnalyzer.CommandLine/GenerateDgmlFileCommand.cs: -------------------------------------------------------------------------------- 1 | using ReferenceConflictAnalyser; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Text.RegularExpressions; 8 | using System.Threading.Tasks; 9 | 10 | namespace ReferenceConflictAnalyzer.CommandLine 11 | { 12 | class GenerateDgmlFileCommand 13 | { 14 | public void Run(string[] args) 15 | { 16 | ValidateParameters(args); 17 | 18 | var dgml = Workflow.CreateDependenciesGraph(_filePath, _configPath, _ignoreSystemAssemblies); 19 | var outputFileName = Path.Combine(_outputFolder, $"{DateTime.Now.ToString("dd-MM-yyyy hh-MM-ss")}.dgml"); 20 | File.WriteAllText(outputFileName, dgml); 21 | } 22 | 23 | #region private 24 | 25 | private const string ParameterPattern = "^-(?\\w+)=(?.*)$"; 26 | private string _filePath; 27 | private string _configPath; 28 | private bool _ignoreSystemAssemblies = true; 29 | private string _outputFolder; 30 | 31 | 32 | private void ValidateParameters(string[] args) 33 | { 34 | var re = new Regex(ParameterPattern, RegexOptions.IgnoreCase); 35 | 36 | foreach (var arg in args) 37 | { 38 | var match = re.Match(arg); 39 | if (match.Success) 40 | { 41 | var value = match.Groups["value"].Value; 42 | switch (match.Groups["name"].Value) 43 | { 44 | case "file": 45 | _filePath = value; 46 | break; 47 | 48 | case "config": 49 | _configPath = value; 50 | break; 51 | 52 | case "ignoreSystemAssemblies": 53 | bool t; 54 | if (bool.TryParse(value, out t)) 55 | _ignoreSystemAssemblies = t; 56 | else 57 | throw new ArgumentException("Incorrect value of 'ignoreSystemAssemblies' parameter"); 58 | break; 59 | 60 | case "output": 61 | _outputFolder = value; 62 | break; 63 | } 64 | 65 | } 66 | } 67 | 68 | if (string.IsNullOrEmpty(_filePath)) 69 | throw new Exception("'file' parameter is mandatory."); 70 | 71 | if (string.IsNullOrEmpty(_outputFolder)) 72 | throw new Exception("'output' parameter is mandatory."); 73 | 74 | } 75 | 76 | #endregion 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /ReferenceConflictAnalyzer.CommandLine/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Text.RegularExpressions; 6 | using System.Threading.Tasks; 7 | 8 | namespace ReferenceConflictAnalyzer.CommandLine 9 | { 10 | class Program 11 | { 12 | static void Main(string[] args) 13 | { 14 | try 15 | { 16 | if (args == null 17 | || !args.Any() 18 | || args[0] == "?" 19 | || args[0] == "help") 20 | { 21 | Console.WriteLine(@"Parameters: 22 | 23 | -file - (mandatory) path to assembly (DLL or executable) to analyze; 24 | -config - (optional) path to related config file; 25 | -ignoreSystemAssemblies - (optional, default value is 'true') exclude from the analysis assemblies with names starting with 'System'; 26 | -output - (mandatory) directory to put genegated DGML file to."); 27 | Console.ReadKey(); 28 | } 29 | else 30 | new GenerateDgmlFileCommand().Run(args); 31 | } 32 | catch (Exception ex) 33 | { 34 | Console.WriteLine(ex.Message); 35 | Console.WriteLine(ex.StackTrace); 36 | Console.ReadKey(); 37 | } 38 | } 39 | 40 | 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ReferenceConflictAnalyzer.CommandLine/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("ReferenceConflictAnalyzer.CommandLine")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ReferenceConflictAnalyzer.CommandLine")] 13 | [assembly: AssemblyCopyright("Copyright © 2018 Mykola Tarasyuk")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("41d0f74e-963c-49dc-8757-324e2b5fc995")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("2.0.0.0")] 36 | [assembly: AssemblyFileVersion("2.0.0.0")] 37 | -------------------------------------------------------------------------------- /ReferenceConflictAnalyzer.CommandLine/ReferenceConflictAnalyzer.CommandLine.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 17.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | 9 | Debug 10 | AnyCPU 11 | {41D0F74E-963C-49DC-8757-324E2B5FC995} 12 | Exe 13 | ReferenceConflictAnalyzer.CommandLine 14 | ReferenceConflictAnalyzer.CommandLine 15 | v4.7.2 16 | 512 17 | 18 | 19 | 20 | AnyCPU 21 | true 22 | full 23 | false 24 | bin\Debug\ 25 | DEBUG;TRACE 26 | prompt 27 | 4 28 | 29 | 30 | AnyCPU 31 | pdbonly 32 | true 33 | bin\Release\ 34 | TRACE 35 | prompt 36 | 4 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | {28335add-45de-44a3-97ff-0640330554ec} 59 | ReferenceConflictAnalyser 60 | 61 | 62 | 63 | --------------------------------------------------------------------------------