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