@@ -39,6 +39,33 @@ 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 for each PowerShell.
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
+
66
+ private static readonly PropertyInfo s_internalHostUIProperty ;
67
+ #endif
68
+
42
69
private readonly ILoggerFactory _loggerFactory ;
43
70
44
71
private readonly ILogger _logger ;
@@ -104,6 +131,31 @@ static PsesInternalHost()
104
131
s_scriptDebuggerTriggerObjectProperty = scriptDebuggerType . GetProperty (
105
132
"TriggerObject" ,
106
133
BindingFlags . Instance | BindingFlags . NonPublic ) ;
134
+
135
+ #if ! CoreCLR
136
+ PropertyInfo transcribeOnlyProperty = typeof ( PSHostUserInterface )
137
+ . GetProperty ( "TranscribeOnly" , BindingFlags . NonPublic | BindingFlags . Instance ) ;
138
+
139
+ MethodInfo transcribeOnlyGetMethod = transcribeOnlyProperty . GetGetMethod ( nonPublic : true ) ;
140
+
141
+ s_getTranscribeOnlyDelegate = ( Func < PSHostUserInterface , bool > ) Delegate . CreateDelegate (
142
+ typeof ( Func < PSHostUserInterface , bool > ) , transcribeOnlyGetMethod ) ;
143
+
144
+ MethodInfo transcribeOnlySetMethod = transcribeOnlyProperty . GetSetMethod ( nonPublic : true ) ;
145
+
146
+ s_setTranscribeOnlyDelegate = ( Action < PSHostUserInterface , bool > ) Delegate . CreateDelegate (
147
+ typeof ( Action < PSHostUserInterface , bool > ) , transcribeOnlySetMethod ) ;
148
+
149
+ s_executionContextProperty = typeof ( System . Management . Automation . Runspaces . Runspace )
150
+ . GetProperty ( "ExecutionContext" , BindingFlags . NonPublic | BindingFlags . Instance ) ;
151
+
152
+ s_internalHostProperty = s_executionContextProperty . PropertyType
153
+ . GetProperty ( "InternalHost" , BindingFlags . NonPublic | BindingFlags . Instance ) ;
154
+
155
+ // It's public but we want the override and reflection confuses me.
156
+ s_internalHostUIProperty = s_internalHostProperty . PropertyType
157
+ . GetProperty ( "UI" , BindingFlags . Public | BindingFlags . Instance ) ;
158
+ #endif
107
159
}
108
160
109
161
public PsesInternalHost (
@@ -476,19 +528,7 @@ public void InvokeDelegate(string representation, ExecutionOptions executionOpti
476
528
public IReadOnlyList < TResult > InvokePSCommand < TResult > ( PSCommand psCommand , PowerShellExecutionOptions executionOptions , CancellationToken cancellationToken )
477
529
{
478
530
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
- }
531
+ return task . ExecuteAndGetResult ( cancellationToken ) ;
492
532
}
493
533
494
534
public void InvokePSCommand ( PSCommand psCommand , PowerShellExecutionOptions executionOptions , CancellationToken cancellationToken ) => InvokePSCommand < PSObject > ( psCommand , executionOptions , cancellationToken ) ;
@@ -507,6 +547,29 @@ public void InvokePSDelegate(string representation, ExecutionOptions executionOp
507
547
508
548
internal void AddToHistory ( string historyEntry ) => _readLineProvider . ReadLine . AddToHistory ( historyEntry ) ;
509
549
550
+ // This works around a bug in PowerShell 5.1 (that was later fixed) where a running
551
+ // transcription could cause output to disappear since the `TranscribeOnly` property was
552
+ // accidentally not reset to false.
553
+ #pragma warning disable CA1822 // Warning to make it static when it's empty for CoreCLR.
554
+ internal void DisableTranscribeOnly ( )
555
+ #pragma warning restore CA1822
556
+ {
557
+ #if ! CoreCLR
558
+ // To fix the TranscribeOnly bug, we have to get the internal UI, which involves a lot
559
+ // of reflection since we can't always just use PowerShell to execute `$Host.UI`.
560
+ s_internalPSHostUserInterface ??=
561
+ s_internalHostUIProperty . GetValue (
562
+ s_internalHostProperty . GetValue (
563
+ s_executionContextProperty . GetValue ( CurrentPowerShell . Runspace ) ) )
564
+ as PSHostUserInterface ;
565
+
566
+ if ( s_getTranscribeOnlyDelegate ( s_internalPSHostUserInterface ) )
567
+ {
568
+ s_setTranscribeOnlyDelegate ( s_internalPSHostUserInterface , false ) ;
569
+ }
570
+ #endif
571
+ }
572
+
510
573
internal Task LoadHostProfilesAsync ( CancellationToken cancellationToken )
511
574
{
512
575
// NOTE: This is a special task run on startup!
0 commit comments