5
5
using System . Collections . Generic ;
6
6
using System . Collections . ObjectModel ;
7
7
using System . Management . Automation ;
8
+ using System . Management . Automation . Host ;
8
9
using System . Management . Automation . Remoting ;
10
+ using System . Reflection ;
9
11
using System . Threading ;
10
12
using Microsoft . Extensions . Logging ;
11
13
using Microsoft . PowerShell . EditorServices . Services . PowerShell . Context ;
@@ -27,6 +29,55 @@ internal class SynchronousPowerShellTask<TResult> : SynchronousTask<IReadOnlyLis
27
29
{
28
30
private static readonly PowerShellExecutionOptions s_defaultPowerShellExecutionOptions = new ( ) ;
29
31
32
+ #if ! CoreCLR
33
+ /// <summary>
34
+ /// To workaround a bug where the `TranscribeOnly` field of the PSHostUserInterface can
35
+ /// accidentally remain true, we have to use a PowerShell pipeline to get the internal
36
+ /// instance (saved here) and reflection to get delegates for the field's getter and setter
37
+ /// methods, and then reset it to false (see <see cref="ExecuteNormally"/>). Note that it
38
+ /// must be the internal instance, not our own UI instance.
39
+ /// See https://github.com/PowerShell/PowerShell/pull/3436
40
+ /// </summary>
41
+ [ ThreadStatic ] // Because we can re-use it, but only for each PowerShell.
42
+ private static PSHostUserInterface s_internalPSHostUserInterface ;
43
+
44
+ private static readonly Func < PSHostUserInterface , bool > s_getTranscribeOnlyDelegate ;
45
+
46
+ private static readonly Action < PSHostUserInterface , bool > s_setTranscribeOnlyDelegate ;
47
+
48
+ private static readonly PropertyInfo s_executionContextProperty ;
49
+
50
+ private static readonly PropertyInfo s_internalHostProperty ;
51
+
52
+ private static readonly PropertyInfo s_internalHostUIProperty ;
53
+
54
+ static SynchronousPowerShellTask ( )
55
+ {
56
+ PropertyInfo transcribeOnlyProperty = typeof ( PSHostUserInterface )
57
+ . GetProperty ( "TranscribeOnly" , BindingFlags . NonPublic | BindingFlags . Instance ) ;
58
+
59
+ MethodInfo transcribeOnlyGetMethod = transcribeOnlyProperty . GetGetMethod ( nonPublic : true ) ;
60
+
61
+ s_getTranscribeOnlyDelegate = ( Func < PSHostUserInterface , bool > ) Delegate . CreateDelegate (
62
+ typeof ( Func < PSHostUserInterface , bool > ) , transcribeOnlyGetMethod ) ;
63
+
64
+ MethodInfo transcribeOnlySetMethod = transcribeOnlyProperty . GetSetMethod ( nonPublic : true ) ;
65
+
66
+ s_setTranscribeOnlyDelegate = ( Action < PSHostUserInterface , bool > ) Delegate . CreateDelegate (
67
+ typeof ( Action < PSHostUserInterface , bool > ) , transcribeOnlySetMethod ) ;
68
+
69
+ s_executionContextProperty = typeof ( SMA . Runspaces . Runspace )
70
+ . GetProperty ( "ExecutionContext" , BindingFlags . NonPublic | BindingFlags . Instance ) ;
71
+
72
+ s_internalHostProperty = s_executionContextProperty . PropertyType
73
+ . GetProperty ( "InternalHost" , BindingFlags . NonPublic | BindingFlags . Instance ) ;
74
+
75
+ // It's public but we want the override and reflection confuses me.
76
+ s_internalHostUIProperty = s_internalHostProperty . PropertyType
77
+ . GetProperty ( "UI" , BindingFlags . Public | BindingFlags . Instance ) ;
78
+ }
79
+ #endif
80
+
30
81
private readonly ILogger _logger ;
31
82
32
83
private readonly PsesInternalHost _psesHost ;
@@ -105,6 +156,21 @@ private IReadOnlyList<TResult> ExecuteNormally(CancellationToken cancellationTok
105
156
if ( PowerShellExecutionOptions . WriteOutputToHost )
106
157
{
107
158
_psCommand . AddOutputCommand ( ) ;
159
+ #if ! CoreCLR
160
+ // To fix the TranscribeOnly bug, we have to get the internal UI, which involves a
161
+ // lot of reflection since we can't always just use PowerShell to execute `$Host.UI`
162
+ // (we tried). With that internal UI we can reset its `TranscribeOnly` flag and so
163
+ // avoid the disappearing output that happens when a transcription is running.
164
+ if ( ! _pwsh . Runspace . RunspaceIsRemote )
165
+ {
166
+ s_internalPSHostUserInterface ??=
167
+ s_internalHostUIProperty . GetValue (
168
+ s_internalHostProperty . GetValue (
169
+ s_executionContextProperty . GetValue ( _pwsh . Runspace ) ) )
170
+ as PSHostUserInterface ;
171
+ DisableTranscribeOnly ( ) ;
172
+ }
173
+ #endif
108
174
}
109
175
110
176
cancellationToken . Register ( CancelNormalExecution ) ;
@@ -148,7 +214,7 @@ private IReadOnlyList<TResult> ExecuteNormally(CancellationToken cancellationTok
148
214
if ( e is PSRemotingTransportException )
149
215
{
150
216
_ = System . Threading . Tasks . Task . Run (
151
- ( ) => _psesHost . UnwindCallStack ( ) ,
217
+ _psesHost . UnwindCallStack ,
152
218
CancellationToken . None )
153
219
. HandleErrorsAsync ( _logger ) ;
154
220
@@ -189,8 +255,6 @@ private IReadOnlyList<TResult> ExecuteNormally(CancellationToken cancellationTok
189
255
190
256
private IReadOnlyList < TResult > ExecuteInDebugger ( CancellationToken cancellationToken )
191
257
{
192
- // TODO: How much of this method can we remove now that it only processes PowerShell's
193
- // intrinsic debugger commands?
194
258
cancellationToken . Register ( CancelDebugExecution ) ;
195
259
196
260
PSDataCollection < PSObject > outputCollection = new ( ) ;
@@ -247,7 +311,7 @@ private IReadOnlyList<TResult> ExecuteInDebugger(CancellationToken cancellationT
247
311
if ( e is PSRemotingTransportException )
248
312
{
249
313
_ = System . Threading . Tasks . Task . Run (
250
- ( ) => _psesHost . UnwindCallStack ( ) ,
314
+ _psesHost . UnwindCallStack ,
251
315
CancellationToken . None )
252
316
. HandleErrorsAsync ( _logger ) ;
253
317
@@ -396,5 +460,18 @@ private void CancelDebugExecution()
396
460
397
461
_pwsh . Runspace . Debugger . StopProcessCommand ( ) ;
398
462
}
463
+
464
+ #if ! CoreCLR
465
+ // This works around a bug in PowerShell 5.1 (that was later fixed) where a running
466
+ // transcription could cause output to disappear since the `TranscribeOnly` property was
467
+ // accidentally not reset to false.
468
+ private static void DisableTranscribeOnly ( )
469
+ {
470
+ if ( s_getTranscribeOnlyDelegate ( s_internalPSHostUserInterface ) )
471
+ {
472
+ s_setTranscribeOnlyDelegate ( s_internalPSHostUserInterface , false ) ;
473
+ }
474
+ }
475
+ #endif
399
476
}
400
477
}
0 commit comments