8
8
using Microsoft . PowerShell . EditorServices . Services . PowerShell ;
9
9
using Microsoft . PowerShell . EditorServices . Services . PowerShell . Execution ;
10
10
using Microsoft . PowerShell . EditorServices . Services . PowerShell . Runspace ;
11
+ using Microsoft . PowerShell . EditorServices . Services . PowerShell . Utility ;
12
+ using Microsoft . PowerShell . EditorServices . Utility ;
11
13
using System ;
12
14
using System . Collections . Generic ;
13
15
using System . Diagnostics ;
18
20
using System . Text ;
19
21
using System . Threading ;
20
22
using System . Threading . Tasks ;
23
+ using SMA = System . Management . Automation ;
21
24
22
25
namespace Microsoft . PowerShell . EditorServices . Services
23
26
{
@@ -258,7 +261,7 @@ public RemoteFileManagerService(
258
261
this . logger = factory . CreateLogger < RemoteFileManagerService > ( ) ;
259
262
_runspaceContext = runspaceContext ;
260
263
_executionService = executionService ;
261
- _executionService . RunspaceChanged += HandleRunspaceChangedAsync ;
264
+ _executionService . RunspaceChanged += HandleRunspaceChanged ;
262
265
263
266
this . editorOperations = editorOperations ;
264
267
@@ -274,7 +277,7 @@ public RemoteFileManagerService(
274
277
275
278
// TODO: Do this somewhere other than the constructor and make it async
276
279
// Register the psedit function in the current runspace
277
- this . RegisterPSEditFunction ( _runspaceContext . CurrentRunspace ) ;
280
+ this . RegisterPSEditFunctionAsync ( ) . HandleErrorsAsync ( logger ) ;
278
281
}
279
282
280
283
#endregion
@@ -505,10 +508,9 @@ private void StoreRemoteFile(
505
508
506
509
private RemotePathMappings GetPathMappings ( IRunspaceInfo runspaceInfo )
507
510
{
508
- RemotePathMappings remotePathMappings = null ;
509
511
string computerName = runspaceInfo . SessionDetails . ComputerName ;
510
512
511
- if ( ! this . filesPerComputer . TryGetValue ( computerName , out remotePathMappings ) )
513
+ if ( ! this . filesPerComputer . TryGetValue ( computerName , out RemotePathMappings remotePathMappings ) )
512
514
{
513
515
remotePathMappings = new RemotePathMappings ( runspaceInfo , this ) ;
514
516
this . filesPerComputer . Add ( computerName , remotePathMappings ) ;
@@ -517,28 +519,33 @@ private RemotePathMappings GetPathMappings(IRunspaceInfo runspaceInfo)
517
519
return remotePathMappings ;
518
520
}
519
521
520
- private async void HandleRunspaceChangedAsync ( object sender , RunspaceChangedEventArgs e )
522
+ private void HandleRunspaceChanged ( object sender , RunspaceChangedEventArgs e )
521
523
{
522
524
if ( e . ChangeAction == RunspaceChangeAction . Enter )
523
525
{
524
- this . RegisterPSEditFunction ( e . NewRunspace ) ;
526
+ this . RegisterPSEditFunction ( e . NewRunspace . Runspace ) ;
525
527
return ;
526
528
}
527
529
528
530
// Close any remote files that were opened
529
- if ( e . PreviousRunspace . IsOnRemoteMachine &&
530
- ( e . ChangeAction == RunspaceChangeAction . Shutdown ||
531
- ! string . Equals (
532
- e . NewRunspace . SessionDetails . ComputerName ,
533
- e . PreviousRunspace . SessionDetails . ComputerName ,
534
- StringComparison . CurrentCultureIgnoreCase ) ) )
531
+ if ( ShouldTearDownRemoteFiles ( e ) )
535
532
{
536
533
RemotePathMappings remotePathMappings ;
537
534
if ( this . filesPerComputer . TryGetValue ( e . PreviousRunspace . SessionDetails . ComputerName , out remotePathMappings ) )
538
535
{
536
+ var fileCloseTasks = new List < Task > ( ) ;
539
537
foreach ( string remotePath in remotePathMappings . OpenedPaths )
540
538
{
541
- await ( this . editorOperations ? . CloseFileAsync ( remotePath ) ) . ConfigureAwait ( false ) ;
539
+ fileCloseTasks . Add ( this . editorOperations ? . CloseFileAsync ( remotePath ) ) ;
540
+ }
541
+
542
+ try
543
+ {
544
+ Task . WaitAll ( fileCloseTasks . ToArray ( ) ) ;
545
+ }
546
+ catch ( Exception ex )
547
+ {
548
+ this . logger . LogError ( ex , "Unable to close all files in closed runspace" ) ;
542
549
}
543
550
}
544
551
}
@@ -549,139 +556,166 @@ private async void HandleRunspaceChangedAsync(object sender, RunspaceChangedEven
549
556
}
550
557
}
551
558
559
+ private static bool ShouldTearDownRemoteFiles ( RunspaceChangedEventArgs runspaceChangedEvent )
560
+ {
561
+ if ( ! runspaceChangedEvent . PreviousRunspace . IsOnRemoteMachine )
562
+ {
563
+ return false ;
564
+ }
565
+
566
+ if ( runspaceChangedEvent . ChangeAction == RunspaceChangeAction . Shutdown )
567
+ {
568
+ return true ;
569
+ }
570
+
571
+ // Check to see if the runspace we're changing to is on a different machine to the one we left
572
+ return ! string . Equals (
573
+ runspaceChangedEvent . NewRunspace . SessionDetails . ComputerName ,
574
+ runspaceChangedEvent . PreviousRunspace . SessionDetails . ComputerName ,
575
+ StringComparison . CurrentCultureIgnoreCase ) ;
576
+ }
577
+
552
578
private async void HandlePSEventReceivedAsync ( object sender , PSEventArgs args )
553
579
{
554
- if ( string . Equals ( RemoteSessionOpenFile , args . SourceIdentifier , StringComparison . CurrentCultureIgnoreCase ) )
580
+ if ( ! string . Equals ( RemoteSessionOpenFile , args . SourceIdentifier , StringComparison . CurrentCultureIgnoreCase ) )
555
581
{
556
- try
582
+ return ;
583
+ }
584
+
585
+ try
586
+ {
587
+ if ( args . SourceArgs . Length >= 1 )
557
588
{
558
- if ( args . SourceArgs . Length >= 1 )
589
+ string localFilePath = string . Empty ;
590
+ string remoteFilePath = args . SourceArgs [ 0 ] as string ;
591
+
592
+ // Is this a local process runspace? Treat as a local file
593
+ if ( ! _runspaceContext . CurrentRunspace . IsOnRemoteMachine )
559
594
{
560
- string localFilePath = string . Empty ;
561
- string remoteFilePath = args . SourceArgs [ 0 ] as string ;
595
+ localFilePath = remoteFilePath ;
596
+ }
597
+ else
598
+ {
599
+ byte [ ] fileContent = null ;
562
600
563
- // Is this a local process runspace? Treat as a local file
564
- if ( ! _runspaceContext . CurrentRunspace . IsOnRemoteMachine )
565
- {
566
- localFilePath = remoteFilePath ;
567
- }
568
- else
601
+ if ( args . SourceArgs . Length >= 2 )
569
602
{
570
- byte [ ] fileContent = null ;
571
-
572
- if ( args . SourceArgs . Length >= 2 )
603
+ // Try to cast as a PSObject to get the BaseObject, if not, then try to case as a byte[]
604
+ PSObject sourceObj = args . SourceArgs [ 1 ] as PSObject ;
605
+ if ( sourceObj != null )
573
606
{
574
- // Try to cast as a PSObject to get the BaseObject, if not, then try to case as a byte[]
575
- PSObject sourceObj = args . SourceArgs [ 1 ] as PSObject ;
576
- if ( sourceObj != null )
577
- {
578
- fileContent = sourceObj . BaseObject as byte [ ] ;
579
- }
580
- else
581
- {
582
- fileContent = args . SourceArgs [ 1 ] as byte [ ] ;
583
- }
584
- }
585
-
586
- // If fileContent is still null after trying to
587
- // unpack the contents, just return an empty byte
588
- // array.
589
- fileContent = fileContent ?? Array . Empty < byte > ( ) ;
590
-
591
- if ( remoteFilePath != null )
592
- {
593
- localFilePath =
594
- this . StoreRemoteFile (
595
- remoteFilePath ,
596
- fileContent ,
597
- _runspaceContext . CurrentRunspace ) ;
607
+ fileContent = sourceObj . BaseObject as byte [ ] ;
598
608
}
599
609
else
600
610
{
601
- await ( this . editorOperations ? . NewFileAsync ( ) ) . ConfigureAwait ( false ) ;
602
- EditorContext context = await ( editorOperations ? . GetEditorContextAsync ( ) ) . ConfigureAwait ( false ) ;
603
- context ? . CurrentFile . InsertText ( Encoding . UTF8 . GetString ( fileContent , 0 , fileContent . Length ) ) ;
611
+ fileContent = args . SourceArgs [ 1 ] as byte [ ] ;
604
612
}
605
613
}
606
614
607
- bool preview = true ;
608
- if ( args . SourceArgs . Length >= 3 )
615
+ // If fileContent is still null after trying to
616
+ // unpack the contents, just return an empty byte
617
+ // array.
618
+ fileContent = fileContent ?? Array . Empty < byte > ( ) ;
619
+
620
+ if ( remoteFilePath != null )
609
621
{
610
- bool ? previewCheck = args . SourceArgs [ 2 ] as bool ? ;
611
- preview = previewCheck ?? true ;
622
+ localFilePath =
623
+ this . StoreRemoteFile (
624
+ remoteFilePath ,
625
+ fileContent ,
626
+ _runspaceContext . CurrentRunspace ) ;
612
627
}
628
+ else
629
+ {
630
+ await ( this . editorOperations ? . NewFileAsync ( ) ) . ConfigureAwait ( false ) ;
631
+ EditorContext context = await ( editorOperations ? . GetEditorContextAsync ( ) ) . ConfigureAwait ( false ) ;
632
+ context ? . CurrentFile . InsertText ( Encoding . UTF8 . GetString ( fileContent , 0 , fileContent . Length ) ) ;
633
+ }
634
+ }
613
635
614
- // Open the file in the editor
615
- this . editorOperations ? . OpenFileAsync ( localFilePath , preview ) ;
636
+ bool preview = true ;
637
+ if ( args . SourceArgs . Length >= 3 )
638
+ {
639
+ bool ? previewCheck = args . SourceArgs [ 2 ] as bool ? ;
640
+ preview = previewCheck ?? true ;
616
641
}
617
- }
618
- catch ( NullReferenceException e )
619
- {
620
- this . logger . LogException ( "Could not store null remote file content" , e ) ;
642
+
643
+ // Open the file in the editor
644
+ await ( this . editorOperations ? . OpenFileAsync ( localFilePath , preview ) ) . ConfigureAwait ( false ) ;
621
645
}
622
646
}
647
+ catch ( NullReferenceException e )
648
+ {
649
+ this . logger . LogException ( "Could not store null remote file content" , e ) ;
650
+ }
651
+ catch ( Exception e )
652
+ {
653
+ this . logger . LogException ( "Unable to handle remote file update" , e ) ;
654
+ }
623
655
}
624
656
625
- private void RegisterPSEditFunction ( IRunspaceInfo runspaceInfo )
657
+ private Task RegisterPSEditFunctionAsync ( )
658
+ => _executionService . ExecuteDelegateAsync (
659
+ "Register psedit function" ,
660
+ ExecutionOptions . Default ,
661
+ ( pwsh , cancellationToken ) => RegisterPSEditFunction ( pwsh . Runspace ) ,
662
+ CancellationToken . None ) ;
663
+
664
+ private void RegisterPSEditFunction ( Runspace runspace )
626
665
{
627
- if ( ! runspaceInfo . IsOnRemoteMachine )
666
+ if ( ! runspace . RunspaceIsRemote )
628
667
{
629
668
return ;
630
669
}
631
670
632
- try
633
- {
634
- runspaceInfo . Runspace . Events . ReceivedEvents . PSEventReceived += HandlePSEventReceivedAsync ;
671
+ runspace . Events . ReceivedEvents . PSEventReceived += HandlePSEventReceivedAsync ;
635
672
636
- PSCommand createCommand = new PSCommand ( )
637
- . AddScript ( CreatePSEditFunctionScript )
638
- . AddParameter ( "PSEditModule" , PSEditModule ) ;
673
+ PSCommand createCommand = new PSCommand ( )
674
+ . AddScript ( CreatePSEditFunctionScript )
675
+ . AddParameter ( "PSEditModule" , PSEditModule ) ;
639
676
640
- if ( runspaceInfo . RunspaceOrigin == RunspaceOrigin . DebuggedRunspace )
641
- {
642
- _executionService . ExecutePSCommandAsync ( createCommand , CancellationToken . None ) . GetAwaiter ( ) . GetResult ( ) ;
643
- }
644
- else
645
- {
646
- using ( var powerShell = System . Management . Automation . PowerShell . Create ( ) )
647
- {
648
- powerShell . Runspace = runspaceInfo . Runspace ;
649
- powerShell . Commands = createCommand ;
650
- powerShell . Invoke ( ) ;
651
- }
652
- }
677
+ var pwsh = SMA . PowerShell . Create ( ) ;
678
+ pwsh . Runspace = runspace ;
679
+ try
680
+ {
681
+ pwsh . InvokeCommand ( createCommand , new PSInvocationSettings { AddToHistory = false , ErrorActionPreference = ActionPreference . Stop } ) ;
653
682
}
654
- catch ( RemoteException e )
683
+ catch ( Exception e )
655
684
{
656
685
this . logger . LogException ( "Could not create psedit function." , e ) ;
657
686
}
687
+ finally
688
+ {
689
+ pwsh . Dispose ( ) ;
690
+ }
658
691
}
659
692
660
693
private void RemovePSEditFunction ( IRunspaceInfo runspaceInfo )
661
694
{
662
- if ( runspaceInfo . RunspaceOrigin = = RunspaceOrigin . PSSession )
695
+ if ( runspaceInfo . RunspaceOrigin ! = RunspaceOrigin . PSSession )
663
696
{
664
- try
697
+ return ;
698
+ }
699
+ try
700
+ {
701
+ if ( runspaceInfo . Runspace . Events != null )
665
702
{
666
- if ( runspaceInfo . Runspace . Events != null )
667
- {
668
- runspaceInfo . Runspace . Events . ReceivedEvents . PSEventReceived -= HandlePSEventReceivedAsync ;
669
- }
703
+ runspaceInfo . Runspace . Events . ReceivedEvents . PSEventReceived -= HandlePSEventReceivedAsync ;
704
+ }
670
705
671
- if ( runspaceInfo . Runspace . RunspaceStateInfo . State == RunspaceState . Opened )
706
+ if ( runspaceInfo . Runspace . RunspaceStateInfo . State == RunspaceState . Opened )
707
+ {
708
+ using ( var powerShell = SMA . PowerShell . Create ( ) )
672
709
{
673
- using ( var powerShell = System . Management . Automation . PowerShell . Create ( ) )
674
- {
675
- powerShell . Runspace = runspaceInfo . Runspace ;
676
- powerShell . Commands . AddScript ( RemovePSEditFunctionScript ) ;
677
- powerShell . Invoke ( ) ;
678
- }
710
+ powerShell . Runspace = runspaceInfo . Runspace ;
711
+ powerShell . Commands . AddScript ( RemovePSEditFunctionScript ) ;
712
+ powerShell . Invoke ( ) ;
679
713
}
680
714
}
681
- catch ( Exception e ) when ( e is RemoteException || e is PSInvalidOperationException )
682
- {
683
- this . logger . LogException ( "Could not remove psedit function." , e ) ;
684
- }
715
+ }
716
+ catch ( Exception e ) when ( e is RemoteException || e is PSInvalidOperationException )
717
+ {
718
+ this . logger . LogException ( "Could not remove psedit function." , e ) ;
685
719
}
686
720
}
687
721
0 commit comments