Sync current directory in WinCompat remote session (#11809)

This commit is contained in:
Andrew
2020-02-11 11:44:30 -08:00
committed by GitHub
parent 4a0eda7fa9
commit d2cb3c3636
3 changed files with 68 additions and 4 deletions
@@ -1978,6 +1978,22 @@ namespace Microsoft.PowerShell.Commands
string message = StringUtil.Format(Modules.WinCompatModuleWarning, moduleProxy.Name, WindowsPowerShellCompatRemotingSession.Name);
WriteWarning(message);
}
// register LocationChanged handler so that $PWD in Windows PS process mirrors local $PWD changes
if (moduleProxyList.Count > 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;
}
@@ -4792,7 +4792,7 @@ namespace Microsoft.PowerShell.Commands
return filePaths;
}
internal PSSession GetWindowsPowerShellCompatRemotingSession()
internal static PSSession GetWindowsPowerShellCompatRemotingSession()
{
PSSession result = null;
var commandInfo = new CmdletInfo("Get-PSSession", typeof(GetPSSessionCommand));
@@ -4807,7 +4807,7 @@ namespace Microsoft.PowerShell.Commands
return result;
}
internal PSSession CreateWindowsPowerShellCompatResources()
internal static PSSession CreateWindowsPowerShellCompatResources()
{
PSSession compatSession = null;
lock (s_WindowsPowerShellCompatSyncObject)
@@ -4832,13 +4832,18 @@ namespace Microsoft.PowerShell.Commands
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 @@ namespace Microsoft.PowerShell.Commands
}
}
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<LocationChangedEventArgs> SyncCurrentLocationDelegate;
internal virtual IList<PSModuleInfo> ImportModulesUsingWinCompat(IEnumerable<string> moduleNames, IEnumerable<ModuleSpecification> moduleFullyQualifiedNames, ImportModuleOptions importModuleOptions) { throw new System.NotImplementedException(); }
private void RemoveTypesAndFormatting(
@@ -4944,7 +4964,7 @@ namespace Microsoft.PowerShell.Commands
if (module.IsWindowsPowerShellCompatModule && (System.Threading.Interlocked.Decrement(ref s_WindowsPowerShellCompatUsageCounter) == 0))
{
CleanupWindowsPowerShellCompatResources();
CleanupWindowsPowerShellCompatResources(this.SessionState);
}
// First remove cmdlets from the session state
@@ -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" {