-
Notifications
You must be signed in to change notification settings - Fork 7.1k
/
PowerShell.cs
5965 lines (5363 loc) · 228 KB
/
PowerShell.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/********************************************************************++
Copyright (c) Microsoft Corporation. All rights reserved.
--********************************************************************/
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Runtime.Serialization;
using System.Threading;
using System.Management.Automation.Host;
using System.Management.Automation.Internal;
using System.Management.Automation.Runspaces;
using System.Management.Automation.Runspaces.Internal;
using System.Diagnostics.CodeAnalysis; // for fxcop.
using Dbg = System.Management.Automation.Diagnostics;
using System.Diagnostics;
using Microsoft.Management.Infrastructure;
#pragma warning disable 1634, 1691 // Stops compiler from warning about unknown warnings
namespace System.Management.Automation
{
#region Exceptions
/// <summary>
/// Defines exception which is thrown when state of the PowerShell is different
/// from the expected state.
/// </summary>
[Serializable]
public class InvalidPowerShellStateException : SystemException
{
/// <summary>
/// Creates a new instance of InvalidPowershellStateException class.
/// </summary>
public InvalidPowerShellStateException()
: base
(StringUtil.Format(PowerShellStrings.InvalidPowerShellStateGeneral))
{
}
/// <summary>
/// Creates a new instance of InvalidPowershellStateException class.
/// </summary>
/// <param name="message">
/// The error message that explains the reason for the exception.
/// </param>
public InvalidPowerShellStateException(string message)
: base(message)
{
}
/// <summary>
/// Creates a new instance of InvalidPowershellStateException class.
/// </summary>
/// <param name="message">
/// The error message that explains the reason for the exception.
/// </param>
/// <param name="innerException">
/// The exception that is the cause of the current exception.
/// </param>
public InvalidPowerShellStateException(string message, Exception innerException)
: base(message, innerException)
{
}
/// <summary>
/// Initializes a new instance of the InvalidPowerShellStateException and defines value of
/// CurrentState.
/// </summary>
/// <param name="currentState">Current state of powershell</param>
internal InvalidPowerShellStateException(PSInvocationState currentState)
: base
(StringUtil.Format(PowerShellStrings.InvalidPowerShellStateGeneral))
{
_currState = currentState;
}
#region ISerializable Members
// No need to implement GetObjectData
// if all fields are static or [NonSerialized]
/// <summary>
/// Initializes a new instance of the InvalidPowerShellStateException
/// class with serialized data.
/// </summary>
/// <param name="info">
/// The <see cref="SerializationInfo"/> that holds the serialized object
/// data about the exception being thrown.
/// </param>
/// <param name="context">
/// The <see cref="StreamingContext"/> that contains contextual information
/// about the source or destination.
/// </param>
protected
InvalidPowerShellStateException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
#endregion
/// <summary>
/// Gets CurrentState of the powershell
/// </summary>
public PSInvocationState CurrentState
{
get
{
return _currState;
}
}
/// <summary>
/// State of powershell when exception was thrown.
/// </summary>
[NonSerialized]
private PSInvocationState _currState = 0;
}
#endregion
#region PSInvocationState, PSInvocationStateInfo, PSInvocationStateChangedEventArgs
/// <summary>
/// Enumerated type defining the state of the PowerShell
/// </summary>
public enum PSInvocationState
{
/// <summary>
/// PowerShell has not been started
/// </summary>
NotStarted = 0,
/// <summary>
/// PowerShell is executing
/// </summary>
Running = 1,
/// <summary>
/// PowerShell is stoping execution.
/// </summary>
Stopping = 2,
/// <summary>
/// PowerShell is completed due to a stop request.
/// </summary>
Stopped = 3,
/// <summary>
/// PowerShell has completed executing a command.
/// </summary>
Completed = 4,
/// <summary>
/// PowerShell completed abnormally due to an error.
/// </summary>
Failed = 5,
/// <summary>
/// PowerShell is in disconnected state.
/// </summary>
Disconnected = 6
}
/// <summary>
/// Enumerated type defining runspace modes for nested pipeline
/// </summary>
public enum RunspaceMode
{
/// <summary>
/// Use current runspace from the current thread of execution
/// </summary>
CurrentRunspace = 0,
/// <summary>
/// Create new runspace
/// </summary>
NewRunspace = 1
}
/// <summary>
/// Type which has information about InvocationState and Exception
/// associated with InvocationState
/// </summary>
public sealed class PSInvocationStateInfo
{
#region Constructors
/// <summary>
/// Constructor for state changes with an optional error.
/// </summary>
/// <param name="state">The new state.</param>
/// <param name="reason">A non-null exception if the state change was
/// caused by an error,otherwise; null.
/// </param>
internal PSInvocationStateInfo(PSInvocationState state, Exception reason)
{
_executionState = state;
_exceptionReason = reason;
}
/// <summary>
/// Construct from PipelineStateInfo
/// </summary>
/// <param name="pipelineStateInfo"></param>
internal PSInvocationStateInfo(PipelineStateInfo pipelineStateInfo)
{
_executionState = (PSInvocationState)((int)pipelineStateInfo.State);
_exceptionReason = pipelineStateInfo.Reason;
}
#endregion
#region Public Properties
/// <summary>
/// The state of the PowerShell instance.
/// </summary>
/// <remarks>
/// </remarks>
public PSInvocationState State
{
get
{
return _executionState;
}
}
/// <summary>
/// The reason for the state change, if caused by an error.
/// </summary>
/// <remarks>
/// The value of this property is non-null if the state
/// changed due to an error. Otherwise, the value of this
/// property is null.
/// </remarks>
public Exception Reason
{
get
{
return _exceptionReason;
}
}
#endregion
/// <summary>
/// Clone the current instance.
/// </summary>
/// <returns>
/// A copy of the current instance.
/// </returns>
internal PSInvocationStateInfo Clone()
{
return new PSInvocationStateInfo(
_executionState,
_exceptionReason
);
}
#region Private data
/// <summary>
/// The current execution state
/// </summary>
private PSInvocationState _executionState;
/// <summary>
/// Non-null exception if the execution state change was due to an error.
/// </summary>
private Exception _exceptionReason;
#endregion
}
/// <summary>
/// Event arguments passed to PowerShell state change handlers
/// <see cref="PowerShell.InvocationStateChanged"/> event.
/// </summary>
public sealed class PSInvocationStateChangedEventArgs : EventArgs
{
#region Constructors
/// <summary>
/// Constructs PSInvocationStateChangedEventArgs from PSInvocationStateInfo
/// </summary>
/// <param name="psStateInfo">
/// state to raise the event with.
/// </param>
internal PSInvocationStateChangedEventArgs(PSInvocationStateInfo psStateInfo)
{
Dbg.Assert(psStateInfo != null, "caller should validate the parameter");
InvocationStateInfo = psStateInfo;
}
#endregion
#region Public Properties
/// <summary>
/// Information about current state of a PowerShell Instance.
/// </summary>
public PSInvocationStateInfo InvocationStateInfo { get; }
#endregion
}
#endregion
/// <summary>
/// Settings to control command invocation.
/// </summary>
public sealed class PSInvocationSettings
{
#region Private Fields
private PSHost _host;
// the following are used to flow the identity to pipeline execution thread
// Invokes a remote command and immediately disconnects, if transport layer
// supports this operation.
#endregion
#region Constructors
/// <summary>
/// Default Constructor.
/// </summary>
public PSInvocationSettings()
{
#if !CORECLR // No ApartmentState In CoreCLR
this.ApartmentState = ApartmentState.Unknown;
#endif
_host = null;
RemoteStreamOptions = 0;
AddToHistory = false;
ErrorActionPreference = null;
}
#endregion
#if !CORECLR // No ApartmentState In CoreCLR
/// <summary>
/// ApartmentState of the thread in which the command
/// is executed.
/// </summary>
public ApartmentState ApartmentState { get; set; }
#endif
/// <summary>
/// Host to use with the Runspace when the command is
/// executed.
/// </summary>
public PSHost Host
{
get
{
return _host;
}
set
{
if (null == value)
{
throw PSTraceSource.NewArgumentNullException("Host");
}
_host = value;
}
}
/// <summary>
/// Options for the Error, Warning, Verbose and Debug streams during remote calls
/// </summary>
public RemoteStreamOptions RemoteStreamOptions { get; set; }
/// <summary>
/// Boolean which tells if the command is added to the history of the
/// Runspace the command is executing in. By default this is false.
/// </summary>
public bool AddToHistory { get; set; }
/// <summary>
/// Determines how errors should be handled during batch command execution
/// </summary>
public ActionPreference? ErrorActionPreference { get; set; }
/// <summary>
/// Used by Powershell remoting infrastructure to flow identity from calling thread to
/// Pipeline Execution Thread.
/// </summary>
/// <remarks>
/// Scenario: In the IIS hosting model, the calling thread is impersonated with a different
/// identity than the process identity. However Pipeline Execution Thread always inherits
/// process's identity and this will create problems related to security. In the IIS hosting
/// model, we should honor calling threads identity.
/// </remarks>
public bool FlowImpersonationPolicy { get; set; }
internal System.Security.Principal.WindowsIdentity WindowsIdentityToImpersonate { get; set; }
/// <summary>
/// When true, allows an unhandled flow control exceptions to
/// propagate to a caller invoking the PowerShell object.
/// </summary>
public bool ExposeFlowControlExceptions
{
get;
set;
}
/// <summary>
/// Invokes a remote command and immediately disconnects, if the transport
/// layer supports this operation.
/// </summary>
internal bool InvokeAndDisconnect { get; set; }
}
/// <summary>
/// Batch execution context
/// </summary>
internal class BatchInvocationContext
{
private AutoResetEvent _completionEvent;
/// <summary>
/// Class constructor
/// </summary>
/// <param name="command"></param>
/// <param name="output"></param>
internal BatchInvocationContext(PSCommand command, PSDataCollection<PSObject> output)
{
Command = command;
Output = output;
_completionEvent = new AutoResetEvent(false);
}
/// <summary>
/// Invocation output
/// </summary>
internal PSDataCollection<PSObject> Output { get; }
/// <summary>
/// Command to invoke
/// </summary>
internal PSCommand Command { get; }
/// <summary>
/// Waits for the completion event
/// </summary>
internal void Wait()
{
_completionEvent.WaitOne();
}
/// <summary>
/// Signals the completion event
/// </summary>
internal void Signal()
{
_completionEvent.Set();
}
}
/// <summary>
/// These flags control whether InvocationInfo is added to items in the Error, Warning, Verbose and Debug
/// streams during remote calls
/// </summary>
[Flags]
public enum RemoteStreamOptions
{
/// <summary>
/// If this flag is set, ErrorRecord will include an instance of InvocationInfo on remote calls
/// </summary>
AddInvocationInfoToErrorRecord = 0x01,
/// <summary>
/// If this flag is set, WarningRecord will include an instance of InvocationInfo on remote calls
/// </summary>
AddInvocationInfoToWarningRecord = 0x02,
/// <summary>
/// If this flag is set, DebugRecord will include an instance of InvocationInfo on remote calls
/// </summary>
AddInvocationInfoToDebugRecord = 0x04,
/// <summary>
/// If this flag is set, VerboseRecord will include an instance of InvocationInfo on remote calls
/// </summary>
AddInvocationInfoToVerboseRecord = 0x08,
/// <summary>
/// If this flag is set, ErrorRecord, WarningRecord, DebugRecord, and VerboseRecord will include an instance of InvocationInfo on remote calls
/// </summary>
AddInvocationInfo = AddInvocationInfoToErrorRecord
| AddInvocationInfoToWarningRecord
| AddInvocationInfoToDebugRecord
| AddInvocationInfoToVerboseRecord
}
#region PowerShell AsyncResult
/// <summary>
/// Internal Async result type used by BeginInvoke() and BeginStop() overloads.
/// </summary>
internal sealed class PowerShellAsyncResult : AsyncResult
{
#region Private Data / Properties
// used to track if this AsyncResult is created by a BeginInvoke operation or
// a BeginStop operation.
/// <summary>
/// true if AsyncResult monitors Async BeginInvoke().
/// false otherwise
/// </summary>
internal bool IsAssociatedWithAsyncInvoke { get; }
/// <summary>
/// The output buffer for the asynchronous invoke
/// </summary>
internal PSDataCollection<PSObject> Output { get; }
#endregion
#region Constructor
/// <summary>
/// Constructor
/// </summary>
/// <param name="ownerId">
/// Instance Id of the Powershell object creating this instance
/// </param>
/// <param name="callback">
/// Callback to call when the async operation completes.
/// </param>
/// <param name="state">
/// A user supplied state to call the "callback" with.
/// </param>
/// <param name="output">
/// The output buffer to return from EndInvoke.
/// </param>
/// <param name="isCalledFromBeginInvoke">
/// true if AsyncResult monitors BeginInvoke.
/// false otherwise
/// </param>
internal PowerShellAsyncResult(Guid ownerId, AsyncCallback callback, object state, PSDataCollection<PSObject> output,
bool isCalledFromBeginInvoke)
: base(ownerId, callback, state)
{
IsAssociatedWithAsyncInvoke = isCalledFromBeginInvoke;
Output = output;
}
#endregion
}
#endregion
/// <summary>
/// Represents a PowerShell command or script to execute against a
/// Runspace(Pool) if provided, otherwise execute using a default
/// Runspace. Provides access to different result buffers
/// like output, error, debug, verbose, progress, warning, and information.
///
/// Provides a simple interface to execute a powershell command:
/// <code>
/// Powershell.Create("get-process").Invoke();
/// </code>
/// The above statement creates a local runspace using default
/// configuration, executes the command and then closes the runspace.
///
/// Using RunspacePool property, the caller can provide the runspace
/// where the command / script is executed.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1724:TypeNamesShouldNotMatchNamespaces", Justification = "PowerShell is a valid type in SMAR namespace.")]
public sealed class PowerShell : IDisposable
{
#region Private Fields
private PSCommand _psCommand;
// worker object which does the invoke
private Worker _worker;
private PowerShellAsyncResult _invokeAsyncResult;
private PowerShellAsyncResult _stopAsyncResult;
private PowerShellAsyncResult _batchAsyncResult;
private PSInvocationSettings _batchInvocationSettings;
private PSCommand _backupPSCommand;
private object _rsConnection;
private PSDataCollection<ErrorRecord> _errorBuffer;
private bool _isDisposed;
private object _syncObject = new object();
// client remote powershell if the powershell
// is executed with a remote runspace pool
private ConnectCommandInfo _connectCmdInfo;
private bool _commandInvokedSynchronously = false;
private bool _isBatching = false;
private bool _stopBatchExecution = false;
#endregion
#region Internal Constructors
/// <summary>
/// Constructs PowerShell
/// </summary>
/// <param name="command">
/// A PSCommand.
/// </param>
/// <param name="extraCommands">
/// A list of extra commands to run
/// </param>
/// <param name="rsConnection">
/// A Runspace or RunspacePool to refer while invoking the command.
/// This can be null in which case a new runspace is created
/// whenever Invoke* method is called.
/// </param>
private PowerShell(PSCommand command, Collection<PSCommand> extraCommands, object rsConnection)
{
Dbg.Assert(command != null, "command must not be null");
ExtraCommands = extraCommands ?? new Collection<PSCommand>();
RunningExtraCommands = false;
_psCommand = command;
_psCommand.Owner = this;
RemoteRunspace remoteRunspace = rsConnection as RemoteRunspace;
_rsConnection = remoteRunspace != null ? remoteRunspace.RunspacePool : rsConnection;
InstanceId = Guid.NewGuid();
InvocationStateInfo = new PSInvocationStateInfo(PSInvocationState.NotStarted, null);
OutputBuffer = null;
OutputBufferOwner = true;
_errorBuffer = new PSDataCollection<ErrorRecord>();
ErrorBufferOwner = true;
InformationalBuffers = new PSInformationalBuffers(InstanceId);
Streams = new PSDataStreams(this);
}
/// <summary>
/// Constructs a PowerShell instance in the disconnected start state with
/// the provided remote command connect information and runspace(pool) objects.
/// </summary>
/// <param name="connectCmdInfo">Remote command connect information.</param>
/// <param name="rsConnection">Remote Runspace or RunspacePool object.</param>
internal PowerShell(ConnectCommandInfo connectCmdInfo, object rsConnection)
: this(new PSCommand(), null, rsConnection)
{
ExtraCommands = new Collection<PSCommand>();
RunningExtraCommands = false;
AddCommand(connectCmdInfo.Command);
_connectCmdInfo = connectCmdInfo;
// The command ID is passed to the PSRP layer through the PowerShell instanceID.
InstanceId = _connectCmdInfo.CommandId;
InvocationStateInfo = new PSInvocationStateInfo(PSInvocationState.Disconnected, null);
if (rsConnection is RemoteRunspace)
{
_runspace = rsConnection as Runspace;
_runspacePool = ((RemoteRunspace)rsConnection).RunspacePool;
}
else if (rsConnection is RunspacePool)
{
_runspacePool = (RunspacePool)rsConnection;
}
Dbg.Assert(_runspacePool != null, "Invalid rsConnection parameter>");
RemotePowerShell = new ClientRemotePowerShell(this, _runspacePool.RemoteRunspacePoolInternal);
}
/// <summary>
///
/// </summary>
/// <param name="inputstream"></param>
/// <param name="outputstream"></param>
/// <param name="errorstream"></param>
/// <param name="runspacePool"></param>
internal PowerShell(ObjectStreamBase inputstream,
ObjectStreamBase outputstream, ObjectStreamBase errorstream, RunspacePool runspacePool)
{
ExtraCommands = new Collection<PSCommand>();
RunningExtraCommands = false;
_rsConnection = runspacePool;
InstanceId = Guid.NewGuid();
InvocationStateInfo = new PSInvocationStateInfo(PSInvocationState.NotStarted, null);
InformationalBuffers = new PSInformationalBuffers(InstanceId);
Streams = new PSDataStreams(this);
PSDataCollectionStream<PSObject> outputdatastream = (PSDataCollectionStream<PSObject>)outputstream;
OutputBuffer = outputdatastream.ObjectStore;
PSDataCollectionStream<ErrorRecord> errordatastream = (PSDataCollectionStream<ErrorRecord>)errorstream;
_errorBuffer = errordatastream.ObjectStore;
if (runspacePool != null && runspacePool.RemoteRunspacePoolInternal != null)
{
RemotePowerShell = new ClientRemotePowerShell(this, runspacePool.RemoteRunspacePoolInternal);
}
}
/// <summary>
/// Creates a PowerShell object in the disconnected start state and with a ConnectCommandInfo object
/// parameter that specifies what remote command to associate with this PowerShell when it is connected.
/// </summary>
/// <param name="connectCmdInfo"></param>
/// <param name="inputstream"></param>
/// <param name="outputstream"></param>
/// <param name="errorstream"></param>
/// <param name="runspacePool"></param>
internal PowerShell(ConnectCommandInfo connectCmdInfo, ObjectStreamBase inputstream, ObjectStreamBase outputstream,
ObjectStreamBase errorstream, RunspacePool runspacePool)
: this(inputstream, outputstream, errorstream, runspacePool)
{
ExtraCommands = new Collection<PSCommand>();
RunningExtraCommands = false;
_psCommand = new PSCommand();
_psCommand.Owner = this;
_runspacePool = runspacePool;
AddCommand(connectCmdInfo.Command);
_connectCmdInfo = connectCmdInfo;
// The command ID is passed to the PSRP layer through the PowerShell instanceID.
InstanceId = _connectCmdInfo.CommandId;
InvocationStateInfo = new PSInvocationStateInfo(PSInvocationState.Disconnected, null);
RemotePowerShell = new ClientRemotePowerShell(this, runspacePool.RemoteRunspacePoolInternal);
}
/// <summary>
/// Sets the command collection in this powershell
/// </summary>
/// <remarks>This method will be called by RemotePipeline
/// before it begins execution. This method is used to set
/// the command collection of the remote pipeline as the
/// command collection of the underlying powershell</remarks>
internal void InitForRemotePipeline(CommandCollection command, ObjectStreamBase inputstream,
ObjectStreamBase outputstream, ObjectStreamBase errorstream, PSInvocationSettings settings, bool redirectShellErrorOutputPipe)
{
Dbg.Assert(command != null, "A command collection need to be specified");
_psCommand = new PSCommand(command[0]);
_psCommand.Owner = this;
for (int i = 1; i < command.Count; i++)
{
AddCommand(command[i]);
}
RedirectShellErrorOutputPipe = redirectShellErrorOutputPipe;
// create the client remote powershell for remoting
// communications
if (RemotePowerShell == null)
{
RemotePowerShell = new ClientRemotePowerShell(this, ((RunspacePool)_rsConnection).RemoteRunspacePoolInternal);
}
// If we get here, we don't call 'Invoke' or any of it's friends on 'this', instead we serialize 'this' in PowerShell.ToPSObjectForRemoting.
// Without the following two steps, we'll be missing the 'ExtraCommands' on the serialized instance of 'this'.
// This is the last possible chance to call set up for batching as we will indirectly call ToPSObjectForRemoting
// in the call to ClientRemotePowerShell.Initialize (which happens just below.)
DetermineIsBatching();
if (_isBatching)
{
SetupAsyncBatchExecution();
}
RemotePowerShell.Initialize(inputstream, outputstream,
errorstream, InformationalBuffers, settings);
}
/// <summary>
/// Initialize PowerShell object for connection to remote command.
/// </summary>
/// <param name="inputstream">Input stream.</param>
/// <param name="outputstream">Output stream.</param>
/// <param name="errorstream">Error stream.</param>
/// <param name="settings">Settings information.</param>
/// <param name="redirectShellErrorOutputPipe">Redirect error output.</param>
internal void InitForRemotePipelineConnect(ObjectStreamBase inputstream, ObjectStreamBase outputstream,
ObjectStreamBase errorstream, PSInvocationSettings settings, bool redirectShellErrorOutputPipe)
{
// The remotePowerShell and DSHandler cannot be initialized with a disconnected runspace.
// Make sure the associated runspace is valid and connected.
CheckRunspacePoolAndConnect();
if (InvocationStateInfo.State != PSInvocationState.Disconnected)
{
throw new InvalidPowerShellStateException(InvocationStateInfo.State);
}
RedirectShellErrorOutputPipe = redirectShellErrorOutputPipe;
if (RemotePowerShell == null)
{
RemotePowerShell = new ClientRemotePowerShell(this, ((RunspacePool)_rsConnection).RemoteRunspacePoolInternal);
}
if (!RemotePowerShell.Initialized)
{
RemotePowerShell.Initialize(inputstream, outputstream, errorstream, InformationalBuffers, settings);
}
}
#endregion
#region Construction Factory
/// <summary>
/// Constructs an empty PowerShell instance; a script or command must be added before invoking this instance
/// </summary>
/// <returns>
/// An instance of PowerShell.
/// </returns>
public static PowerShell Create()
{
return new PowerShell(new PSCommand(), null, null);
}
/// <summary>
/// Constructs an empty PowerShell instance; a script or command must be added before invoking this instance
/// </summary>
/// <param name="runspace">runspace mode</param>
/// <returns>An instance of PowerShell.</returns>
public static PowerShell Create(RunspaceMode runspace)
{
PowerShell result = null;
switch (runspace)
{
case RunspaceMode.CurrentRunspace:
if (Runspace.DefaultRunspace == null)
{
throw new InvalidOperationException(PowerShellStrings.NoDefaultRunspaceForPSCreate);
}
result = new PowerShell(new PSCommand(), null, Runspace.DefaultRunspace);
result.IsChild = true;
result.IsNested = true;
result.IsRunspaceOwner = false;
result._runspace = Runspace.DefaultRunspace;
break;
case RunspaceMode.NewRunspace:
result = new PowerShell(new PSCommand(), null, null);
break;
}
return result;
}
/// <summary>
/// Constructs an empty PowerShell instance; a script or command must be added before invoking this instance
/// </summary>
/// <param name="initialSessionState">InitialSessionState with which to create the runspace</param>
/// <returns>An instance of PowerShell.</returns>
public static PowerShell Create(InitialSessionState initialSessionState)
{
PowerShell result = Create();
result.Runspace = RunspaceFactory.CreateRunspace(initialSessionState);
result.Runspace.Open();
return result;
}
/// <summary>
/// Creates a nested powershell within the current instance.
/// Nested PowerShell is used to do simple operations like checking state
/// of a variable while another command is using the runspace.
///
/// Nested PowerShell should be invoked from the same thread as the parent
/// PowerShell invocation thread. So effectively the parent Powershell
/// invocation thread is blocked until nested invoke() operation is
/// complete.
///
/// Implement PSHost.EnterNestedPrompt to perform invoke() operation on the
/// nested powershell.
/// </summary>
/// <exception cref="InvalidOperationException">
/// 1. State of powershell instance is not valid to create a nested powershell instance.
/// Nested PowerShell should be created only for a running powershell instance.
/// </exception>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "ps", Justification = "ps represents PowerShell and is used at many places.")]
public PowerShell CreateNestedPowerShell()
{
if ((null != _worker) && (null != _worker.CurrentlyRunningPipeline))
{
PowerShell result = new PowerShell(new PSCommand(),
null, _worker.CurrentlyRunningPipeline.Runspace);
result.IsNested = true;
return result;
}
throw PSTraceSource.NewInvalidOperationException(PowerShellStrings.InvalidStateCreateNested);
}
/// <summary>
/// Method needed when deserializing PowerShell object coming from a RemoteDataObject
/// </summary>
/// <param name="isNested">Indicates if PowerShell object is nested</param>
/// <param name="psCommand">Commands that the PowerShell pipeline is built of</param>
/// <param name="extraCommands">Extra commands to run</param>
private static PowerShell Create(bool isNested, PSCommand psCommand, Collection<PSCommand> extraCommands)
{
PowerShell powerShell = new PowerShell(psCommand, extraCommands, null);
powerShell.IsNested = isNested;
return powerShell;
}
#endregion
#region Command / Parameter Construction
/// <summary>
/// Add a cmdlet to construct a command pipeline.
/// For example, to construct a command string "get-process | sort-object",
/// <code>
/// PowerShell shell = PowerShell.Create("get-process").AddCommand("sort-object");
/// </code>
/// </summary>
/// <param name="cmdlet">
/// A string representing cmdlet.
/// </param>
/// <returns>
/// A PowerShell instance with <paramref name="cmdlet"/> added.
/// </returns>
/// <remarks>
/// This method is not thread safe.
/// </remarks>
/// <exception cref="ArgumentNullException">
/// cmdlet is null.
/// </exception>
/// <exception cref="InvalidPowerShellStateException">
/// Powershell instance cannot be changed in its
/// current state.
/// </exception>
/// <exception cref="ObjectDisposedException">
/// Object is disposed.
/// </exception>
public PowerShell AddCommand(string cmdlet)
{
lock (_syncObject)
{
AssertChangesAreAccepted();
_psCommand.AddCommand(cmdlet);
return this;
}
}
/// <summary>
/// Add a cmdlet to construct a command pipeline.
/// For example, to construct a command string "get-process | sort-object",
/// <code>
/// PowerShell shell = PowerShell.Create("get-process").AddCommand("sort-object");
/// </code>
/// </summary>
/// <param name="cmdlet">
/// A string representing cmdlet.
/// </param>
/// <param name="useLocalScope">
/// if true local scope is used to run the script command.
/// </param>
/// <returns>
/// A PowerShell instance with <paramref name="cmdlet"/> added.
/// </returns>
/// <remarks>
/// This method is not thread safe.
/// </remarks>
/// <exception cref="ArgumentNullException">
/// cmdlet is null.
/// </exception>
/// <exception cref="InvalidPowerShellStateException">
/// Powershell instance cannot be changed in its
/// current state.
/// </exception>
/// <exception cref="ObjectDisposedException">
/// Object is disposed.
/// </exception>
public PowerShell AddCommand(string cmdlet, bool useLocalScope)
{
lock (_syncObject)
{
AssertChangesAreAccepted();
_psCommand.AddCommand(cmdlet, useLocalScope);
return this;
}
}
/// <summary>
/// Add a piece of script to construct a command pipeline.
/// For example, to construct a command string "get-process | foreach { $_.Name }"
/// <code>
/// PowerShell shell = PowerShell.Create("get-process").
/// AddCommand("foreach { $_.Name }", true);
/// </code>
/// </summary>
/// <param name="script">