/********************************************************************++
* Copyright (c) Microsoft Corporation. All rights reserved.
* --********************************************************************/
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading;
using System.Runtime.Serialization;
using System.Management.Automation.Host;
using System.Management.Automation.Remoting;
using Microsoft.PowerShell.Commands;
using System.Management.Automation.Internal;
using System.Diagnostics.CodeAnalysis; // for fxcop
using Dbg = System.Management.Automation.Diagnostics;
using System.Diagnostics;
using System.Linq;
using Microsoft.PowerShell.Telemetry.Internal;
#pragma warning disable 1634, 1691 // Stops compiler from warning about unknown warnings
namespace System.Management.Automation.Runspaces
{
///
/// Runspace class for local runspace
///
internal sealed partial class LocalRunspace : RunspaceBase
{
#region constructors
///
/// Construct an instance of an Runspace using a custom implementation
/// of PSHost.
///
///
/// The explicit PSHost implementation
///
///
/// configuration information for this minshell.
///
[SuppressMessage("Microsoft.Maintainability", "CA1505:AvoidUnmaintainableCode")]
internal LocalRunspace(PSHost host, RunspaceConfiguration runspaceConfig)
: base(host, runspaceConfig)
{
}
///
/// Construct an instance of an Runspace using a custom implementation
/// of PSHost.
///
///
/// The explicit PSHost implementation
///
///
/// configuration information for this minshell.
///
///
/// If true, don't make a copy of the initial session state object
///
[SuppressMessage("Microsoft.Maintainability", "CA1505:AvoidUnmaintainableCode")]
internal LocalRunspace(PSHost host, InitialSessionState initialSessionState, bool suppressClone)
: base(host, initialSessionState, suppressClone)
{
}
///
/// Construct an instance of an Runspace using a custom implementation
/// of PSHost.
///
///
/// The explicit PSHost implementation
///
///
/// configuration information for this minshell.
///
[SuppressMessage("Microsoft.Maintainability", "CA1505:AvoidUnmaintainableCode")]
internal LocalRunspace(PSHost host, InitialSessionState initialSessionState)
: base(host, initialSessionState)
{
}
#endregion constructors
///
/// Private data to be used by applications built on top of PowerShell.
///
/// Local runspace pool is created with application private data set to an empty .
///
/// Runspaces that are part of a inherit application private data from the pool.
///
public override PSPrimitiveDictionary GetApplicationPrivateData()
{
// if we didn't get applicationPrivateData from a runspace pool,
// then we create a new one
if (_applicationPrivateData == null)
{
lock (this.SyncRoot)
{
if (_applicationPrivateData == null)
{
_applicationPrivateData = new PSPrimitiveDictionary();
}
}
}
return _applicationPrivateData;
}
///
/// A method that runspace pools can use to propagate application private data into runspaces
///
///
internal override void SetApplicationPrivateData(PSPrimitiveDictionary applicationPrivateData)
{
_applicationPrivateData = applicationPrivateData;
}
private PSPrimitiveDictionary _applicationPrivateData;
///
/// Gets the event manager
///
public override PSEventManager Events
{
get
{
System.Management.Automation.ExecutionContext context = this.GetExecutionContext;
if (context == null)
{
return null;
}
return context.Events;
}
}
///
/// This property determines whether a new thread is create for each invocation
///
///
/// Any updates to the value of this property must be done before the Runspace is opened
///
///
/// An attempt to change this property was made after opening the Runspace
///
///
/// The thread options cannot be changed to the requested value
///
public override PSThreadOptions ThreadOptions
{
get
{
return _createThreadOptions;
}
set
{
lock (this.SyncRoot)
{
if (value != _createThreadOptions)
{
if (this.RunspaceStateInfo.State != RunspaceState.BeforeOpen)
{
#if CORECLR // No ApartmentState.STA Support In CoreCLR
bool allowed = value == PSThreadOptions.ReuseThread;
#else
// if the runspace is already opened we only allow changing the options if
// the apartment state is MTA and the new value is ReuseThread
bool allowed = (this.ApartmentState == ApartmentState.MTA || this.ApartmentState == ApartmentState.Unknown) // Unknown is the same as MTA
&&
value == PSThreadOptions.ReuseThread;
#endif
if (!allowed)
{
throw new InvalidOperationException(StringUtil.Format(RunspaceStrings.InvalidThreadOptionsChange));
}
}
_createThreadOptions = value;
}
}
}
}
private PSThreadOptions _createThreadOptions = PSThreadOptions.Default;
///
/// Resets the runspace state to allow for fast reuse. Not all of the runspace
/// elements are reset. The goal is to minimize the chance of the user taking
/// accidental dependencies on prior runspace state.
///
public override void ResetRunspaceState()
{
PSInvalidOperationException invalidOperation = null;
if (this.InitialSessionState == null)
{
invalidOperation = PSTraceSource.NewInvalidOperationException();
}
else if (this.RunspaceState != Runspaces.RunspaceState.Opened)
{
invalidOperation = PSTraceSource.NewInvalidOperationException(
RunspaceStrings.RunspaceNotInOpenedState, this.RunspaceState);
}
else if (this.RunspaceAvailability != Runspaces.RunspaceAvailability.Available)
{
invalidOperation = PSTraceSource.NewInvalidOperationException(
RunspaceStrings.ConcurrentInvokeNotAllowed);
}
if (invalidOperation != null)
{
invalidOperation.Source = "ResetRunspaceState";
throw invalidOperation;
}
this.InitialSessionState.ResetRunspaceState(this.ExecutionContext);
// Finally, reset history for this runspace. This needs to be done
// last to so that changes to the default MaximumHistorySize will be picked up.
_history = new History(this.ExecutionContext);
}
#region protected_methods
///
/// Create a pipeline from a command string
///
/// A valid command string. Can be null
/// if true command is added to history
/// True for nested pipeline
///
/// A pipeline pre-filled with Commands specified in commandString.
///
protected override Pipeline CoreCreatePipeline(string command, bool addToHistory, bool isNested)
{
// NTRAID#Windows Out Of Band Releases-915851-2005/09/13
if (_disposed)
{
throw PSTraceSource.NewObjectDisposedException("runspace");
}
return (Pipeline)new LocalPipeline(this, command, addToHistory, isNested);
}
#endregion protected_methods
#region protected_properties
///
/// Gets the execution context
///
internal override System.Management.Automation.ExecutionContext GetExecutionContext
{
get
{
if (_engine == null)
return null;
else
return _engine.Context;
}
}
///
/// Returns true if the internal host is in a nested prompt
///
internal override bool InNestedPrompt
{
get
{
System.Management.Automation.ExecutionContext context = this.GetExecutionContext;
if (context == null)
{
return false;
}
return context.InternalHost.HostInNestedPrompt();
}
}
#endregion protected_properties
#region internal_properties
///
/// CommandFactory object for this runspace.
///
internal CommandFactory CommandFactory
{
get
{
return _commandFactory;
}
}
///
/// Gets history manager for this runspace
///
///
internal History History
{
get
{
return _history;
}
}
///
/// Gets transcription data for this runspace
///
///
internal TranscriptionData TranscriptionData
{
get
{
return _transcriptionData;
}
}
private TranscriptionData _transcriptionData = null;
private JobRepository _jobRepository;
///
/// List of jobs in this runspace
///
internal JobRepository JobRepository
{
get
{
return _jobRepository;
}
}
private JobManager _jobManager;
///
/// Manager for JobSourceAdapters registered in this runspace.
///
public override JobManager JobManager
{
get
{
return _jobManager;
}
}
private RunspaceRepository _runspaceRepository;
///
/// List of remote runspaces in this runspace
///
internal RunspaceRepository RunspaceRepository
{
get
{
return _runspaceRepository;
}
}
#endregion internal_properties
#region Debugger
///
/// Debugger
///
public override Debugger Debugger
{
get
{
return InternalDebugger ?? base.Debugger;
}
}
private static string s_debugPreferenceCachePath = Path.Combine(Path.Combine(Platform.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "WindowsPowerShell"), "DebugPreference.clixml");
private static object s_debugPreferenceLockObject = new object();
///
/// DebugPreference serves as a property bag to keep
/// track of all process specific debug preferences.
///
public class DebugPreference
{
public string[] AppDomainNames;
}
///
/// CreateDebugPerfStruct is a helper method to populate DebugPreference
///
/// App Domain Names
/// DebugPreference
private static DebugPreference CreateDebugPreference(string[] AppDomainNames)
{
DebugPreference DebugPreference = new DebugPreference();
DebugPreference.AppDomainNames = AppDomainNames;
return DebugPreference;
}
///
/// SetDebugPreference is a helper method used to enable and disable debug preference.
///
/// Process Name
/// App Domain Name
/// Indicates if the debug preference has to be enabled or disabled.
internal static void SetDebugPreference(string processName, List appDomainName, bool enable)
{
lock (s_debugPreferenceLockObject)
{
bool iscacheUpdated = false;
Hashtable debugPreferenceCache = null;
string[] appDomainNames = null;
if (appDomainName != null)
{
appDomainNames = appDomainName.ToArray();
}
if (!File.Exists(LocalRunspace.s_debugPreferenceCachePath))
{
if (enable)
{
DebugPreference DebugPreference = CreateDebugPreference(appDomainNames);
debugPreferenceCache = new Hashtable();
debugPreferenceCache.Add(processName, DebugPreference);
iscacheUpdated = true;
}
}
else
{
debugPreferenceCache = GetDebugPreferenceCache(null);
if (debugPreferenceCache != null)
{
if (enable)
{
// Debug preference is set to enable.
// If the cache does not contain the process name, then we just update the cache.
if (!debugPreferenceCache.ContainsKey(processName))
{
DebugPreference DebugPreference = CreateDebugPreference(appDomainNames);
debugPreferenceCache.Add(processName, DebugPreference);
iscacheUpdated = true;
}
else
{
// In this case, the cache contains the process name, hence we check the list of
// app domains for which the debug preference is set to enable.
DebugPreference processDebugPreference = GetProcessSpecificDebugPreference(debugPreferenceCache[processName]);
// processDebugPreference would point to null if debug preference is enabled for all app domains.
// If processDebugPreference is not null then it means that user has selected specific
// appdomins for which the debug preference has to be enabled.
if (processDebugPreference != null)
{
List cachedAppDomainNames = null;
if (processDebugPreference.AppDomainNames != null && processDebugPreference.AppDomainNames.Length > 0)
{
cachedAppDomainNames = new List(processDebugPreference.AppDomainNames);
foreach (string currentAppDomainName in appDomainName)
{
if (!cachedAppDomainNames.Contains(currentAppDomainName, StringComparer.OrdinalIgnoreCase))
{
cachedAppDomainNames.Add(currentAppDomainName);
iscacheUpdated = true;
}
}
}
if (iscacheUpdated)
{
DebugPreference DebugPreference = CreateDebugPreference(cachedAppDomainNames.ToArray());
debugPreferenceCache[processName] = DebugPreference;
}
}
}
}
else
{
// Debug preference is set to disable.
if (debugPreferenceCache.ContainsKey(processName))
{
if (appDomainName == null)
{
debugPreferenceCache.Remove(processName);
iscacheUpdated = true;
}
else
{
DebugPreference processDebugPreference = GetProcessSpecificDebugPreference(debugPreferenceCache[processName]);
// processDebugPreference would point to null if debug preference is enabled for all app domains.
// If processDebugPreference is not null then it means that user has selected specific
// appdomins for which the debug preference has to be enabled.
if (processDebugPreference != null)
{
List cachedAppDomainNames = null;
if (processDebugPreference.AppDomainNames != null && processDebugPreference.AppDomainNames.Length > 0)
{
cachedAppDomainNames = new List(processDebugPreference.AppDomainNames);
foreach (string currentAppDomainName in appDomainName)
{
if (cachedAppDomainNames.Contains(currentAppDomainName, StringComparer.OrdinalIgnoreCase))
{
// remove requested appdomains debug preference details.
cachedAppDomainNames.Remove(currentAppDomainName);
iscacheUpdated = true;
}
}
}
if (iscacheUpdated)
{
DebugPreference DebugPreference = CreateDebugPreference(cachedAppDomainNames.ToArray());
debugPreferenceCache[processName] = DebugPreference;
}
}
}
}
}
}
else
{
// For whatever reason, cache is corrupted. Hence override the cache content.
if (enable)
{
debugPreferenceCache = new Hashtable();
DebugPreference DebugPreference = CreateDebugPreference(appDomainNames);
debugPreferenceCache.Add(processName, DebugPreference);
iscacheUpdated = true;
}
}
}
if (iscacheUpdated)
{
using (PowerShell ps = PowerShell.Create())
{
ps.AddCommand("Export-Clixml").AddParameter("Path", LocalRunspace.s_debugPreferenceCachePath).AddParameter("InputObject", debugPreferenceCache);
ps.Invoke();
}
}
}
}
///
/// GetDebugPreferenceCache is a helper method used to fetch
/// the debug preference cache contents as a Hashtable.
///
/// Runspace
/// If the Debug preference is persisted then a hashtable containing
/// the debug preference is returned or else Null is returned.
private static Hashtable GetDebugPreferenceCache(Runspace runspace)
{
Hashtable debugPreferenceCache = null;
using (PowerShell ps = PowerShell.Create())
{
if (runspace != null)
{
ps.Runspace = runspace;
}
ps.AddCommand("Import-Clixml").AddParameter("Path", LocalRunspace.s_debugPreferenceCachePath);
Collection psObjects = ps.Invoke();
if (psObjects != null && psObjects.Count == 1)
{
debugPreferenceCache = psObjects[0].BaseObject as Hashtable;
}
}
return debugPreferenceCache;
}
///
/// GetProcessSpecificDebugPreference is a helper method used to fetch persisted process specific debug preference.
///
///
///
private static DebugPreference GetProcessSpecificDebugPreference(object debugPreference)
{
DebugPreference processDebugPreference = null;
if (debugPreference != null)
{
PSObject debugPreferencePsObject = debugPreference as PSObject;
if (debugPreferencePsObject != null)
{
processDebugPreference = LanguagePrimitives.ConvertTo(debugPreferencePsObject);
}
}
return processDebugPreference;
}
#endregion
///
/// Open the runspace
///
///
/// parameter which control if Open is done synchronously or asynchronously
///
protected override void OpenHelper(bool syncCall)
{
if (syncCall)
{
//Open runspace synchronously
DoOpenHelper();
}
else
{
//Open runspace in another thread
Thread asyncThread = new Thread(new ThreadStart(this.OpenThreadProc));
asyncThread.Start();
}
}
///
/// Start method for asynchronous open
///
private void OpenThreadProc()
{
#pragma warning disable 56500
try
{
DoOpenHelper();
}
catch (Exception)
{
//This exception is reported by raising RunspaceState
//change event.
}
#pragma warning restore 56500
}
///
/// Helper function used for opening a runspace
///
private void DoOpenHelper()
{
// NTRAID#Windows Out Of Band Releases-915851-2005/09/13
if (_disposed)
{
throw PSTraceSource.NewObjectDisposedException("runspace");
}
bool startLifeCycleEventWritten = false;
s_runspaceInitTracer.WriteLine("begin open runspace");
try
{
_transcriptionData = new TranscriptionData();
if (InitialSessionState != null)
{
// All ISS-based configuration of the engine itself is done by AutomationEngine,
// which calls InitialSessionState.Bind(). Anything that doesn't
// require an active and open runspace should be done in ISS.Bind()
_engine = new AutomationEngine(Host, null, InitialSessionState);
}
else
{
_engine = new AutomationEngine(Host, RunspaceConfiguration, null);
}
_engine.Context.CurrentRunspace = this;
//Log engine for start of engine life
MshLog.LogEngineLifecycleEvent(_engine.Context, EngineState.Available);
startLifeCycleEventWritten = true;
_commandFactory = new CommandFactory(_engine.Context);
_history = new History(_engine.Context);
_jobRepository = new JobRepository();
_jobManager = new JobManager();
_runspaceRepository = new RunspaceRepository();
s_runspaceInitTracer.WriteLine("initializing built-in aliases and variable information");
InitializeDefaults();
}
catch (Exception exception)
{
s_runspaceInitTracer.WriteLine("Runspace open failed");
//Log engine health event
LogEngineHealthEvent(exception);
//Log engine for end of engine life
if (startLifeCycleEventWritten)
{
Dbg.Assert(_engine.Context != null, "if startLifeCycleEventWritten is true, ExecutionContext must be present");
MshLog.LogEngineLifecycleEvent(_engine.Context, EngineState.Stopped);
}
//Open failed. Set the RunspaceState to Broken.
SetRunspaceState(RunspaceState.Broken, exception);
//Raise the event
RaiseRunspaceStateEvents();
//Rethrow the exception. For asynchronous execution,
//OpenThreadProc will catch it. For synchronous execution
//caller of open will catch it.
throw;
}
SetRunspaceState(RunspaceState.Opened);
RunspaceOpening.Set();
//Raise the event
RaiseRunspaceStateEvents();
s_runspaceInitTracer.WriteLine("runspace opened successfully");
// Now do initial state configuration that requires an active runspace
if (InitialSessionState != null)
{
Exception initError = InitialSessionState.BindRunspace(this, s_runspaceInitTracer);
if (initError != null)
{
// Log engine health event
LogEngineHealthEvent(initError);
// Log engine for end of engine life
Debug.Assert(_engine.Context != null,
"if startLifeCycleEventWritten is true, ExecutionContext must be present");
MshLog.LogEngineLifecycleEvent(_engine.Context, EngineState.Stopped);
// Open failed. Set the RunspaceState to Broken.
SetRunspaceState(RunspaceState.Broken, initError);
// Raise the event
RaiseRunspaceStateEvents();
// Throw the exception. For asynchronous execution,
// OpenThreadProc will catch it. For synchronous execution
// caller of open will catch it.
throw initError;
}
}
TelemetryAPI.ReportLocalSessionCreated(InitialSessionState, TranscriptionData);
}
///
/// Logs engine health event
///
internal void LogEngineHealthEvent(Exception exception)
{
LogEngineHealthEvent(
exception,
Severity.Error,
MshLog.EVENT_ID_CONFIGURATION_FAILURE,
null);
}
///
/// Logs engine health event
///
internal void LogEngineHealthEvent(Exception exception,
Severity severity,
int id,
Dictionary additionalInfo)
{
Dbg.Assert(exception != null, "Caller should validate the parameter");
LogContext logContext = new LogContext();
logContext.EngineVersion = Version.ToString();
logContext.HostId = Host.InstanceId.ToString();
logContext.HostName = Host.Name;
logContext.HostVersion = Host.Version.ToString();
logContext.RunspaceId = InstanceId.ToString();
logContext.Severity = severity.ToString();
if (this.RunspaceConfiguration == null)
{
logContext.ShellId = Utils.DefaultPowerShellShellID;
}
else
{
logContext.ShellId = this.RunspaceConfiguration.ShellId;
}
MshLog.LogEngineHealthEvent(
logContext,
id,
exception,
additionalInfo);
}
///
/// Returns the thread that must be used to execute pipelines when CreateThreadOptions is ReuseThread
///
///
/// The pipeline calls this function after ensuring there is a single thread in the pipeline, so no locking is necessary
///
internal PipelineThread GetPipelineThread()
{
if (_pipelineThread == null)
{
#if CORECLR // No ApartmentState In CoreCLR
_pipelineThread = new PipelineThread();
#else
_pipelineThread = new PipelineThread(this.ApartmentState);
#endif
}
return _pipelineThread;
}
private PipelineThread _pipelineThread = null;
protected override void CloseHelper(bool syncCall)
{
if (syncCall)
{
//Do close synchronously
DoCloseHelper();
}
else
{
//Do close asynchronously
Thread asyncThread = new Thread(new ThreadStart(this.CloseThreadProc));
asyncThread.Start();
}
}
///
/// Start method for asynchronous close
///
private void CloseThreadProc()
{
#pragma warning disable 56500
try
{
DoCloseHelper();
}
catch (Exception)
{
}
#pragma warning restore 56500
}
///
/// Close the runspace.
///
///
/// Attempts to create/execute pipelines after a call to
/// close will fail.
///
private void DoCloseHelper()
{
// Stop any transcription if we're the last runspace to exit
ExecutionContext executionContext = this.GetExecutionContext;
if (executionContext != null)
{
Runspace hostRunspace = null;
try
{
hostRunspace = executionContext.EngineHostInterface.Runspace;
}
catch (PSNotImplementedException)
{
// EngineHostInterface.Runspace throws PSNotImplementedException if there
// is no interactive host.
}
if ((hostRunspace == null) || (this == hostRunspace))
{
PSHostUserInterface host = executionContext.EngineHostInterface.UI;
if (host != null)
{
host.StopAllTranscribing();
}
}
}
// Generate the shutdown event
if (Events != null)
Events.GenerateEvent(PSEngineEvent.Exiting, null, new object[] { }, null, true, false);
//Stop all running pipelines
//Note:Do not perform the Cancel in lock. Reason is
//Pipeline executes in separate thread, say threadP.
//When pipeline is canceled/failed/completed in
//Pipeline.ExecuteThreadProc it removes the pipeline
//from the list of running pipelines. threadP will need
//lock to remove the pipelines from the list of running pipelines
//And we will deadlock.
//Note:It is possible that one or more pipelines in the list
//of active pipelines have completed before we call cancel.
//That is fine since Pipeline.Cancel handles that( It ignores
//the cancel request if pipeline execution has already
//completed/failed/canceled.
StopPipelines();
// Disconnect all disconnectable jobs in the job repository.
StopOrDisconnectAllJobs();
// Close or disconnect all the remote runspaces available in the
// runspace repository.
CloseOrDisconnectAllRemoteRunspaces(() =>
{
List runspaces = new List();
foreach (PSSession psSession in this.RunspaceRepository.Runspaces)
{
runspaces.Add(psSession.Runspace as RemoteRunspace);
}
return runspaces;
});
//Notify Engine components that that runspace is closing.
_engine.Context.RunspaceClosingNotification();
//Log engine lifecycle event.
MshLog.LogEngineLifecycleEvent(_engine.Context, EngineState.Stopped);
// Uninitialize the AMSI scan interface
AmsiUtils.Uninitialize();
//All pipelines have been canceled. Close the runspace.
_engine = null;
_commandFactory = null;
SetRunspaceState(RunspaceState.Closed);
//Raise Event
RaiseRunspaceStateEvents();
// Report telemetry if we have no more open runspaces.
bool allRunspacesClosed = true;
bool hostProvidesExitTelemetry = false;
foreach (var r in Runspace.RunspaceList)
{
if (r.RunspaceStateInfo.State != RunspaceState.Closed)
{
allRunspacesClosed = false;
break;
}
var localRunspace = r as LocalRunspace;
if (localRunspace != null && localRunspace.Host is IHostProvidesTelemetryData)
{
hostProvidesExitTelemetry = true;
break;
}
}
if (allRunspacesClosed && !hostProvidesExitTelemetry)
{
TelemetryAPI.ReportExitTelemetry(null);
}
}
///
/// Closes or disconnects all the remote runspaces passed in by the getRunspace
/// function. If a remote runspace supports disconnect then it will be disconnected
/// rather than closed.
///
private void CloseOrDisconnectAllRemoteRunspaces(Func> getRunspaces)
{
List runspaces = getRunspaces();
if (runspaces.Count == 0) { return; }
// whether the close of all remoterunspaces completed
using (ManualResetEvent remoteRunspaceCloseCompleted = new ManualResetEvent(false))
{
ThrottleManager throttleManager = new ThrottleManager();
throttleManager.ThrottleComplete += delegate (object sender, EventArgs e)
{
remoteRunspaceCloseCompleted.Set();
};
foreach (RemoteRunspace remoteRunspace in runspaces)
{
IThrottleOperation operation = new CloseOrDisconnectRunspaceOperationHelper(remoteRunspace);
throttleManager.AddOperation(operation);
}
throttleManager.EndSubmitOperations();
remoteRunspaceCloseCompleted.WaitOne();
}
}
///
/// Disconnects all disconnectable jobs listed in the JobRepository.
///
private void StopOrDisconnectAllJobs()
{
if (JobRepository.Jobs.Count == 0) { return; }
List disconnectRunspaces = new List();
using (ManualResetEvent jobsStopCompleted = new ManualResetEvent(false))
{
ThrottleManager throttleManager = new ThrottleManager();
throttleManager.ThrottleComplete += delegate (object sender, EventArgs e)
{
jobsStopCompleted.Set();
};
foreach (Job job in this.JobRepository.Jobs)
{
// Only stop or disconnect PowerShell jobs.
if (job is PSRemotingJob == false)
{
continue;
}
if (!job.CanDisconnect)
{
// If the job cannot be disconnected then add it to
// the stop list.
throttleManager.AddOperation(new StopJobOperationHelper(job));
}
else if (job.JobStateInfo.State == JobState.Running)
{
// Otherwise add disconnectable runspaces to list so that
// they can be disconnected.
IEnumerable jobRunspaces = job.GetRunspaces();
if (jobRunspaces != null)
{
disconnectRunspaces.AddRange(jobRunspaces);
}
}
} // foreach job
// Stop jobs.
throttleManager.EndSubmitOperations();
jobsStopCompleted.WaitOne();
} // using jobsStopCompleted
// Disconnect all disconnectable job runspaces found.
CloseOrDisconnectAllRemoteRunspaces(() =>
{
return disconnectRunspaces;
});
}
internal void ReleaseDebugger()
{
Debugger debugger = Debugger;
if (debugger != null)
{
try
{
if (debugger.UnhandledBreakpointMode == UnhandledBreakpointProcessingMode.Wait)
{
// Sets the mode and also releases a held debug stop.
debugger.UnhandledBreakpointMode = UnhandledBreakpointProcessingMode.Ignore;
}
}
catch (PSNotImplementedException) { }
}
}
#region SessionStateProxy
protected override void DoSetVariable(string name, object value)
{
// NTRAID#Windows Out Of Band Releases-915851-2005/09/13
if (_disposed)
{
throw PSTraceSource.NewObjectDisposedException("runspace");
}
_engine.Context.EngineSessionState.SetVariableValue(name, value, CommandOrigin.Internal);
}
protected override object DoGetVariable(string name)
{
// NTRAID#Windows Out Of Band Releases-915851-2005/09/13
if (_disposed)
{
throw PSTraceSource.NewObjectDisposedException("runspace");
}
return _engine.Context.EngineSessionState.GetVariableValue(name);
}
protected override List DoApplications
{
get
{
if (_disposed)
{
throw PSTraceSource.NewObjectDisposedException("runspace");
}
return _engine.Context.EngineSessionState.Applications;
}
}
protected override List DoScripts
{
get
{
if (_disposed)
{
throw PSTraceSource.NewObjectDisposedException("runspace");
}
return _engine.Context.EngineSessionState.Scripts;
}
}
protected override DriveManagementIntrinsics DoDrive
{
get
{
if (_disposed)
{
throw PSTraceSource.NewObjectDisposedException("runspace");
}
return _engine.Context.SessionState.Drive;
}
}
protected override PSLanguageMode DoLanguageMode
{
get
{
if (_disposed)
{
throw PSTraceSource.NewObjectDisposedException("runspace");
}
return _engine.Context.SessionState.LanguageMode;
}
set
{
if (_disposed)
{
throw PSTraceSource.NewObjectDisposedException("runspace");
}
_engine.Context.SessionState.LanguageMode = value;
}
}
protected override PSModuleInfo DoModule
{
get
{
if (_disposed)
{
throw PSTraceSource.NewObjectDisposedException("runspace");
}
return _engine.Context.EngineSessionState.Module;
}
}
protected override PathIntrinsics DoPath
{
get
{
if (_disposed)
{
throw PSTraceSource.NewObjectDisposedException("runspace");
}
return _engine.Context.SessionState.Path;
}
}
protected override CmdletProviderManagementIntrinsics DoProvider
{
get
{
if (_disposed)
{
throw PSTraceSource.NewObjectDisposedException("runspace");
}
return _engine.Context.SessionState.Provider;
}
}
protected override PSVariableIntrinsics DoPSVariable
{
get
{
if (_disposed)
{
throw PSTraceSource.NewObjectDisposedException("runspace");
}
return _engine.Context.SessionState.PSVariable;
}
}
protected override CommandInvocationIntrinsics DoInvokeCommand
{
get
{
if (_disposed)
{
throw PSTraceSource.NewObjectDisposedException("runspace");
}
return _engine.Context.EngineIntrinsics.InvokeCommand;
}
}
protected override ProviderIntrinsics DoInvokeProvider
{
get
{
if (_disposed)
{
throw PSTraceSource.NewObjectDisposedException("runspace");
}
return _engine.Context.EngineIntrinsics.InvokeProvider;
}
}
#endregion SessionStateProxy
#region IDisposable Members
///
/// Set to true when object is disposed
///
private bool _disposed;
///
/// Protected dispose which can be overridden by derived classes.
///
///
[SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed", MessageId = "pipelineThread", Justification = "pipelineThread is disposed in Close()")]
protected override void Dispose(bool disposing)
{
try
{
if (_disposed)
{
return;
}
lock (SyncRoot)
{
if (_disposed)
{
return;
}
_disposed = true;
}
if (disposing)
{
Close();
_engine = null;
_history = null;
_transcriptionData = null;
_jobManager = null;
_jobRepository = null;
_runspaceRepository = null;
if (RunspaceOpening != null)
{
RunspaceOpening.Dispose();
RunspaceOpening = null;
}
Platform.RemoveTemporaryDirectory();
// Dispose the event manager
if (this.ExecutionContext != null && this.ExecutionContext.Events != null)
{
try
{
this.ExecutionContext.Events.Dispose();
}
catch (ObjectDisposedException)
{
;
}
}
}
}
finally
{
base.Dispose(disposing);
}
}
///
/// Close the runspace
///
public override void Close()
{
// Do not put cleanup activities in here, as they aren't
// captured in CloseAsync() case. Instead, put them in
// DoCloseHelper()
base.Close(); // call base.Close() first to make it stop the pipeline
if (_pipelineThread != null)
{
_pipelineThread.Close();
}
}
#endregion IDisposable Members
#region private fields
///
/// CommandFactory for creating Command objects
///
private CommandFactory _commandFactory;
///
/// AutomationEngine instance for this runspace
///
private AutomationEngine _engine;
internal AutomationEngine Engine
{
get
{
return _engine;
}
}
///
/// Manages history for this runspace
///
private History _history;
[TraceSource("RunspaceInit", "Initialization code for Runspace")]
private static
PSTraceSource s_runspaceInitTracer =
PSTraceSource.GetTracer("RunspaceInit", "Initialization code for Runspace", false);
///
/// This ensures all processes have a server/listener.
///
private static RemoteSessionNamedPipeServer s_IPCNamedPipeServer = RemoteSessionNamedPipeServer.IPCNamedPipeServer;
#endregion private fields
}
#region Helper Class
///
/// Helper class to stop a running job.
///
internal sealed class StopJobOperationHelper : IThrottleOperation
{
private Job _job;
///
/// Internal constructor
///
/// Job object to stop.
internal StopJobOperationHelper(Job job)
{
_job = job;
_job.StateChanged += new EventHandler(HandleJobStateChanged);
}
///
/// Handles the Job state change event.
///
/// Originator of event, unused
/// Event arguments containing Job state.
private void HandleJobStateChanged(object sender, JobStateEventArgs eventArgs)
{
if (_job.IsFinishedState(_job.JobStateInfo.State))
{
// We are done when the job is in the finished state.
RaiseOperationCompleteEvent();
}
}
///
/// Override method to start the operation.
///
internal override void StartOperation()
{
if (_job.IsFinishedState(_job.JobStateInfo.State))
{
// The job is already in the finished state and so cannot be stopped.
RaiseOperationCompleteEvent();
}
else
{
// Otherwise stop the job.
_job.StopJob();
}
}
///
/// Override method to stop the operation. Not used, stop operation must
/// run to completion.
///
internal override void StopOperation()
{
}
///
/// Event to signal ThrottleManager when the operation is complete.
///
internal override event EventHandler OperationComplete;
///
/// Raise the OperationComplete event.
///
private void RaiseOperationCompleteEvent()
{
_job.StateChanged -= new EventHandler(HandleJobStateChanged);
OperationStateEventArgs operationStateArgs = new OperationStateEventArgs();
operationStateArgs.OperationState = OperationState.StartComplete;
operationStateArgs.BaseEvent = EventArgs.Empty;
OperationComplete.SafeInvoke(this, operationStateArgs);
}
}
///
/// Helper class to disconnect a runspace if the runspace supports disconnect
/// semantics or otherwise close the runspace.
///
internal sealed class CloseOrDisconnectRunspaceOperationHelper : IThrottleOperation
{
private RemoteRunspace _remoteRunspace;
///
/// Internal constructor
///
///
internal CloseOrDisconnectRunspaceOperationHelper(RemoteRunspace remoteRunspace)
{
_remoteRunspace = remoteRunspace;
_remoteRunspace.StateChanged += new EventHandler(HandleRunspaceStateChanged);
}
///
/// Handle the runspace state changed event
///
/// sender of this information, unused
/// runspace event args
private void HandleRunspaceStateChanged(object sender, RunspaceStateEventArgs eventArgs)
{
switch (eventArgs.RunspaceStateInfo.State)
{
case RunspaceState.BeforeOpen:
case RunspaceState.Closing:
case RunspaceState.Opened:
case RunspaceState.Opening:
case RunspaceState.Disconnecting:
return;
}
//remoteRunspace.Dispose();
//remoteRunspace = null;
RaiseOperationCompleteEvent();
}
///
/// Start the operation of closing the runspace
///
internal override void StartOperation()
{
if (_remoteRunspace.RunspaceStateInfo.State == RunspaceState.Closed ||
_remoteRunspace.RunspaceStateInfo.State == RunspaceState.Broken ||
_remoteRunspace.RunspaceStateInfo.State == RunspaceState.Disconnected)
{
// If the runspace is currently in a disconnected state then leave it
// as is.
// in this case, calling a close won't raise any events. Simply raise
// the OperationCompleted event. After the if check, but before we
// get to this point if the state was changed, then the StateChanged
// event handler will anyway raise the event and so we are fine
RaiseOperationCompleteEvent();
}
else
{
// If the runspace supports disconnect semantics and is running a command,
// then disconnect it rather than closing it.
if (_remoteRunspace.CanDisconnect &&
_remoteRunspace.GetCurrentlyRunningPipeline() != null)
{
_remoteRunspace.DisconnectAsync();
}
else
{
_remoteRunspace.CloseAsync();
}
}
}
///
/// There is no scenario where we are going to cancel this close
/// Hence this method is intentionally empty
///
internal override void StopOperation()
{
}
///
/// Event raised when the required operation is complete
///
internal override event EventHandler OperationComplete;
///
/// Raise the operation completed event
///
private void RaiseOperationCompleteEvent()
{
_remoteRunspace.StateChanged -= new EventHandler(HandleRunspaceStateChanged);
OperationStateEventArgs operationStateEventArgs =
new OperationStateEventArgs();
operationStateEventArgs.OperationState =
OperationState.StartComplete;
operationStateEventArgs.BaseEvent = EventArgs.Empty;
OperationComplete.SafeInvoke(this, operationStateEventArgs);
}
}
///
/// Defines the exception thrown an error loading modules occurs while opening the runspace. It
/// contains a list of all of the module errors that have occurred
///
[Serializable]
public class RunspaceOpenModuleLoadException : RuntimeException
{
#region ctor
///
/// Initializes a new instance of ScriptBlockToPowerShellNotSupportedException
/// with the message set to typeof(ScriptBlockToPowerShellNotSupportedException).FullName
///
public RunspaceOpenModuleLoadException()
: base(typeof(ScriptBlockToPowerShellNotSupportedException).FullName)
{
}
///
/// Initializes a new instance of ScriptBlockToPowerShellNotSupportedException setting the message
///
/// the exception's message
public RunspaceOpenModuleLoadException(string message)
: base(message)
{
}
///
/// Initializes a new instance of ScriptBlockToPowerShellNotSupportedException setting the message and innerException
///
/// the exception's message
/// the exceptions's inner exception
public RunspaceOpenModuleLoadException(string message, Exception innerException)
: base(message, innerException)
{
}
///
/// Recommended constructor for the class
///
/// The name of the module that cause the error
/// The collection of errors that occurred during module processing
internal RunspaceOpenModuleLoadException(
string moduleName,
PSDataCollection errors)
: base(StringUtil.Format(RunspaceStrings.ErrorLoadingModulesOnRunspaceOpen, moduleName,
(errors != null && errors.Count > 0 && errors[0] != null) ? errors[0].ToString() : String.Empty), null)
{
_errors = errors;
this.SetErrorId("ErrorLoadingModulesOnRunspaceOpen");
this.SetErrorCategory(ErrorCategory.OpenError);
}
#endregion ctor
///
/// The collection of error records generated while loading the modules.
///
public PSDataCollection ErrorRecords
{
get { return _errors; }
}
private PSDataCollection _errors;
#region Serialization
///
/// Initializes a new instance of RunspaceOpenModuleLoadException with serialization parameters
///
/// serialization information
/// streaming context
protected RunspaceOpenModuleLoadException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
///
/// Populates a with the
/// data needed to serialize the RunspaceOpenModuleLoadException object.
///
/// The to populate with data.
/// The destination for this serialization.
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
if (info == null)
{
throw new PSArgumentNullException("info");
}
base.GetObjectData(info, context);
}
#endregion Serialization
} // RunspaceOpenException
#endregion Helper Class
}