From cb1f0af9504bd1225f16051efc7e3fca012eda36 Mon Sep 17 00:00:00 2001 From: Chunqing Chen Date: Mon, 31 Jul 2017 20:22:05 -0700 Subject: [PATCH 1/3] PS should try to load the assembly from file path first before loading from assemblyName --- .../engine/ExecutionContext.cs | 170 +++++++----------- .../Import-Module.Tests.ps1 | 28 +++ 2 files changed, 94 insertions(+), 104 deletions(-) diff --git a/src/System.Management.Automation/engine/ExecutionContext.cs b/src/System.Management.Automation/engine/ExecutionContext.cs index 0200aa5920f..7d593b94c60 100644 --- a/src/System.Management.Automation/engine/ExecutionContext.cs +++ b/src/System.Management.Automation/engine/ExecutionContext.cs @@ -1323,110 +1323,72 @@ internal void RemoveAssembly(string name) [SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Reflection.Assembly.LoadFrom")] internal static Assembly LoadAssembly(string name, string filename, out Exception error) { - // First we try to load the assembly based on the given name - - Assembly loadedAssembly = null; - error = null; - - string fixedName = null; - if (!String.IsNullOrEmpty(name)) - { - // Remove the '.dll' if it's there... - fixedName = name.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) - ? Path.GetFileNameWithoutExtension(name) - : name; - - var assemblyString = Utils.IsPowerShellAssembly(fixedName) - ? Utils.GetPowerShellAssemblyStrongName(fixedName) - : fixedName; - - try - { - loadedAssembly = Assembly.Load(new AssemblyName(assemblyString)); - } - catch (FileNotFoundException fileNotFound) - { - error = fileNotFound; - } - catch (FileLoadException fileLoadException) - { - error = fileLoadException; - // this is a legitimate error on CoreCLR for a newly emited with Add-Type assemblies - // they cannot be loaded by name, but we are only interested in importing them by path - } - catch (BadImageFormatException badImage) - { - error = badImage; - return null; - } - catch (SecurityException securityException) - { - error = securityException; - return null; - } - } - - if (loadedAssembly != null) - return loadedAssembly; - - if (!String.IsNullOrEmpty(filename)) - { - error = null; - - try - { - loadedAssembly = Assembly.LoadFrom(filename); - return loadedAssembly; - } - catch (FileNotFoundException fileNotFound) - { - error = fileNotFound; - } - catch (FileLoadException fileLoadException) - { - error = fileLoadException; - return null; - } - catch (BadImageFormatException badImage) - { - error = badImage; - return null; - } - catch (SecurityException securityException) - { - error = securityException; - return null; - } - } - -#if !CORECLR// Assembly.LoadWithPartialName is not in CoreCLR. In CoreCLR, 'LoadWithPartialName' can be replaced by Assembly.Load with the help of AssemblyLoadContext. - // Finally try with partial name... - if (!String.IsNullOrEmpty(fixedName)) - { - try - { - // This is a deprecated API, use of this API needs to be - // reviewed periodically. -#pragma warning disable 0618 - loadedAssembly = Assembly.LoadWithPartialName(fixedName); - - if (loadedAssembly != null) - { - // In the past, LoadWithPartialName would just return null in most cases when the assembly could not be found or loaded. - // In addition to this, the error was always cleared. So now, clear the error variable only if the assembly was loaded. - error = null; - } - return loadedAssembly; - } - - // Expected exceptions are ArgumentNullException and BadImageFormatException. See https://msdn.microsoft.com/en-us/library/12xc5368(v=vs.110).aspx - catch (BadImageFormatException badImage) - { - error = badImage; - } - } -#endif - return null; + // First we try to load the assembly based on the filename + + Assembly loadedAssembly = null; + error = null; + + if (!String.IsNullOrEmpty(filename)) + { + try + { + loadedAssembly = Assembly.LoadFrom(filename); + } + catch (FileNotFoundException fileNotFound) + { + error = fileNotFound; + } + catch (FileLoadException fileLoadException) + { + error = fileLoadException; + } + catch (BadImageFormatException badImage) + { + error = badImage; + } + catch (SecurityException securityException) + { + error = securityException; + } + } + else if (!String.IsNullOrEmpty(name)) + { + string fixedName = null; + // Remove the '.dll' if it's there... + fixedName = name.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) + ? Path.GetFileNameWithoutExtension(name) + : name; + + var assemblyString = Utils.IsPowerShellAssembly(fixedName) + ? Utils.GetPowerShellAssemblyStrongName(fixedName) + : fixedName; + + try + { + loadedAssembly = Assembly.Load(new AssemblyName(assemblyString)); + } + catch (FileNotFoundException fileNotFound) + { + error = fileNotFound; + } + catch (FileLoadException fileLoadException) + { + error = fileLoadException; + // this is a legitimate error on CoreCLR for a newly emited with Add-Type assemblies + // they cannot be loaded by name, but we are only interested in importing them by path + } + catch (BadImageFormatException badImage) + { + error = badImage; + } + catch (SecurityException securityException) + { + error = securityException; + } + } + + // We either return the loaded Assembly, or return null. + return loadedAssembly; } /// diff --git a/test/powershell/Modules/Microsoft.PowerShell.Core/Import-Module.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Core/Import-Module.Tests.ps1 index f56e52a93cc..6b2ac729ad4 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Core/Import-Module.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Core/Import-Module.Tests.ps1 @@ -88,3 +88,31 @@ Describe "Import-Module for Binary Modules in GAC" -Tags 'CI' { } } } + +Describe "Import-Module for Binary Modules" -Tags 'CI' { + + It "PS should try to load the assembly from file path first" { + $src = @" +using System.Management.Automation; // Windows PowerShell namespace. + +namespace ModuleCmdlets +{ + [Cmdlet(VerbsDiagnostic.Test,"BinaryModuleCmdlet1")] + public class TestBinaryModuleCmdlet1Command : Cmdlet + { + protected override void BeginProcessing() + { + WriteObject("BinaryModuleCmdlet1 exported by the ModuleCmdlets module."); + } + } +} +"@ + + Add-Type -TypeDefinition $src -OutputAssembly $TESTDRIVE\System.dll + $assembly = Import-Module $TESTDRIVE\System.dll -Force -PassThru + + #Ignore slash format difference under windows/Unix + $path = (Get-ChildItem $TESTDRIVE\System.dll).FullName + $assembly.Path | Should be $path + } + } From 192de6620fdd8956c0a255c1e66994a92fce6ce2 Mon Sep 17 00:00:00 2001 From: Chunqing Chen Date: Wed, 27 Sep 2017 05:09:01 -0700 Subject: [PATCH 2/3] [Feature] fix test so it would test if the temporary assembly is really loaded --- .../Modules/Microsoft.PowerShell.Core/Import-Module.Tests.ps1 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/powershell/Modules/Microsoft.PowerShell.Core/Import-Module.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Core/Import-Module.Tests.ps1 index 6b2ac729ad4..19a6f446665 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Core/Import-Module.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Core/Import-Module.Tests.ps1 @@ -114,5 +114,7 @@ namespace ModuleCmdlets #Ignore slash format difference under windows/Unix $path = (Get-ChildItem $TESTDRIVE\System.dll).FullName $assembly.Path | Should be $path + Test-BinaryModuleCmdlet1 | Should Be "BinaryModuleCmdlet1 exported by the ModuleCmdlets module." } } + From 52ac1bbba3be24fc147e5a0a8e0782096f43ac62 Mon Sep 17 00:00:00 2001 From: Chunqing Chen Date: Thu, 28 Sep 2017 09:01:10 -0700 Subject: [PATCH 3/3] [Feature] fix test so it would test if the temporary assembly is really loaded 2 --- .../Microsoft.PowerShell.Core/Import-Module.Tests.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/powershell/Modules/Microsoft.PowerShell.Core/Import-Module.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Core/Import-Module.Tests.ps1 index 19a6f446665..84f6d716f7a 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Core/Import-Module.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Core/Import-Module.Tests.ps1 @@ -109,12 +109,12 @@ namespace ModuleCmdlets "@ Add-Type -TypeDefinition $src -OutputAssembly $TESTDRIVE\System.dll - $assembly = Import-Module $TESTDRIVE\System.dll -Force -PassThru + $results = powershell -noprofile -c "`$module = Import-Module $TESTDRIVE\System.dll -Passthru; `$module.ImplementingAssembly.Location; Test-BinaryModuleCmdlet1" #Ignore slash format difference under windows/Unix $path = (Get-ChildItem $TESTDRIVE\System.dll).FullName - $assembly.Path | Should be $path - Test-BinaryModuleCmdlet1 | Should Be "BinaryModuleCmdlet1 exported by the ModuleCmdlets module." + $results[0] | Should Be $path + $results[1] | Should BeExactly "BinaryModuleCmdlet1 exported by the ModuleCmdlets module." } }