├── .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 | 
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 |
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 |
--------------------------------------------------------------------------------