/// <summary>
        /// if flush is true, data will be sent immediately to the client. This is accomplished
        /// by using WSMAN_FLAG_RECEIVE_FLUSH flag provided by WSMan API.
        /// </summary>
        /// <param name="data"></param>
        /// <param name="flush"></param>
        /// <param name="reportAsPending"></param>
        /// <param name="reportAsDataBoundary"></param>
        protected override void SendDataToClient(
            byte[] data,
            bool flush,
            bool reportAsPending,
            bool reportAsDataBoundary)
        {
            if (true == _isClosed)
            {
                return;
            }

            // double-check locking mechanism is used here to avoid entering into lock
            // every time data is sent..entering/exiting from lock is costly.
            if (!_isRequestPending)
            {
                // Dont send data until we have received request from client.
                // The following blocks the calling thread. The thread is
                // unblocked once a request from client arrives.
                _waitHandle.WaitOne();
                _isRequestPending = true;
                // at this point request must be pending..so dispose waitHandle
                _waitHandle.Dispose();
            }

            int result = (int)WSManPluginErrorCodes.NoError;
            // at this point we have pending request from client. so it is safe
            // to send data to client using WSMan API.
            using (WSManNativeApi.WSManData_ManToUn dataToBeSent = new WSManNativeApi.WSManData_ManToUn(data))
            {
                lock (_syncObject)
                {
                    if (!_isClosed)
                    {
                        int flags = 0;
                        if (flush)
                            flags |= (int)WSManNativeApi.WSManFlagReceive.WSMAN_FLAG_RECEIVE_FLUSH;
                        if (reportAsDataBoundary)
                            //currently assigning hardcoded value for this flag, this is a new change in wsman.h and needs to be replaced with the actual definition once
                            // modified wsman.h is in public headers
                            flags |= (int)WSManNativeApi.WSManFlagReceive.WSMAN_FLAG_RECEIVE_RESULT_DATA_BOUNDARY;

                        result = WSManNativeApi.WSManPluginReceiveResult(
                            _requestDetails.unmanagedHandle,
                            flags,
                            WSManPluginConstants.SupportedOutputStream,
                            dataToBeSent,
                            reportAsPending ? WSManNativeApi.WSMAN_COMMAND_STATE_PENDING : null,
                            0);
                    }
                }
            }
            if ((int)WSManPluginErrorCodes.NoError != result)
            {
                ReportError(result, "WSManPluginReceiveResult");
            }
        }
        /// <summary>
        /// Starts connecting to an existing remote session. This will result in a WSManConnectShellEx WSMan
        /// async call. Piggy backs available data in input stream as openXml in connect SOAP.
        /// DSHandler will push negotiation related messages through the open content
        /// </summary>
        /// <exception cref="PSRemotingTransportException">
        /// WSManConnectShellEx failed.
        /// </exception>
        internal override void ConnectAsync()
        {
            Dbg.Assert(!isClosed, "object already disposed");
            Dbg.Assert(!String.IsNullOrEmpty(ConnectionInfo.ShellUri), "shell uri cannot be null or empty.");

            ReceivedDataCollection.PrepareForStreamConnect();
            // additional content with connect shell call. Negotiation and connect related messages
            // should be included in payload
            if (null == _openContent)
            {
                DataPriorityType additionalDataType;
                byte[] additionalData = dataToBeSent.ReadOrRegisterCallback(null, out additionalDataType);

                if (null != additionalData)
                {
                    // WSMan expects the data to be in XML format (which is text + xml tags)
                    // so convert byte[] into base64 encoded format
                    string base64EncodedDataInXml = string.Format(CultureInfo.InvariantCulture, "<{0} xmlns=\"{1}\">{2}</{0}>",
                        WSManNativeApi.PS_CONNECT_XML_TAG,
                        WSManNativeApi.PS_XML_NAMESPACE,
                        Convert.ToBase64String(additionalData));
                    _openContent = new WSManNativeApi.WSManData_ManToUn(base64EncodedDataInXml);
                }

                //THERE SHOULD BE NO ADDITIONAL DATA. If there is, it means we are not able to push all initial negotiation related data
                // as part of Connect SOAP. The connect algorithm is based on this assumption. So bail out.
                additionalData = dataToBeSent.ReadOrRegisterCallback(null, out additionalDataType);
                if (additionalData != null)
                {
                    //Negotiation payload does not fit in ConnectShell. bail out. 
                    //Assert for now. should be replaced with raising an exception so upper layers can catch.
                    Dbg.Assert(false, "Negotiation payload does not fit in ConnectShell");
                    return;
                }
            }

            // Create and store context for this shell operation. This context is used from various callbacks
            _sessionContextID = GetNextSessionTMHandleId();
            AddSessionTransportManager(_sessionContextID, this);

            //session is implicitly assumed to support disconnect
            SupportsDisconnect = true;

            // Create Callback
            _connectSessionCallback = new WSManNativeApi.WSManShellAsync(new IntPtr(_sessionContextID), s_sessionConnectCallback);
            lock (syncObject)
            {
                if (isClosed)
                {
                    // the transport is already closed..so no need to connect
                    // anymore.
                    return;
                }

                Dbg.Assert(_startMode == WSManTransportManagerUtils.tmStartModes.None, "startMode is not in expected state");
                _startMode = WSManTransportManagerUtils.tmStartModes.Connect;

                int flags = 0;
                flags |= (ConnectionInfo.OutputBufferingMode == Runspaces.OutputBufferingMode.Block) ?
                                (int)WSManNativeApi.WSManShellFlag.WSMAN_FLAG_SERVER_BUFFERING_MODE_BLOCK : 0;
                flags |= (ConnectionInfo.OutputBufferingMode == Runspaces.OutputBufferingMode.Drop) ?
                                (int)WSManNativeApi.WSManShellFlag.WSMAN_FLAG_SERVER_BUFFERING_MODE_DROP : 0;

                WSManNativeApi.WSManConnectShellEx(_wsManSessionHandle,
                    flags,
                    ConnectionInfo.ShellUri,
                    RunspacePoolInstanceId.ToString().ToUpperInvariant(),  //wsman is case sensitive wrt shellId. so consistently using upper case
                    IntPtr.Zero,
                    _openContent,
                    _connectSessionCallback,
                    ref _wsManShellOperationHandle);
            }

            if (_wsManShellOperationHandle == IntPtr.Zero)
            {
                TransportErrorOccuredEventArgs eventargs = WSManTransportManagerUtils.ConstructTransportErrorEventArgs(WSManAPIData.WSManAPIHandle,
                    this,
                    new WSManNativeApi.WSManError(),
                    TransportMethodEnum.ConnectShellEx,
                    RemotingErrorIdStrings.ConnectExFailed, this.ConnectionInfo.ComputerName);
                ProcessWSManTransportError(eventargs);
                return;
            }
        }
        /// <summary>
        /// Sets a string value for a WSMan Session option.
        /// </summary>
        /// <param name="option"></param>
        /// <param name="stringData"></param>
        /// <exception cref="PSInvalidOperationException">
        /// Setting session option failed with a non-zero error code.
        /// </exception>
        internal void SetWSManSessionOption(WSManNativeApi.WSManSessionOption option, string stringData)
        {
            using (WSManNativeApi.WSManData_ManToUn data = new WSManNativeApi.WSManData_ManToUn(stringData))
            {
                int result = WSManNativeApi.WSManSetSessionOption(_wsManSessionHandle,
                      option, data);

                if (result != 0)
                {
                    // Get the error message from WSMan
                    string errorMessage = WSManNativeApi.WSManGetErrorMessage(WSManAPIData.WSManAPIHandle, result);

                    PSInvalidOperationException exception = new PSInvalidOperationException(errorMessage);
                    throw exception;
                }
            }
        }
        private void SendData(byte[] data, DataPriorityType priorityType)
        {
            tracer.WriteLine("Command sending data of size : {0}", data.Length);
            byte[] package = data;

            #region SHIM: Redirection code for command data send.

            bool sendContinue = true;

            if (s_commandSendRedirect != null)
            {
                object[] arguments = new object[2] { null, package };
                sendContinue = (bool)s_commandSendRedirect.DynamicInvoke(arguments);
                package = (byte[])arguments[0];
            }

            if (!sendContinue)
                return;

            #endregion

            using (WSManNativeApi.WSManData_ManToUn serializedContent =
                         new WSManNativeApi.WSManData_ManToUn(package))
            {
                PSEtwLog.LogAnalyticInformational(
                    PSEventId.WSManSendShellInputEx, PSOpcode.Send, PSTask.None,
                    PSKeyword.Transport | PSKeyword.UseAlwaysAnalytic,
                    RunspacePoolInstanceId.ToString(),
                    powershellInstanceId.ToString(),
                    serializedContent.BufferLength.ToString(CultureInfo.InvariantCulture));

                lock (syncObject)
                {
                    // make sure the transport is not closed.
                    if (isClosed)
                    {
                        tracer.WriteLine("Client Session TM: Transport manager is closed. So returning");
                        return;
                    }

                    // send callback
                    _sendToRemoteCompleted = new WSManNativeApi.WSManShellAsync(new IntPtr(_cmdContextId), s_cmdSendCallback);
                    WSManNativeApi.WSManSendShellInputEx(_wsManShellOperationHandle, _wsManCmdOperationHandle, 0,
                        priorityType == DataPriorityType.Default ?
                            WSManNativeApi.WSMAN_STREAM_ID_STDIN : WSManNativeApi.WSMAN_STREAM_ID_PROMPTRESPONSE,
                        serializedContent,
                        _sendToRemoteCompleted,
                        ref _wsManSendOperationHandle);
                }
            }
        }
        internal override void Dispose(bool isDisposing)
        {
            tracer.WriteLine("Disposing session with session context: {0} Operation Context: {1}", _sessionContextID, _wsManShellOperationHandle);

            CloseSessionAndClearResources();

            DisposeWSManAPIDataAsync();

            // openContent is used by redirection ie., while redirecting to 
            // a new machine and hence this is cleared only when the session
            // is disposing.
            if (isDisposing && (null != _openContent))
            {
                _openContent.Dispose();
                _openContent = null;
            }

            base.Dispose(isDisposing);
        }
        /// <summary>
        /// Starts connecting to remote end asynchronously. This will result in a WSManCreateShellEx WSMan
        /// async call. By the time this call returns, we will have a valid handle, if the operation
        /// succeeds. Make sure other methods are called only after this method returns. Thread
        /// synchronization is left to the caller.
        /// </summary>
        /// <exception cref="PSRemotingTransportException">
        /// WSManCreateShellEx failed.
        /// </exception>
        internal override void CreateAsync()
        {
            Dbg.Assert(!isClosed, "object already disposed");
            Dbg.Assert(!String.IsNullOrEmpty(ConnectionInfo.ShellUri), "shell uri cannot be null or empty.");
            Dbg.Assert(WSManAPIData != null, "WSManApiData should always be created before session creation.");

            List<WSManNativeApi.WSManOption> shellOptions = new List<WSManNativeApi.WSManOption>(WSManAPIData.CommonOptionSet);

            #region SHIM: Redirection code for protocol version

            if (s_protocolVersionRedirect != null)
            {
                string newProtocolVersion = (string)s_protocolVersionRedirect.DynamicInvoke();
                shellOptions.Clear();
                WSManNativeApi.WSManOption newPrtVOption = new WSManNativeApi.WSManOption();
                newPrtVOption.name = RemoteDataNameStrings.PS_STARTUP_PROTOCOL_VERSION_NAME;
                newPrtVOption.value = newProtocolVersion;
                newPrtVOption.mustComply = true;
                shellOptions.Add(newPrtVOption);
            }

            #endregion

            // Pass the WSManConnectionInfo object IdleTimeout value if it is
            // valid.  Otherwise pass the default value that instructs the server
            // to use its default IdleTimeout value.
            uint uIdleTimeout = (ConnectionInfo.IdleTimeout > 0) ?
                (uint)ConnectionInfo.IdleTimeout : UseServerDefaultIdleTimeoutUInt;

            // startup info           
            WSManNativeApi.WSManShellStartupInfo_ManToUn startupInfo =
                new WSManNativeApi.WSManShellStartupInfo_ManToUn(WSManAPIData.InputStreamSet,
                WSManAPIData.OutputStreamSet,
                uIdleTimeout,
                _sessionName);

            // additional content with create shell call. Piggy back first fragment from
            // the dataToBeSent buffer.
            if (null == _openContent)
            {
                DataPriorityType additionalDataType;
                byte[] additionalData = dataToBeSent.ReadOrRegisterCallback(null, out additionalDataType);

                #region SHIM: Redirection code for session data send.

                bool sendContinue = true;

                if (s_sessionSendRedirect != null)
                {
                    object[] arguments = new object[2] { null, additionalData };
                    sendContinue = (bool)s_sessionSendRedirect.DynamicInvoke(arguments);
                    additionalData = (byte[])arguments[0];
                }

                if (!sendContinue)
                    return;

                #endregion

                if (null != additionalData)
                {
                    // WSMan expects the data to be in XML format (which is text + xml tags)
                    // so convert byte[] into base64 encoded format
                    string base64EncodedDataInXml = string.Format(CultureInfo.InvariantCulture, "<{0} xmlns=\"{1}\">{2}</{0}>",
                        WSManNativeApi.PS_CREATION_XML_TAG,
                        WSManNativeApi.PS_XML_NAMESPACE,
                        Convert.ToBase64String(additionalData));
                    _openContent = new WSManNativeApi.WSManData_ManToUn(base64EncodedDataInXml);
                }
            }

            // Create the session context information only once.  CreateAsync() can be called multiple
            // times by RetrySessionCreation for flaky networks.
            if (_sessionContextID == 0)
            {
                // Create and store context for this shell operation. This context is used from various callbacks
                _sessionContextID = GetNextSessionTMHandleId();
                AddSessionTransportManager(_sessionContextID, this);

                // Create Callback
                _createSessionCallback = new WSManNativeApi.WSManShellAsync(new IntPtr(_sessionContextID), s_sessionCreateCallback);
                _createSessionCallbackGCHandle = GCHandle.Alloc(_createSessionCallback);
            }

            PSEtwLog.LogAnalyticInformational(PSEventId.WSManCreateShell, PSOpcode.Connect,
                PSTask.CreateRunspace, PSKeyword.Transport | PSKeyword.UseAlwaysAnalytic,
                RunspacePoolInstanceId.ToString());

            try
            {
                lock (syncObject)
                {
                    if (isClosed)
                    {
                        // the transport is already closed..so no need to create a connection
                        // anymore.
                        return;
                    }

                    Dbg.Assert(_startMode == WSManTransportManagerUtils.tmStartModes.None, "startMode is not in expected state");
                    _startMode = WSManTransportManagerUtils.tmStartModes.Create;

                    if (_noMachineProfile)
                    {
                        WSManNativeApi.WSManOption noProfile = new WSManNativeApi.WSManOption();
                        noProfile.name = WSManNativeApi.NoProfile;
                        noProfile.mustComply = true;
                        noProfile.value = "1";
                        shellOptions.Add(noProfile);
                    }

                    int flags = _noCompression ? (int)WSManNativeApi.WSManShellFlag.WSMAN_FLAG_NO_COMPRESSION : 0;
                    flags |= (ConnectionInfo.OutputBufferingMode == Runspaces.OutputBufferingMode.Block) ?
                                    (int)WSManNativeApi.WSManShellFlag.WSMAN_FLAG_SERVER_BUFFERING_MODE_BLOCK : 0;
                    flags |= (ConnectionInfo.OutputBufferingMode == Runspaces.OutputBufferingMode.Drop) ?
                                    (int)WSManNativeApi.WSManShellFlag.WSMAN_FLAG_SERVER_BUFFERING_MODE_DROP : 0;

                    using (WSManNativeApi.WSManOptionSet optionSet = new WSManNativeApi.WSManOptionSet(shellOptions.ToArray()))
                    {
                        WSManNativeApi.WSManCreateShellEx(_wsManSessionHandle,
                            flags,
                            ConnectionInfo.ShellUri,
                            RunspacePoolInstanceId.ToString().ToUpperInvariant(),
                            startupInfo,
                            optionSet,
                            _openContent,
                            _createSessionCallback,
                            ref _wsManShellOperationHandle);
                    }
                }

                if (_wsManShellOperationHandle == IntPtr.Zero)
                {
                    TransportErrorOccuredEventArgs eventargs = WSManTransportManagerUtils.ConstructTransportErrorEventArgs(WSManAPIData.WSManAPIHandle,
                        this,
                        new WSManNativeApi.WSManError(),
                        TransportMethodEnum.CreateShellEx,
                        RemotingErrorIdStrings.ConnectExFailed,
                        this.ConnectionInfo.ComputerName);
                    ProcessWSManTransportError(eventargs);
                    return;
                }
            }
            finally
            {
                startupInfo.Dispose();
            }
        }
        /// <summary>
        /// if flush is true, data will be sent immediately to the client. This is accomplished
        /// by using WSMAN_FLAG_RECEIVE_FLUSH flag provided by WSMan API.
        /// </summary>
        /// <param name="data"></param>
        /// <param name="flush"></param>
        /// <param name="reportAsPending"></param>
        /// <param name="reportAsDataBoundary"></param>
        protected override void SendDataToClient(
            byte[] data,
            bool flush,
            bool reportAsPending,
            bool reportAsDataBoundary)
        {
            if (true == _isClosed)
            {
                return;
            }

            // double-check locking mechanism is used here to avoid entering into lock
            // every time data is sent..entering/exiting from lock is costly.
            if (!_isRequestPending)
            {
                // Dont send data until we have received request from client.
                // The following blocks the calling thread. The thread is 
                // unblocked once a request from client arrives.
                _waitHandle.WaitOne();
                _isRequestPending = true;
                // at this point request must be pending..so dispose waitHandle
                _waitHandle.Dispose();
            }

            int result = (int)WSManPluginErrorCodes.NoError;
            // at this point we have pending request from client. so it is safe
            // to send data to client using WSMan API.    
            using (WSManNativeApi.WSManData_ManToUn dataToBeSent = new WSManNativeApi.WSManData_ManToUn(data))
            {
                lock (_syncObject)
                {
                    if (!_isClosed)
                    {
                        int flags = 0;
                        if (flush)
                            flags |= (int)WSManNativeApi.WSManFlagReceive.WSMAN_FLAG_RECEIVE_FLUSH;
                        if (reportAsDataBoundary)
                            //currently assigning hardcoded value for this flag, this is a new change in wsman.h and needs to be replaced with the actual definition once
                            // modified wsman.h is in public headers
                            flags |= (int)WSManNativeApi.WSManFlagReceive.WSMAN_FLAG_RECEIVE_RESULT_DATA_BOUNDARY;

                        result = WSManNativeApi.WSManPluginReceiveResult(
                            _requestDetails.unmanagedHandle,
                            flags,
                            WSManPluginConstants.SupportedOutputStream,
                            dataToBeSent,
                            reportAsPending ? WSManNativeApi.WSMAN_COMMAND_STATE_PENDING : null,
                            0);
                    }
                }
            }
            if ((int)WSManPluginErrorCodes.NoError != result)
            {
                ReportError(result, "WSManPluginReceiveResult");
            }
        }