Skip to content

Commit 96652e2

Browse files
committed
Merge pull request #280 from daviwil/RuleLoadingFix
Handle assembly loading errors at composition time
2 parents 30f3d0f + e7002c0 commit 96652e2

File tree

3 files changed

+92
-9
lines changed

3 files changed

+92
-9
lines changed

Engine/SafeDirectoryCatalog.cs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//
2+
// Copyright (c) Microsoft Corporation.
3+
//
4+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
5+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
6+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
7+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
8+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
9+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
10+
// THE SOFTWARE.
11+
//
12+
13+
using System;
14+
using System.ComponentModel.Composition;
15+
using System.ComponentModel.Composition.Hosting;
16+
using System.IO;
17+
using System.Linq;
18+
using System.Reflection;
19+
20+
namespace Microsoft.Windows.PowerShell.ScriptAnalyzer
21+
{
22+
/// <summary>
23+
/// Provides a simple DirectoryCatalog implementation that doesn't
24+
/// fail assembly loading when it encounters a ReflectionTypeLoadException.
25+
/// The default DirectoryCatalog implementation stops evaluating any
26+
/// remaining assemblies in the directory if one of these exceptions
27+
/// are encountered.
28+
/// </summary>
29+
internal class SafeDirectoryCatalog : AggregateCatalog
30+
{
31+
public SafeDirectoryCatalog(string folderLocation, IOutputWriter outputWriter)
32+
{
33+
if (outputWriter == null)
34+
{
35+
throw new ArgumentNullException("outputWriter");
36+
}
37+
38+
// Make sure the directory actually exists
39+
var directoryInfo = new DirectoryInfo(folderLocation);
40+
if (directoryInfo.Exists == false)
41+
{
42+
throw new CompositionException(
43+
"The specified folder does not exist: " + directoryInfo.FullName);
44+
}
45+
46+
// Load each DLL found in the directory
47+
foreach (var dllFile in directoryInfo.GetFileSystemInfos("*.dll"))
48+
{
49+
try
50+
{
51+
// Attempt to create an AssemblyCatalog for this DLL
52+
var assemblyCatalog =
53+
new AssemblyCatalog(
54+
Assembly.LoadFile(
55+
dllFile.FullName));
56+
57+
// We must call ToArray here to pre-initialize the Parts
58+
// IEnumerable and cause it to be stored. The result is
59+
// not used here because it will be accessed later once
60+
// the composition container starts assembling parts.
61+
assemblyCatalog.Parts.ToArray();
62+
63+
this.Catalogs.Add(assemblyCatalog);
64+
}
65+
catch (ReflectionTypeLoadException e)
66+
{
67+
// Write out the exception details and allow the
68+
// loading process to continue
69+
outputWriter.WriteWarning(e.ToString());
70+
}
71+
}
72+
}
73+
}
74+
}

Engine/ScriptAnalyzer.cs

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -357,13 +357,16 @@ private void Initialize(
357357

358358
#region Verify rules
359359

360+
// Safely get one non-duplicated list of rules
360361
IEnumerable<IRule> rules =
361-
this.ScriptRules.Union<IRule>(
362-
this.TokenRules).Union<IRule>(
362+
Enumerable.Union<IRule>(
363+
Enumerable.Union<IRule>(
364+
this.ScriptRules ?? Enumerable.Empty<IRule>(),
365+
this.TokenRules ?? Enumerable.Empty<IRule>()),
363366
this.ExternalRules ?? Enumerable.Empty<IExternalRule>());
364367

365368
// Ensure that rules were actually loaded
366-
if (rules == null || rules.Count() == 0)
369+
if (rules == null || rules.Any() == false)
367370
{
368371
this.outputWriter.ThrowTerminatingError(
369372
new ErrorRecord(
@@ -411,22 +414,24 @@ private void LoadRules(Dictionary<string, List<string>> result, CommandInvocatio
411414
{
412415
List<string> paths = new List<string>();
413416

414-
// Clear external rules for each invoke.
415-
ExternalRules = new List<ExternalRule>();
416-
417417
// Initialize helper
418418
Helper.Instance = new Helper(invokeCommand, this.outputWriter);
419419
Helper.Instance.Initialize();
420420

421421
// Clear external rules for each invoke.
422-
ExternalRules = new List<ExternalRule>();
422+
this.ScriptRules = null;
423+
this.TokenRules = null;
424+
this.ExternalRules = null;
423425

424426
// An aggregate catalog that combines multiple catalogs.
425427
using (AggregateCatalog catalog = new AggregateCatalog())
426428
{
427429
// Adds all the parts found in the same directory.
428430
string dirName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
429-
catalog.Catalogs.Add(new DirectoryCatalog(dirName));
431+
catalog.Catalogs.Add(
432+
new SafeDirectoryCatalog(
433+
dirName,
434+
this.outputWriter));
430435

431436
// Adds user specified directory
432437
paths = result.ContainsKey("ValidDllPaths") ? result["ValidDllPaths"] : result["ValidPaths"];
@@ -438,7 +443,10 @@ private void LoadRules(Dictionary<string, List<string>> result, CommandInvocatio
438443
}
439444
else
440445
{
441-
catalog.Catalogs.Add(new DirectoryCatalog(path));
446+
catalog.Catalogs.Add(
447+
new SafeDirectoryCatalog(
448+
path,
449+
this.outputWriter));
442450
}
443451
}
444452

Engine/ScriptAnalyzerEngine.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
<Compile Include="Helper.cs" />
7272
<Compile Include="IOutputWriter.cs" />
7373
<Compile Include="Loggers\WriteObjectsLogger.cs" />
74+
<Compile Include="SafeDirectoryCatalog.cs" />
7475
<Compile Include="ScriptAnalyzer.cs" />
7576
<Compile Include="SpecialVars.cs" />
7677
<Compile Include="Strings.Designer.cs">

0 commit comments

Comments
 (0)