diff --git a/src/System.Management.Automation/engine/Modules/ImportModuleCommand.cs b/src/System.Management.Automation/engine/Modules/ImportModuleCommand.cs index 745c69bbf64..21fd37e27ad 100644 --- a/src/System.Management.Automation/engine/Modules/ImportModuleCommand.cs +++ b/src/System.Management.Automation/engine/Modules/ImportModuleCommand.cs @@ -1978,6 +1978,22 @@ internal override IList ImportModulesUsingWinCompat(IEnumerable 0) + { + // make sure that we add registration only once to a multicast delegate + SyncCurrentLocationDelegate ??= SyncCurrentLocationHandler; + var alreadyregistered = this.SessionState.InvokeCommand.LocationChangedAction?.GetInvocationList().Contains(SyncCurrentLocationDelegate); + + if (!alreadyregistered ?? true) + { + this.SessionState.InvokeCommand.LocationChangedAction += SyncCurrentLocationDelegate; + + // first sync has to be triggered manually + SyncCurrentLocationHandler(sender: this, args: new LocationChangedEventArgs(sessionState: null, oldPath: null, newPath: this.SessionState.Path.CurrentLocation)); + } + } #endif return moduleProxyList; } diff --git a/src/System.Management.Automation/engine/Modules/ModuleCmdletBase.cs b/src/System.Management.Automation/engine/Modules/ModuleCmdletBase.cs index 6fc4dcd0b14..8c71d4be4b7 100644 --- a/src/System.Management.Automation/engine/Modules/ModuleCmdletBase.cs +++ b/src/System.Management.Automation/engine/Modules/ModuleCmdletBase.cs @@ -4792,7 +4792,7 @@ internal static Collection GetResolvedPathCollection(string filePath, Ex return filePaths; } - internal PSSession GetWindowsPowerShellCompatRemotingSession() + internal static PSSession GetWindowsPowerShellCompatRemotingSession() { PSSession result = null; var commandInfo = new CmdletInfo("Get-PSSession", typeof(GetPSSessionCommand)); @@ -4807,7 +4807,7 @@ internal PSSession GetWindowsPowerShellCompatRemotingSession() return result; } - internal PSSession CreateWindowsPowerShellCompatResources() + internal static PSSession CreateWindowsPowerShellCompatResources() { PSSession compatSession = null; lock (s_WindowsPowerShellCompatSyncObject) @@ -4832,13 +4832,18 @@ internal PSSession CreateWindowsPowerShellCompatResources() return compatSession; } - internal void CleanupWindowsPowerShellCompatResources() + internal static void CleanupWindowsPowerShellCompatResources(SessionState sessionState) { lock (s_WindowsPowerShellCompatSyncObject) { var compatSession = GetWindowsPowerShellCompatRemotingSession(); if (compatSession != null) { + if (sessionState?.InvokeCommand.LocationChangedAction != null) + { + sessionState.InvokeCommand.LocationChangedAction -= SyncCurrentLocationDelegate; + } + var commandInfo = new CmdletInfo("Remove-PSSession", typeof(RemovePSSessionCommand)); using var ps = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace); ps.AddCommand(commandInfo); @@ -4848,6 +4853,21 @@ internal void CleanupWindowsPowerShellCompatResources() } } + internal static void SyncCurrentLocationHandler(object sender, LocationChangedEventArgs args) + { + PSSession compatSession = GetWindowsPowerShellCompatRemotingSession(); + if (compatSession?.Runspace.RunspaceStateInfo.State == RunspaceState.Opened) + { + using var ps = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace); + ps.AddCommand(new CmdletInfo("Invoke-Command", typeof(InvokeCommandCommand))); + ps.AddParameter("Session", compatSession); + ps.AddParameter("ScriptBlock", ScriptBlock.Create(string.Format("Set-Location -Path '{0}'", args.NewPath.Path))); + ps.Invoke(); + } + } + + internal static System.EventHandler SyncCurrentLocationDelegate; + internal virtual IList ImportModulesUsingWinCompat(IEnumerable moduleNames, IEnumerable moduleFullyQualifiedNames, ImportModuleOptions importModuleOptions) { throw new System.NotImplementedException(); } private void RemoveTypesAndFormatting( @@ -4944,7 +4964,7 @@ internal void RemoveModule(PSModuleInfo module, string moduleNameInRemoveModuleC if (module.IsWindowsPowerShellCompatModule && (System.Threading.Interlocked.Decrement(ref s_WindowsPowerShellCompatUsageCounter) == 0)) { - CleanupWindowsPowerShellCompatResources(); + CleanupWindowsPowerShellCompatResources(this.SessionState); } // First remove cmdlets from the session state diff --git a/test/powershell/Modules/Microsoft.PowerShell.Core/CompatiblePSEditions.Module.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Core/CompatiblePSEditions.Module.Tests.ps1 index bbecf39d792..7ae3a77de9d 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Core/CompatiblePSEditions.Module.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Core/CompatiblePSEditions.Module.Tests.ps1 @@ -327,6 +327,34 @@ Describe "Import-Module from CompatiblePSEditions-checked paths" -Tag "CI" { [System.Management.Automation.Internal.InternalTestHooks]::SetTestHook("TestWindowsPowerShellVersionString", $null) } } + + It "Current location in Windows PS mirrors local current location" -TestCases $failCases -Skip:(-not $IsWindows) { + param($Editions, $ModuleName, $Result) + $pwdBackup = $PWD + $location = Join-Path $TestDrive "Custom dir" (New-Guid).ToString() + $null = New-Item -Path $location -ItemType Directory + Push-Location -Path $location + try + { + # right after module import remote $PWD should be synchronized + Import-Module $ModuleName -UseWindowsPowerShell + $s = Get-PSSession -Name WinPSCompatSession + (Invoke-Command -Session $s {Get-Location}).Path | Should -BeExactly $PWD.Path + + # after local $PWD changes remote $PWD should be synchronized + Set-Location -Path .. + (Invoke-Command -Session $s {Get-Location}).Path | Should -BeExactly $PWD.Path + + # after WinCompat cleanup local $PWD changes should not cause errors + Remove-module $ModuleName -Force + + Pop-Location + } + finally + { + Set-Location $pwdBackup + } + } } Context "Imports from absolute path" {