@@ -39,6 +39,31 @@ internal class PsesInternalHost : PSHost, IHostSupportsInteractiveSession, IRuns
39
39
40
40
private static readonly PropertyInfo s_scriptDebuggerTriggerObjectProperty ;
41
41
42
+ #if ! CoreCLR
43
+ /// <summary>
44
+ /// To workaround a horrid bug where the `TranscribeOnly` field of the PSHostUserInterface
45
+ /// can accidentally remain true, we have to use a bunch of reflection so that <see
46
+ /// cref="DisableTranscribeOnly()" /> can reset it to false. (This was fixed in PowerShell
47
+ /// 7.) Note that it must be the internal UI instance, not our own UI instance, otherwise
48
+ /// this would be easier. Because of the amount of reflection involved, we contain it to
49
+ /// only PowerShell 5.1 at compile-time, and we have to set this up in this class, not <see
50
+ /// cref="SynchronousPowerShellTask" /> because that's templated, making statics practically
51
+ /// useless. <see cref = "SynchronousPowerShellTask.ExecuteNormally" /> method calls <see
52
+ /// cref="DisableTranscribeOnly()" /> when necessary.
53
+ /// See: https://github.com/PowerShell/PowerShell/pull/3436
54
+ /// </summary>
55
+ [ ThreadStatic ] // Because we can re-use it, but only once per instance of PSES.
56
+ private static PSHostUserInterface s_internalPSHostUserInterface ;
57
+
58
+ private static readonly Func < PSHostUserInterface , bool > s_getTranscribeOnlyDelegate ;
59
+
60
+ private static readonly Action < PSHostUserInterface , bool > s_setTranscribeOnlyDelegate ;
61
+
62
+ private static readonly PropertyInfo s_executionContextProperty ;
63
+
64
+ private static readonly PropertyInfo s_internalHostProperty ;
65
+ #endif
66
+
42
67
private readonly ILoggerFactory _loggerFactory ;
43
68
44
69
private readonly ILogger _logger ;
@@ -104,6 +129,27 @@ static PsesInternalHost()
104
129
s_scriptDebuggerTriggerObjectProperty = scriptDebuggerType . GetProperty (
105
130
"TriggerObject" ,
106
131
BindingFlags . Instance | BindingFlags . NonPublic ) ;
132
+
133
+ #if ! CoreCLR
134
+ PropertyInfo transcribeOnlyProperty = typeof ( PSHostUserInterface )
135
+ . GetProperty ( "TranscribeOnly" , BindingFlags . NonPublic | BindingFlags . Instance ) ;
136
+
137
+ MethodInfo transcribeOnlyGetMethod = transcribeOnlyProperty . GetGetMethod ( nonPublic : true ) ;
138
+
139
+ s_getTranscribeOnlyDelegate = ( Func < PSHostUserInterface , bool > ) Delegate . CreateDelegate (
140
+ typeof ( Func < PSHostUserInterface , bool > ) , transcribeOnlyGetMethod ) ;
141
+
142
+ MethodInfo transcribeOnlySetMethod = transcribeOnlyProperty . GetSetMethod ( nonPublic : true ) ;
143
+
144
+ s_setTranscribeOnlyDelegate = ( Action < PSHostUserInterface , bool > ) Delegate . CreateDelegate (
145
+ typeof ( Action < PSHostUserInterface , bool > ) , transcribeOnlySetMethod ) ;
146
+
147
+ s_executionContextProperty = typeof ( System . Management . Automation . Runspaces . Runspace )
148
+ . GetProperty ( "ExecutionContext" , BindingFlags . NonPublic | BindingFlags . Instance ) ;
149
+
150
+ s_internalHostProperty = s_executionContextProperty . PropertyType
151
+ . GetProperty ( "InternalHost" , BindingFlags . NonPublic | BindingFlags . Instance ) ;
152
+ #endif
107
153
}
108
154
109
155
public PsesInternalHost (
@@ -476,19 +522,7 @@ public void InvokeDelegate(string representation, ExecutionOptions executionOpti
476
522
public IReadOnlyList < TResult > InvokePSCommand < TResult > ( PSCommand psCommand , PowerShellExecutionOptions executionOptions , CancellationToken cancellationToken )
477
523
{
478
524
SynchronousPowerShellTask < TResult > task = new ( _logger , this , psCommand , executionOptions , cancellationToken ) ;
479
- try
480
- {
481
- return task . ExecuteAndGetResult ( cancellationToken ) ;
482
- }
483
- finally
484
- {
485
- // At the end of each PowerShell command we need to reset PowerShell 5.1's
486
- // `TranscribeOnly` property to avoid a bug where output disappears.
487
- if ( UI is EditorServicesConsolePSHostUserInterface ui )
488
- {
489
- ui . DisableTranscribeOnly ( ) ;
490
- }
491
- }
525
+ return task . ExecuteAndGetResult ( cancellationToken ) ;
492
526
}
493
527
494
528
public void InvokePSCommand ( PSCommand psCommand , PowerShellExecutionOptions executionOptions , CancellationToken cancellationToken ) => InvokePSCommand < PSObject > ( psCommand , executionOptions , cancellationToken ) ;
@@ -507,6 +541,28 @@ public void InvokePSDelegate(string representation, ExecutionOptions executionOp
507
541
508
542
internal void AddToHistory ( string historyEntry ) => _readLineProvider . ReadLine . AddToHistory ( historyEntry ) ;
509
543
544
+ // This works around a bug in PowerShell 5.1 (that was later fixed) where a running
545
+ // transcription could cause output to disappear since the `TranscribeOnly` property was
546
+ // accidentally not reset to false.
547
+ #pragma warning disable CA1822 // Warning to make it static when it's empty for CoreCLR.
548
+ internal void DisableTranscribeOnly ( )
549
+ #pragma warning restore CA1822
550
+ {
551
+ #if ! CoreCLR
552
+ // To fix the TranscribeOnly bug, we have to get the internal UI, which involves a lot
553
+ // of reflection since we can't always just use PowerShell to execute `$Host.UI`.
554
+ s_internalPSHostUserInterface ??=
555
+ ( s_internalHostProperty . GetValue (
556
+ s_executionContextProperty . GetValue ( CurrentPowerShell . Runspace ) )
557
+ as PSHost ) . UI ;
558
+
559
+ if ( s_getTranscribeOnlyDelegate ( s_internalPSHostUserInterface ) )
560
+ {
561
+ s_setTranscribeOnlyDelegate ( s_internalPSHostUserInterface , false ) ;
562
+ }
563
+ #endif
564
+ }
565
+
510
566
internal Task LoadHostProfilesAsync ( CancellationToken cancellationToken )
511
567
{
512
568
// NOTE: This is a special task run on startup!
0 commit comments