diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/AddType.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/AddType.cs index 4e6fc676432..0f795cb2a23 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/AddType.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/AddType.cs @@ -596,10 +596,24 @@ protected override void EndProcessing() #region LoadAssembly - // We now ship .Net Core's reference assemblies with PowerShell, so that Add-Type can work + // We now ship .NET Core's reference assemblies with PowerShell, so that Add-Type can work // in a predictable way and won't be broken when we move to newer version of .NET Core. - // The reference assemblies are located at '$PSHOME\ref'. - private static readonly string s_netcoreAppRefFolder = PathType.Combine(PathType.GetDirectoryName(typeof(PSObject).Assembly.Location), "ref"); + // The reference assemblies are located at '$PSHOME\ref' for pwsh. + // + // For applications that host PowerShell, the 'ref' folder will be deployed to the 'publish' + // folder, not where 'System.Management.Automation.dll' is located. So here we should use + // the entry assembly's location to construct the path to the 'ref' folder. + // For pwsh, the entry assembly is 'pwsh.dll', so the entry assembly's location is still + // $PSHOME. + // However, 'Assembly.GetEntryAssembly()' returns null when the managed code is called from + // unmanaged code (PowerShell WSMan remoting scenario), so in that case, we continue to use + // the location of 'System.Management.Automation.dll'. + private static readonly string s_netcoreAppRefFolder = PathType.Combine( + PathType.GetDirectoryName( + (Assembly.GetEntryAssembly() ?? typeof(PSObject).Assembly).Location), + "ref"); + + // Path to the folder where .NET Core runtime assemblies are located. private static readonly string s_frameworkFolder = PathType.GetDirectoryName(typeof(object).Assembly.Location); // These assemblies are always automatically added to ReferencedAssemblies. diff --git a/test/hosting/test_HostingBasic.cs b/test/hosting/test_HostingBasic.cs index 164f1f02aae..b16cd5ba0ec 100644 --- a/test/hosting/test_HostingBasic.cs +++ b/test/hosting/test_HostingBasic.cs @@ -139,5 +139,40 @@ public static void TestCommandFromNative() File.Delete(target); } } + + /// + /// Reference assemblies should be handled correctly so that Add-Type works in the hosting scenario. + /// + [Fact] + public static void TestAddTypeCmdletInHostScenario() + { + string code = @" + using System; + public class Foo + { + public Foo(string name, string path) + { + this.Name = name; + this.Path = path; + } + + public string Name; + public string Path; + } + "; + + using (System.Management.Automation.PowerShell ps = System.Management.Automation.PowerShell.Create()) + { + ps.AddCommand("Add-Type").AddParameter("TypeDefinition", code).Invoke(); + ps.Commands.Clear(); + + var results = ps.AddScript("[Foo]::new('Joe', 'Unknown')").Invoke(); + Assert.Single(results); + + dynamic foo = results[0]; + Assert.Equal("Joe", foo.Name); + Assert.Equal("Unknown", foo.Path); + } + } } } diff --git a/tools/packaging/packaging.psm1 b/tools/packaging/packaging.psm1 index f574fa2ce9c..9d644a797d9 100644 --- a/tools/packaging/packaging.psm1 +++ b/tools/packaging/packaging.psm1 @@ -1561,6 +1561,16 @@ function New-ILNugetPackage CreateNugetPlatformFolder -Platform 'unix' -PackageRuntimesFolder $packageRuntimesFolderPath -PlatformBinPath $LinuxFxdBinPath } + if ($file -eq "Microsoft.PowerShell.SDK.dll") + { + # Copy the '$PSHome\ref' folder to the NuGet package, so 'dotnet publish' can deploy the 'ref' folder to the publish folder. + # This is to make 'Add-Type' work in application that hosts PowerShell. + + $contentFolder = New-Item (Join-Path $filePackageFolder "contentFiles\any\any") -ItemType Directory -Force + $dotnetRefAsmFolder = Join-Path -Path $WinFxdBinPath -ChildPath "ref" + Copy-Item -Path $dotnetRefAsmFolder -Destination $contentFolder -Recurse -Force + } + #region nuspec # filed a tracking bug for automating generation of dependecy list: https://github.com/PowerShell/PowerShell/issues/6247 $deps = [System.Collections.ArrayList]::new()