Skip to content

Commit e3a004c

Browse files
chunqingchendaxian-dbw
authored andcommitted
Change PS to load the assembly with 'Assembly.LoadFrom' before 'Assembly.Load' when the file path is given (PowerShell#4196)
It's to solve a side-by-side problem we have with powershell core. If a .NET Core version assembly has the same name as it's .NET ancestor in GAC, then even if you specify the file path to `Import-Module`, powershell core will still load the one in GAC because it tries 'Assembly.Load' first. Now we change it to use `Assembly.LoadFrom` first when a file path is given, so it works for modules that have side-by-side assemblies.
1 parent b07f24e commit e3a004c

File tree

2 files changed

+96
-104
lines changed

2 files changed

+96
-104
lines changed

src/System.Management.Automation/engine/ExecutionContext.cs

Lines changed: 66 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -1322,110 +1322,72 @@ internal void RemoveAssembly(string name)
13221322
[SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Reflection.Assembly.LoadFrom")]
13231323
internal static Assembly LoadAssembly(string name, string filename, out Exception error)
13241324
{
1325-
// First we try to load the assembly based on the given name
1326-
1327-
Assembly loadedAssembly = null;
1328-
error = null;
1329-
1330-
string fixedName = null;
1331-
if (!String.IsNullOrEmpty(name))
1332-
{
1333-
// Remove the '.dll' if it's there...
1334-
fixedName = name.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)
1335-
? Path.GetFileNameWithoutExtension(name)
1336-
: name;
1337-
1338-
var assemblyString = Utils.IsPowerShellAssembly(fixedName)
1339-
? Utils.GetPowerShellAssemblyStrongName(fixedName)
1340-
: fixedName;
1341-
1342-
try
1343-
{
1344-
loadedAssembly = Assembly.Load(new AssemblyName(assemblyString));
1345-
}
1346-
catch (FileNotFoundException fileNotFound)
1347-
{
1348-
error = fileNotFound;
1349-
}
1350-
catch (FileLoadException fileLoadException)
1351-
{
1352-
error = fileLoadException;
1353-
// this is a legitimate error on CoreCLR for a newly emited with Add-Type assemblies
1354-
// they cannot be loaded by name, but we are only interested in importing them by path
1355-
}
1356-
catch (BadImageFormatException badImage)
1357-
{
1358-
error = badImage;
1359-
return null;
1360-
}
1361-
catch (SecurityException securityException)
1362-
{
1363-
error = securityException;
1364-
return null;
1365-
}
1366-
}
1367-
1368-
if (loadedAssembly != null)
1369-
return loadedAssembly;
1370-
1371-
if (!String.IsNullOrEmpty(filename))
1372-
{
1373-
error = null;
1374-
1375-
try
1376-
{
1377-
loadedAssembly = Assembly.LoadFrom(filename);
1378-
return loadedAssembly;
1379-
}
1380-
catch (FileNotFoundException fileNotFound)
1381-
{
1382-
error = fileNotFound;
1383-
}
1384-
catch (FileLoadException fileLoadException)
1385-
{
1386-
error = fileLoadException;
1387-
return null;
1388-
}
1389-
catch (BadImageFormatException badImage)
1390-
{
1391-
error = badImage;
1392-
return null;
1393-
}
1394-
catch (SecurityException securityException)
1395-
{
1396-
error = securityException;
1397-
return null;
1398-
}
1399-
}
1400-
1401-
#if !CORECLR// Assembly.LoadWithPartialName is not in CoreCLR. In CoreCLR, 'LoadWithPartialName' can be replaced by Assembly.Load with the help of AssemblyLoadContext.
1402-
// Finally try with partial name...
1403-
if (!String.IsNullOrEmpty(fixedName))
1404-
{
1405-
try
1406-
{
1407-
// This is a deprecated API, use of this API needs to be
1408-
// reviewed periodically.
1409-
#pragma warning disable 0618
1410-
loadedAssembly = Assembly.LoadWithPartialName(fixedName);
1411-
1412-
if (loadedAssembly != null)
1413-
{
1414-
// In the past, LoadWithPartialName would just return null in most cases when the assembly could not be found or loaded.
1415-
// In addition to this, the error was always cleared. So now, clear the error variable only if the assembly was loaded.
1416-
error = null;
1417-
}
1418-
return loadedAssembly;
1419-
}
1420-
1421-
// Expected exceptions are ArgumentNullException and BadImageFormatException. See https://msdn.microsoft.com/en-us/library/12xc5368(v=vs.110).aspx
1422-
catch (BadImageFormatException badImage)
1423-
{
1424-
error = badImage;
1425-
}
1426-
}
1427-
#endif
1428-
return null;
1325+
// First we try to load the assembly based on the filename
1326+
1327+
Assembly loadedAssembly = null;
1328+
error = null;
1329+
1330+
if (!String.IsNullOrEmpty(filename))
1331+
{
1332+
try
1333+
{
1334+
loadedAssembly = Assembly.LoadFrom(filename);
1335+
}
1336+
catch (FileNotFoundException fileNotFound)
1337+
{
1338+
error = fileNotFound;
1339+
}
1340+
catch (FileLoadException fileLoadException)
1341+
{
1342+
error = fileLoadException;
1343+
}
1344+
catch (BadImageFormatException badImage)
1345+
{
1346+
error = badImage;
1347+
}
1348+
catch (SecurityException securityException)
1349+
{
1350+
error = securityException;
1351+
}
1352+
}
1353+
else if (!String.IsNullOrEmpty(name))
1354+
{
1355+
string fixedName = null;
1356+
// Remove the '.dll' if it's there...
1357+
fixedName = name.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)
1358+
? Path.GetFileNameWithoutExtension(name)
1359+
: name;
1360+
1361+
var assemblyString = Utils.IsPowerShellAssembly(fixedName)
1362+
? Utils.GetPowerShellAssemblyStrongName(fixedName)
1363+
: fixedName;
1364+
1365+
try
1366+
{
1367+
loadedAssembly = Assembly.Load(new AssemblyName(assemblyString));
1368+
}
1369+
catch (FileNotFoundException fileNotFound)
1370+
{
1371+
error = fileNotFound;
1372+
}
1373+
catch (FileLoadException fileLoadException)
1374+
{
1375+
error = fileLoadException;
1376+
// this is a legitimate error on CoreCLR for a newly emited with Add-Type assemblies
1377+
// they cannot be loaded by name, but we are only interested in importing them by path
1378+
}
1379+
catch (BadImageFormatException badImage)
1380+
{
1381+
error = badImage;
1382+
}
1383+
catch (SecurityException securityException)
1384+
{
1385+
error = securityException;
1386+
}
1387+
}
1388+
1389+
// We either return the loaded Assembly, or return null.
1390+
return loadedAssembly;
14291391
}
14301392

