/// <summary> /// unlock the shell / command specified so that the shell / command /// starts sending data to the client. /// </summary> /// <param name="pluginContext"></param> /// <param name="requestDetails"></param> /// <param name="flags"></param> /// <param name="shellContext"></param> /// <param name="commandContext"></param> /// <param name="streamSet"></param> internal void EnableShellOrCommandToSendDataToClient( IntPtr pluginContext, WSManNativeApi.WSManPluginRequest requestDetails, int flags, IntPtr shellContext, IntPtr commandContext, WSManNativeApi.WSManStreamIDSet_UnToMan streamSet) { if (!validateIncomingContexts(requestDetails, shellContext, "EnableShellOrCommandToSendDataToClient")) { return; } SetThreadProperties(requestDetails); PSEtwLog.LogAnalyticInformational(PSEventId.ServerClientReceiveRequest, PSOpcode.Open, PSTask.None, PSKeyword.ManagedPlugin | PSKeyword.UseAlwaysAnalytic, ((IntPtr)shellContext).ToString(), ((IntPtr)commandContext).ToString(), requestDetails.ToString()); WSManPluginShellSession mgdShellSession = GetFromActiveShellSessions(shellContext); if (null == mgdShellSession) { ReportOperationComplete( requestDetails, WSManPluginErrorCodes.InvalidShellContext, StringUtil.Format( RemotingErrorIdStrings.WSManPluginInvalidShellContext) ); return; } WSManPluginOperationShutdownContext ctxtToReport = new WSManPluginOperationShutdownContext(pluginContext, shellContext, IntPtr.Zero, true); if (null == ctxtToReport) { ReportOperationComplete(requestDetails, WSManPluginErrorCodes.OutOfMemory); return; } if (IntPtr.Zero == commandContext) { // the instruction is destined for shell (runspace) session. so let shell handle it if (mgdShellSession.EnableSessionToSendDataToClient(requestDetails, flags, streamSet, ctxtToReport)) { return; } } else { // the instruction is destined for command ctxtToReport.commandContext = commandContext; WSManPluginCommandSession mgdCmdSession = mgdShellSession.GetCommandSession(commandContext); if (null == mgdCmdSession) { ReportOperationComplete( requestDetails, WSManPluginErrorCodes.InvalidCommandContext, StringUtil.Format( RemotingErrorIdStrings.WSManPluginInvalidCommandContext)); return; } if (mgdCmdSession.EnableSessionToSendDataToClient(requestDetails, flags, streamSet, ctxtToReport)) { return; } } }
/// <summary> /// This gets called on a thread pool thread once Shutdown wait handle is notified. /// </summary> /// <param name="context"></param> internal void CloseShellOperation( WSManPluginOperationShutdownContext context) { PSEtwLog.LogAnalyticInformational(PSEventId.ServerCloseOperation, PSOpcode.Disconnect, PSTask.None, PSKeyword.ManagedPlugin | PSKeyword.UseAlwaysAnalytic, ((IntPtr)context.shellContext).ToString(), ((IntPtr)context.commandContext).ToString(), context.isReceiveOperation.ToString()); WSManPluginShellSession mgdShellSession = GetFromActiveShellSessions(context.shellContext); if (null == mgdShellSession) { // this should never be the case. this will protect the service. //Dbg.Assert(false, "context.shellContext not matched"); return; } SetThreadProperties(mgdShellSession.creationRequestDetails); // update the internal data store only if this is not receive operation. if (!context.isReceiveOperation) { DeleteFromActiveShellSessions(context.shellContext); } string errorMsg = StringUtil.Format(RemotingErrorIdStrings.WSManPluginOperationClose); System.Exception reasonForClose = new System.Exception(errorMsg); mgdShellSession.CloseOperation(context, reasonForClose); }
internal void CloseCommandOperation( WSManPluginOperationShutdownContext context) { PSEtwLog.LogAnalyticInformational(PSEventId.ServerCloseOperation, PSOpcode.Disconnect, PSTask.None, PSKeyword.ManagedPlugin | PSKeyword.UseAlwaysAnalytic, context.shellContext.ToString(), context.commandContext.ToString(), context.isReceiveOperation.ToString()); WSManPluginShellSession mgdShellSession = GetFromActiveShellSessions(context.shellContext); if (null == mgdShellSession) { // this should never be the case. this will protect the service. //Dbg.Assert(false, "context.shellContext not matched"); return; } SetThreadProperties(mgdShellSession.creationRequestDetails); mgdShellSession.CloseCommandOperation(context); }
/// <summary> /// Close the operation specified by the supplied context. /// </summary> /// <param name="context"></param> internal static void PerformCloseOperation( WSManPluginOperationShutdownContext context) { WSManPluginInstance pluginToUse = GetFromActivePlugins(context.pluginContext); if (null == pluginToUse) { return; } if (IntPtr.Zero == context.commandContext) { // this is targeted at shell pluginToUse.CloseShellOperation(context); } else { // shutdown is targeted at command pluginToUse.CloseCommandOperation(context); } }
/// <summary> /// Create a new shell in the plugin context. /// </summary> /// <param name="pluginContext"></param> /// <param name="requestDetails"></param> /// <param name="flags"></param> /// <param name="extraInfo"></param> /// <param name="startupInfo"></param> /// <param name="inboundShellInformation"></param> internal void CreateShell( IntPtr pluginContext, WSManNativeApi.WSManPluginRequest requestDetails, int flags, string extraInfo, WSManNativeApi.WSManShellStartupInfo_UnToMan startupInfo, WSManNativeApi.WSManData_UnToMan inboundShellInformation) { if (null == requestDetails) { // Nothing can be done because requestDetails are required to report operation complete PSEtwLog.LogAnalyticInformational(PSEventId.ReportOperationComplete, PSOpcode.Close, PSTask.None, PSKeyword.ManagedPlugin | PSKeyword.UseAlwaysAnalytic, "null", Convert.ToString(WSManPluginErrorCodes.NullInvalidInput, CultureInfo.InvariantCulture), StringUtil.Format( RemotingErrorIdStrings.WSManPluginNullInvalidInput, "requestDetails", "WSManPluginShell"), String.Empty); return; } if ((null == requestDetails.senderDetails) || (null == requestDetails.operationInfo)) { ReportOperationComplete( requestDetails, WSManPluginErrorCodes.NullInvalidInput, StringUtil.Format( RemotingErrorIdStrings.WSManPluginNullInvalidInput, "requestDetails", "WSManPluginShell")); return; } if (null == startupInfo) { ReportOperationComplete( requestDetails, WSManPluginErrorCodes.NullInvalidInput, StringUtil.Format( RemotingErrorIdStrings.WSManPluginNullInvalidInput, "startupInfo", "WSManPluginShell")); return; } if ((0 == startupInfo.inputStreamSet.streamIDsCount) || (0 == startupInfo.outputStreamSet.streamIDsCount)) { ReportOperationComplete( requestDetails, WSManPluginErrorCodes.NullInvalidStreamSets, StringUtil.Format( RemotingErrorIdStrings.WSManPluginNullInvalidStreamSet, WSManPluginConstants.SupportedInputStream, WSManPluginConstants.SupportedOutputStream)); return; } if (String.IsNullOrEmpty(extraInfo)) { ReportOperationComplete( requestDetails, WSManPluginErrorCodes.NullInvalidInput, StringUtil.Format( RemotingErrorIdStrings.WSManPluginNullInvalidInput, "extraInfo", "WSManPluginShell")); return; } WSManPluginInstance.SetThreadProperties(requestDetails); // check if protocolversion option is honored if (!EnsureOptionsComply(requestDetails)) { return; } int result = WSManPluginConstants.ExitCodeSuccess; WSManPluginShellSession mgdShellSession; WSManPluginOperationShutdownContext context; System.Byte[] convertedBase64 = null; try { PSSenderInfo senderInfo = GetPSSenderInfo(requestDetails.senderDetails); // inbound shell information is already verified by pwrshplugin.dll.. so no need // to verify here. WSManPluginServerTransportManager serverTransportMgr; if (Platform.IsWindows) { serverTransportMgr = new WSManPluginServerTransportManager(BaseTransportManager.DefaultFragmentSize, new PSRemotingCryptoHelperServer()); } else { serverTransportMgr = new WSManPluginServerTransportManager(BaseTransportManager.DefaultFragmentSize, null); } PSEtwLog.LogAnalyticInformational(PSEventId.ServerCreateRemoteSession, PSOpcode.Connect, PSTask.None, PSKeyword.ManagedPlugin | PSKeyword.UseAlwaysAnalytic, requestDetails.ToString(), senderInfo.UserInfo.Identity.Name, requestDetails.resourceUri); ServerRemoteSession remoteShellSession = ServerRemoteSession.CreateServerRemoteSession(senderInfo, requestDetails.resourceUri, extraInfo, serverTransportMgr); if (null == remoteShellSession) { WSManPluginInstance.ReportWSManOperationComplete( requestDetails, WSManPluginErrorCodes.SessionCreationFailed); return; } context = new WSManPluginOperationShutdownContext(pluginContext, requestDetails.unmanagedHandle, IntPtr.Zero, false); if (null == context) { ReportOperationComplete(requestDetails, WSManPluginErrorCodes.OutOfMemory); return; } // Create a shell session wrapper to track and service future interactions. mgdShellSession = new WSManPluginShellSession(requestDetails, serverTransportMgr, remoteShellSession, context); AddToActiveShellSessions(mgdShellSession); mgdShellSession.SessionClosed += new EventHandler<EventArgs>(HandleShellSessionClosed); if (null != inboundShellInformation) { if ((uint)WSManNativeApi.WSManDataType.WSMAN_DATA_TYPE_TEXT != inboundShellInformation.Type) { // only text data is supported ReportOperationComplete( requestDetails, WSManPluginErrorCodes.InvalidInputDatatype, StringUtil.Format( RemotingErrorIdStrings.WSManPluginInvalidInputDataType, "WSMAN_DATA_TYPE_TEXT")); DeleteFromActiveShellSessions(requestDetails.unmanagedHandle); return; } else { convertedBase64 = ServerOperationHelpers.ExtractEncodedXmlElement( inboundShellInformation.Text, WSManNativeApi.PS_CREATION_XML_TAG); } } // now report the shell context to WSMan. PSEtwLog.LogAnalyticInformational(PSEventId.ReportContext, PSOpcode.Connect, PSTask.None, PSKeyword.ManagedPlugin | PSKeyword.UseAlwaysAnalytic, requestDetails.ToString(), requestDetails.ToString()); result = wsmanPinvokeStatic.WSManPluginReportContext(requestDetails.unmanagedHandle, 0, requestDetails.unmanagedHandle); if (WSManPluginConstants.ExitCodeSuccess != result) { ReportOperationComplete( requestDetails, WSManPluginErrorCodes.ReportContextFailed, StringUtil.Format( RemotingErrorIdStrings.WSManPluginReportContextFailed)); DeleteFromActiveShellSessions(requestDetails.unmanagedHandle); return; } } catch (System.Exception e) { CommandProcessorBase.CheckForSevereException(e); PSEtwLog.LogOperationalError(PSEventId.TransportError, PSOpcode.Connect, PSTask.None, PSKeyword.UseAlwaysOperational, "00000000-0000-0000-0000-000000000000", "00000000-0000-0000-0000-000000000000", Convert.ToString(WSManPluginErrorCodes.ManagedException, CultureInfo.InvariantCulture), e.Message, e.StackTrace); DeleteFromActiveShellSessions(requestDetails.unmanagedHandle); ReportOperationComplete( requestDetails, WSManPluginErrorCodes.ManagedException, StringUtil.Format( RemotingErrorIdStrings.WSManPluginManagedException, e.Message)); return; } bool isRegisterWaitForSingleObjectSucceeded = true; //always synchronize calls to OperationComplete once notification handle is registered.. else duplicate OperationComplete calls are bound to happen lock (mgdShellSession.shellSyncObject) { mgdShellSession.registeredShutdownNotification = 1; // Wrap the provided handle so it can be passed to the registration function EventWaitHandle eventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset); if (Platform.IsWindows) { SafeWaitHandle safeWaitHandle = new SafeWaitHandle(requestDetails.shutdownNotificationHandle, false); // Owned by WinRM ClrFacade.SetSafeWaitHandle(eventWaitHandle, safeWaitHandle); } else { //On non-windows platforms the shutdown notification is done through a callback instead of a windows event handle. //Register the callback and this will then signal the event. Note, the gch object is deleted in the shell shutdown //notification that will always come in to shut down the operation. GCHandle gch = GCHandle.Alloc(eventWaitHandle); IntPtr p = GCHandle.ToIntPtr(gch); wsmanPinvokeStatic.WSManPluginRegisterShutdownCallback( requestDetails.unmanagedHandle, WSManPluginManagedEntryWrapper.workerPtrs.UnmanagedStruct.wsManPluginShutdownCallbackNative, p); } mgdShellSession.registeredShutDownWaitHandle = ThreadPool.RegisterWaitForSingleObject( eventWaitHandle, new WaitOrTimerCallback(WSManPluginManagedEntryWrapper.PSPluginOperationShutdownCallback), context, -1, // INFINITE true); // TODO: Do I need to worry not being able to set missing WT_TRANSFER_IMPERSONATION? if (null == mgdShellSession.registeredShutDownWaitHandle) { isRegisterWaitForSingleObjectSucceeded = false; } } if (!isRegisterWaitForSingleObjectSucceeded) { mgdShellSession.registeredShutdownNotification = 0; WSManPluginInstance.ReportWSManOperationComplete( requestDetails, WSManPluginErrorCodes.ShutdownRegistrationFailed); DeleteFromActiveShellSessions(requestDetails.unmanagedHandle); return; } try { if (convertedBase64 != null) { mgdShellSession.SendOneItemToSessionHelper(convertedBase64, WSManPluginConstants.SupportedInputStream); } } catch (System.Exception e) { CommandProcessorBase.CheckForSevereException(e); PSEtwLog.LogOperationalError(PSEventId.TransportError, PSOpcode.Connect, PSTask.None, PSKeyword.UseAlwaysOperational, "00000000-0000-0000-0000-000000000000", "00000000-0000-0000-0000-000000000000", Convert.ToString(WSManPluginErrorCodes.ManagedException, CultureInfo.InvariantCulture), e.Message, e.StackTrace); if (Interlocked.Exchange(ref mgdShellSession.registeredShutdownNotification, 0) == 1) { // unregister callback.. wait for any ongoing callbacks to complete.. nothing much we could do if this fails bool ignore = mgdShellSession.registeredShutDownWaitHandle.Unregister(null); mgdShellSession.registeredShutDownWaitHandle = null; //this will called OperationComplete PerformCloseOperation(context); } return; } return; }
internal override void CloseOperation( WSManPluginOperationShutdownContext context, Exception reasonForClose) { // let command sessions to close. lock (cmdSyncObject) { if (true == isClosed) { return; } if (!context.isReceiveOperation) { isClosed = true; } } bool isRcvOp = context.isReceiveOperation; // only one thread will be here. bool isRcvOpShuttingDown = (context.isShuttingDown) && (context.isReceiveOperation) && (context.commandContext == creationRequestDetails.unmanagedHandle); bool isCmdShuttingDown = (context.isShuttingDown) && (!context.isReceiveOperation) && (context.commandContext == creationRequestDetails.unmanagedHandle); // close the pending send operation if any ReportSendOperationComplete(); // close the shell's transport manager first..so we wont send data. transportMgr.DoClose(isRcvOpShuttingDown, reasonForClose); if (!isRcvOp) { // raise session closed event and let dependent code to release resources. // null check is not performed here because Managed C++ will take care of this. base.SafeInvokeSessionClosed(creationRequestDetails.unmanagedHandle, EventArgs.Empty); // Send Operation Complete to WSMan service WSManPluginInstance.ReportWSManOperationComplete(creationRequestDetails, reasonForClose); // let base class release its resources this.Close(isCmdShuttingDown); } }
internal static void PerformWSManPluginSignal( IntPtr pluginContext, // PVOID IntPtr requestDetails, // WSMAN_PLUGIN_REQUEST* int flags, IntPtr shellContext, // PVOID IntPtr commandContext, // PVOID string code) { WSManNativeApi.WSManPluginRequest request = WSManNativeApi.WSManPluginRequest.UnMarshal(requestDetails); // Close Command if (IntPtr.Zero != commandContext) { if (!String.Equals(code, WSManPluginConstants.CtrlCSignal, StringComparison.Ordinal)) { // Close operations associated with this command.. WSManPluginOperationShutdownContext cmdCtxt = new WSManPluginOperationShutdownContext(pluginContext, shellContext, commandContext, false); if (null != cmdCtxt) { PerformCloseOperation(cmdCtxt); } else { ReportOperationComplete(request, WSManPluginErrorCodes.OutOfMemory); return; } } else { // we got crtl_c (stop) message from client. so stop powershell WSManPluginInstance pluginToUse = GetFromActivePlugins(pluginContext); if (null == pluginToUse) { ReportOperationComplete( request, WSManPluginErrorCodes.PluginContextNotFound, StringUtil.Format( RemotingErrorIdStrings.WSManPluginContextNotFound)); return; } // this will ReportOperationComplete by itself.. // so we just here. pluginToUse.StopCommand(request, shellContext, commandContext); return; } } ReportOperationComplete(request, WSManPluginErrorCodes.NoError); }
// Closes the command and clears associated resources internal void CloseCommandOperation( WSManPluginOperationShutdownContext context) { WSManPluginCommandSession mgdCmdSession = GetCommandSession(context.commandContext); if (null == mgdCmdSession) { // this should never be the case. this will protect the service. return; } // update the internal data store only if this is not receive operation. if (!context.isReceiveOperation) { DeleteFromActiveCmdSessions(mgdCmdSession.creationRequestDetails.unmanagedHandle); } mgdCmdSession.CloseOperation(context, null); }
internal override void CloseOperation( WSManPluginOperationShutdownContext context, Exception reasonForClose) { // let command sessions to close. lock (shellSyncObject) { if (true == isClosed) { return; } if (!context.isReceiveOperation) { isClosed = true; } } bool isRcvOpShuttingDown = (context.isShuttingDown) && (context.isReceiveOperation); bool isRcvOp = context.isReceiveOperation; bool isShuttingDown = context.isShuttingDown; // close the pending send operation if any ReportSendOperationComplete(); // close the shell's transport manager after commands handled the operation transportMgr.DoClose(isRcvOpShuttingDown, reasonForClose); if (!isRcvOp) { // Initiate close on the active command sessions and then clear the internal // Command Session dictionary CloseAndClearCommandSessions(reasonForClose); // raise session closed event and let dependent code to release resources. // null check is not performed here because the handler will take care of this. base.SafeInvokeSessionClosed(creationRequestDetails.unmanagedHandle, EventArgs.Empty); // Send Operation Complete to WSMan service WSManPluginInstance.ReportWSManOperationComplete(creationRequestDetails, reasonForClose); // let base class release its resources base.Close(isShuttingDown); } // TODO: Do this.Dispose(); here? }
internal abstract void CloseOperation(WSManPluginOperationShutdownContext context, Exception reasonForClose);
internal WSManPluginShellSession( WSManNativeApi.WSManPluginRequest creationRequestDetails, WSManPluginServerTransportManager transportMgr, ServerRemoteSession remoteSession, WSManPluginOperationShutdownContext shutDownContext) : base(creationRequestDetails, transportMgr) { _remoteSession = remoteSession; _remoteSession.Closed += new EventHandler<RemoteSessionStateMachineEventArgs>(this.HandleServerRemoteSessionClosed); _activeCommandSessions = new Dictionary<IntPtr, WSManPluginCommandSession>(); this.shellSyncObject = new System.Object(); this.shutDownContext = shutDownContext; }
// close current session and transport manager because of an exception internal void Close(Exception reasonForClose) { lastErrorReported = reasonForClose; WSManPluginOperationShutdownContext context = new WSManPluginOperationShutdownContext(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, false); CloseOperation(context, reasonForClose); }
internal void Close(bool isShuttingDown) { if (Interlocked.Exchange(ref registeredShutdownNotification, 0) == 1) { // release the shutdown notification handle. if (null != registeredShutDownWaitHandle) { registeredShutDownWaitHandle.Unregister(null); registeredShutDownWaitHandle = null; } } // Delete the context only if isShuttingDown != true. isShuttingDown will // be true only when the method is called from RegisterWaitForSingleObject // handler..in which case the context will be freed from the callback. if (null != shutDownContext) { shutDownContext = null; } transportMgr.WSManTransportErrorOccured -= new EventHandler<TransportErrorOccuredEventArgs>(this.HandleTransportError); // We should not use request details again after so releasing the resource. // Remember not to free this memory as this memory is allocated and owned by WSMan. creationRequestDetails = null; // if already disposing..no need to let finalizer thread // put resources to clean this object. //System.GC.SuppressFinalize(this); // TODO: This is already called in Dispose(). }
internal bool EnableSessionToSendDataToClient( WSManNativeApi.WSManPluginRequest requestDetails, int flags, WSManNativeApi.WSManStreamIDSet_UnToMan streamSet, WSManPluginOperationShutdownContext ctxtToReport) { if (true == isClosed) { WSManPluginInstance.ReportWSManOperationComplete(requestDetails, lastErrorReported); return false; } if ((null == streamSet) || (1 != streamSet.streamIDsCount)) { // only "stdout" is the supported output stream. WSManPluginInstance.ReportOperationComplete( requestDetails, WSManPluginErrorCodes.InvalidOutputStream, StringUtil.Format( RemotingErrorIdStrings.WSManPluginInvalidOutputStream, WSManPluginConstants.SupportedOutputStream)); return false; } if (!String.Equals(streamSet.streamIDs[0], WSManPluginConstants.SupportedOutputStream, StringComparison.Ordinal)) { // only "stdout" is the supported output stream. WSManPluginInstance.ReportOperationComplete( requestDetails, WSManPluginErrorCodes.InvalidOutputStream, StringUtil.Format( RemotingErrorIdStrings.WSManPluginInvalidOutputStream, WSManPluginConstants.SupportedOutputStream)); return false; } return transportMgr.EnableTransportManagerSendDataToClient(requestDetails, ctxtToReport); }
internal void DoClose( bool isShuttingDown, Exception reasonForClose) { if (_isClosed) { return; } lock (_syncObject) { if (_isClosed) { return; } _isClosed = true; _lastErrorReported = reasonForClose; if (!_isRequestPending) { // release threads blocked on the sending data to client if any _waitHandle.Set(); } } // only one thread will reach here // let everyone know that we are about to close try { RaiseClosingEvent(); foreach (var cmdTransportKvp in _activeCmdTransportManagers) { cmdTransportKvp.Value.Close(reasonForClose); } _activeCmdTransportManagers.Clear(); if (null != _registeredShutDownWaitHandle) { // This will not wait for the callback to complete. _registeredShutDownWaitHandle.Unregister(null); _registeredShutDownWaitHandle = null; } // Delete the context only if isShuttingDown != true. isShuttingDown will // be true only when the method is called from RegisterWaitForSingleObject // handler..in which case the context will be freed from the callback. if (null != _shutDownContext) { _shutDownContext = null; } // This might happen when client did not send a receive request // but the server is closing if (null != _requestDetails) { // Notify that no more data is being sent on this transport. WSManNativeApi.WSManPluginReceiveResult( _requestDetails.unmanagedHandle, (int)WSManNativeApi.WSManFlagReceive.WSMAN_FLAG_RECEIVE_RESULT_NO_MORE_DATA, WSManPluginConstants.SupportedOutputStream, IntPtr.Zero, WSManNativeApi.WSMAN_COMMAND_STATE_DONE, 0); WSManPluginInstance.ReportWSManOperationComplete(_requestDetails, reasonForClose); // We should not use request details again after reporting operation complete // so releasing the resource. Remember not to free this memory as this memory // is allocated and owned by WSMan. _requestDetails = null; } } finally { // dispose resources _waitHandle.Dispose(); } }
internal bool EnableTransportManagerSendDataToClient( WSManNativeApi.WSManPluginRequest requestDetails, WSManPluginOperationShutdownContext ctxtToReport) { _shutDownContext = ctxtToReport; bool isRegisterWaitForSingleObjectSucceeded = true; lock (_syncObject) { if (_isRequestPending) { // if a request is already pending..ignore this. WSManPluginInstance.ReportWSManOperationComplete( requestDetails, WSManPluginErrorCodes.NoError); return false; } if (_isClosed) { WSManPluginInstance.ReportWSManOperationComplete(requestDetails, _lastErrorReported); return false; } _isRequestPending = true; _requestDetails = requestDetails; if (Platform.IsWindows) { // Wrap the provided handle so it can be passed to the registration function SafeWaitHandle safeWaitHandle = new SafeWaitHandle(requestDetails.shutdownNotificationHandle, false); // Owned by WinRM EventWaitHandle eventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset); ClrFacade.SetSafeWaitHandle(eventWaitHandle, safeWaitHandle); _registeredShutDownWaitHandle = ThreadPool.RegisterWaitForSingleObject( eventWaitHandle, new WaitOrTimerCallback(WSManPluginManagedEntryWrapper.PSPluginOperationShutdownCallback), _shutDownContext, -1, // INFINITE true); // TODO: Do I need to worry not being able to set missing WT_TRANSFER_IMPERSONATION? if (null == _registeredShutDownWaitHandle) { isRegisterWaitForSingleObjectSucceeded = false; } } // release thread waiting to send data to the client. _waitHandle.Set(); } if (!isRegisterWaitForSingleObjectSucceeded) { WSManPluginInstance.PerformCloseOperation(ctxtToReport); WSManPluginInstance.ReportOperationComplete( requestDetails, WSManPluginErrorCodes.ShutdownRegistrationFailed, StringUtil.Format( RemotingErrorIdStrings.WSManPluginShutdownRegistrationFailed)); return false; } return true; }