diff --git a/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs b/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs index b782b390177..8e008dc0ef2 100644 --- a/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs +++ b/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs @@ -910,7 +910,15 @@ public static string GetModulePath(string currentProcessModulePath, string hklmM string systemModulePathToUse = string.IsNullOrEmpty(hklmMachineModulePath) ? psHomeModulePath : hklmMachineModulePath; currentProcessModulePath = AddToPath(currentProcessModulePath, personalModulePathToUse, 0); - currentProcessModulePath = AddToPath(currentProcessModulePath, systemModulePathToUse, -1); + + int insertIndex = -1; +#if !UNIX + string windowsPowerShellModulePath = GetWindowsPowerShellPSHomeModulePath(); + // If the Windows PowerShell Module path is already present, insert the system module path + // ($PSHOME/Modules) before it. + insertIndex = PathContainsSubstring(currentProcessModulePath, windowsPowerShellModulePath); +#endif + currentProcessModulePath = AddToPath(currentProcessModulePath, systemModulePathToUse, insertIndex); } // if we reached this point - always add location to EVT.Process @@ -918,6 +926,7 @@ public static string GetModulePath(string currentProcessModulePath, string hklmM // index of $PSHome\Modules in currentProcessModulePath int indexOfPSHomeModulePath = PathContainsSubstring(currentProcessModulePath, psHomeModulePath); + // if $PSHome\Modules not found (psHomePosition == -1) - append location to the end; // if $PSHome\Modules IS found (psHomePosition >= 0) - insert location before $PSHome\Modules currentProcessModulePath = AddToPath(currentProcessModulePath, sharedModulePath, indexOfPSHomeModulePath); diff --git a/test/powershell/engine/Module/ModulePath.Tests.ps1 b/test/powershell/engine/Module/ModulePath.Tests.ps1 index 19e78354dc4..11f628f66e9 100644 --- a/test/powershell/engine/Module/ModulePath.Tests.ps1 +++ b/test/powershell/engine/Module/ModulePath.Tests.ps1 @@ -130,4 +130,33 @@ Describe "SxS Module Path Basic Tests" -tags "CI" { $paths -contains $customeModules | Should -BeTrue } + It 'Ensures $PSHOME\Modules is inserted correctly when launched from a different version of PowerShell' -Skip:(!($IsCoreCLR -and $IsWindows)) { + # When launched from a different version of PowerShell, PSModulePath contains the other version's PSHOME\Modules path + # and the Windows PowerShell modoule path. THe other version's module path should be removed and this version's + # PSHOME\Modules path should be inserted before Windows PowerShell module path. + $winpwshModulePath = [System.IO.Path]::Combine([System.Environment]::SystemDirectory, "WindowsPowerShell", "v1.0", "Modules"); + $pwshModulePath = Join-Path -Path $PSHOME -ChildPath 'Modules' + + # create a fake 'other version' $PSHOME and $PSHOME\Modules + $fakeHome = Join-Path -Path $TestDrive -ChildPath 'fakepwsh' + $fakeModulePath = Join-Path -Path $fakeHome -ChildPath 'Modules' + + $null = New-Item -Path $fakeHome -ItemType Directory + $null = New-Item -Path $fakeModulePath -ItemType Directory + + # powershell looks for these to files to determine the directory is a pwsh directory. + Set-Content -Path "$fakeHome\pwsh.exe" -Value "fake pwsh.exe" + Set-Content -Path "$fakeHome\pwsh.deps.json" -Value 'fake pwsh.deps.json' + + # replace the actual pwsh module path with the fake one. + $fakeModulePath = $env:PSModulePath.Replace($pwshModulePath, $fakeModulePath, [StringComparison]::OrdinalIgnoreCase) + + $newModulePath = & $powershell -nopro -c '$env:PSModulePath' + $pwshIndex = $newModulePath.IndexOf($pwshModulePath, [StringComparison]::OrdinalIgnoreCase) + $wpshIndex = $newModulePath.IndexOf($winpwshModulePath, [StringComparison]::OrdinalIgnoreCase) + # ensure both module paths exist and the pwsh module path occurs before the Windows PowerShell module path + $pwshIndex | Should -Not -Be -1 + $wpshIndex | Should -Not -Be -1 + $pwshIndex | Should -BeLessThan $wpshIndex + } }