14311393
/// <summary>

test/powershell/Modules/Microsoft.PowerShell.Core/Import-Module.Tests.ps1

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,33 @@ Describe "Import-Module for Binary Modules in GAC" -Tags 'CI' {
9494
}
9595
}
9696
}
97+
98+
Describe "Import-Module for Binary Modules" -Tags 'CI' {
99+
100+
It "PS should try to load the assembly from file path first" {
101+
$src = @"
102+
using System.Management.Automation; // Windows PowerShell namespace.
103+
104+
namespace ModuleCmdlets
105+
{
106+
[Cmdlet(VerbsDiagnostic.Test,"BinaryModuleCmdlet1")]
107+
public class TestBinaryModuleCmdlet1Command : Cmdlet
108+
{
109+
protected override void BeginProcessing()
110+
{
111+
WriteObject("BinaryModuleCmdlet1 exported by the ModuleCmdlets module.");
112+
}
113+
}
114+
}
115+
"@
116+
117+
Add-Type -TypeDefinition $src -OutputAssembly $TESTDRIVE\System.dll
118+
$results = powershell -noprofile -c "`$module = Import-Module $TESTDRIVE\System.dll -Passthru; `$module.ImplementingAssembly.Location; Test-BinaryModuleCmdlet1"
119+
120+
#Ignore slash format difference under windows/Unix
121+
$path = (Get-ChildItem $TESTDRIVE\System.dll).FullName
122+
$results[0] | Should Be $path
123+
$results[1] | Should BeExactly "BinaryModuleCmdlet1 exported by the ModuleCmdlets module."
124+
}
125+
}
126+

0 commit comments

Comments
 (0)