-
Notifications
You must be signed in to change notification settings - Fork 365
/
ComponentAssemblyResolver.cs
142 lines (123 loc) · 5.39 KB
/
ComponentAssemblyResolver.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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
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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
//===============================================================================================================
// System : Sandcastle Tools - Sandcastle Tools Core Class Library
// File : ComponentAssemblyResolver.cs
// Author : Eric Woodruff (Eric@EWoodruff.us)
// Updated : 07/09/2021
// Note : Copyright 2021, Eric Woodruff, All rights reserved
//
// This file contains a class used to resolve assembly dependencies when loading component assemblies with MEF
//
// This code is published under the Microsoft Public License (Ms-PL). A copy of the license should be
// distributed with the code and can be found at the project website: https://GitHub.com/EWSoftware/SHFB. This
// notice, the author's name, and all copyright notices must remain intact in all applications, documentation,
// and source files.
//
// Date Who Comments
// ==============================================================================================================
// 07/09/2021 EFW Created the code
//===============================================================================================================
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
namespace Sandcastle.Core
{
/// <summary>
/// This is used to resolve assembly dependencies when loading component assemblies with MEF
/// </summary>
public sealed class ComponentAssemblyResolver : IDisposable
{
#region Private data members
//=====================================================================
private readonly HashSet<string> componentFolders;
#endregion
#region Constructor
//=====================================================================
/// <summary>
/// Constructor
/// </summary>
public ComponentAssemblyResolver()
{
// This will be used to track folders containing components so that we can search them for dependency
// assemblies later on. We'll search the tools folder as well.
componentFolders = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)
};
// When ran as an MSBuild task, it won't always find dependent assemblies so we must find them
// manually.
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
}
#endregion
#region IDisposable implementation
//=====================================================================
/// <summary>
/// This handles garbage collection to ensure proper disposal of the resolver if not done explicitly
/// with <see cref="Dispose()"/>.
/// </summary>
~ComponentAssemblyResolver()
{
this.Dispose();
}
/// <summary>
/// This implements the Dispose() interface to properly dispose of the resolver
/// </summary>
public void Dispose()
{
AppDomain.CurrentDomain.AssemblyResolve -= CurrentDomain_AssemblyResolve;
GC.SuppressFinalize(this);
}
#endregion
#region Helper methods
//=====================================================================
/// <summary>
/// Add a folder to check for dependencies
/// </summary>
/// <param name="folder">The folder to check</param>
public void AddFolder(string folder)
{
componentFolders.Add(folder);
}
#endregion
#region Event handlers
//=====================================================================
/// <summary>
/// This is handled to resolve dependent assemblies and load them when necessary
/// </summary>
/// <param name="sender">The sender of the event</param>
/// <param name="args">The event arguments</param>
/// <returns>The loaded assembly or null if not found</returns>
private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
string[] nameInfo = args.Name.Split(new char[] { ',' });
string resolveName = nameInfo[0];
// See if it has already been loaded
Assembly asm = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName == args.Name);
// If not already loaded, check for dependency assemblies in the component and build tasks folders
if(asm == null)
{
foreach(string folder in componentFolders)
{
try
{
string foundAssembly = Directory.EnumerateFiles(folder, "*.dll").FirstOrDefault(
f => resolveName.Equals(Path.GetFileNameWithoutExtension(f), StringComparison.OrdinalIgnoreCase));
if(foundAssembly != null)
{
asm = Assembly.LoadFile(foundAssembly);
break;
}
}
catch(Exception ex)
{
// Just ignore any exceptions here
System.Diagnostics.Debug.WriteLine(ex);
}
}
}
return asm;
}
#endregion
}
}