OletxEnlistment.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / tx / System / Transactions / Oletx / OletxEnlistment.cs / 1305376 / OletxEnlistment.cs

                            using System; 
using System.Collections;
using System.Diagnostics;
using System.Globalization;
using System.IO; 
using System.Reflection;
using System.Runtime.InteropServices; 
using System.Security.Permissions; 
using System.Threading;
using System.Transactions; 
using System.Transactions.Diagnostics;

namespace System.Transactions.Oletx
{ 
    [Serializable]
    internal class OletxRecoveryInformation 
    { 
        internal byte[] proxyRecoveryInformation;
 
        internal OletxRecoveryInformation(
            byte[] proxyRecoveryInformation
            )
        { 
            this.proxyRecoveryInformation = proxyRecoveryInformation;
        } 
    } 

    class OletxEnlistment : OletxBaseEnlistment, 
        IPromotedEnlistment
    {
        internal enum OletxEnlistmentState
        { 
            Active,
            Phase0Preparing, 
            Preparing, 
            SinglePhaseCommitting,
            Prepared, 
            Committing,
            Committed,
            Aborting,
            Aborted, 
            InDoubt,
            Done 
        } 

        IEnlistmentShim enlistmentShim; 
        IPhase0EnlistmentShim phase0Shim;
        bool canDoSinglePhase;
        IEnlistmentNotificationInternal iEnlistmentNotification;
        // The information that comes from/goes to the proxy. 
        byte[] proxyPrepareInfoByteArray = null;
 
        OletxEnlistmentState state = OletxEnlistmentState.Active; 

        bool isSinglePhase = false; 
        Guid transactionGuid = Guid.Empty;

        // We need to keep track of the handle for the phase 1 notifications
        // so that if the enlistment terminates the conversation due for instance 
        // to a force rollback the handle can be cleaned up.
        internal IntPtr phase1Handle = IntPtr.Zero; 
 
        // Set to true if we receive an AbortRequest while we still have
        // another notification, like prepare, outstanding.  It indicates that 
        // we need to fabricate a rollback to the app after it responds to Prepare.
        bool fabricateRollback = false;

        bool tmWentDown = false; 
        bool aborting = false;
 
        byte [] prepareInfoByteArray; 

        internal Guid TransactionIdentifier 
        {
            get
            {
                return transactionGuid; 
            }
        } 
 
        #region Constructor
 
        internal OletxEnlistment(
            bool canDoSinglePhase,
            IEnlistmentNotificationInternal enlistmentNotification,
            Guid transactionGuid, 
            EnlistmentOptions enlistmentOptions,
            OletxResourceManager oletxResourceManager, 
            OletxTransaction oletxTransaction 
            ) : base( oletxResourceManager, oletxTransaction )
        { 
            Guid myGuid = Guid.Empty;

            // This will get set later by the creator of this object after it
            // has enlisted with the proxy. 
            this.enlistmentShim = null;
            this.phase0Shim = null; 
 
            this.canDoSinglePhase = canDoSinglePhase;
            this.iEnlistmentNotification = enlistmentNotification; 
            this.state = OletxEnlistmentState.Active;
            this.transactionGuid = transactionGuid;

            this.proxyPrepareInfoByteArray = null; 

            if ( DiagnosticTrace.Information ) 
            { 
                EnlistmentTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                    this.InternalTraceIdentifier, 
                    EnlistmentType.Durable,
                    enlistmentOptions
                    );
            } 

            // Always do this last incase anything earlier fails. 
            AddToEnlistmentTable(); 
        }
 

        internal OletxEnlistment(
            IEnlistmentNotificationInternal enlistmentNotification,
            OletxTransactionStatus xactStatus, 
            byte[] prepareInfoByteArray,
            OletxResourceManager oletxResourceManager 
            ) : base( oletxResourceManager, null ) 
        {
            Guid myGuid = Guid.Empty; 

            // This will get set later by the creator of this object after it
            // has enlisted with the proxy.
            this.enlistmentShim = null; 
            this.phase0Shim = null;
 
            this.canDoSinglePhase = false; 
            this.iEnlistmentNotification = enlistmentNotification;
            this.state = OletxEnlistmentState.Active; 

            // Do this before we do any tracing because it will affect the trace identifiers that we generate.
            Debug.Assert( ( null != prepareInfoByteArray ),
                "OletxEnlistment.ctor - null oletxTransaction without a prepareInfoByteArray" ); 

            int prepareInfoLength = prepareInfoByteArray.Length; 
            this.proxyPrepareInfoByteArray = new byte[ prepareInfoLength ]; 
            Array.Copy(prepareInfoByteArray, proxyPrepareInfoByteArray, prepareInfoLength);
 
            byte[] txGuidByteArray = new byte[ 16 ];
            Array.Copy(proxyPrepareInfoByteArray, txGuidByteArray, 16);

            this.transactionGuid = new Guid( txGuidByteArray ); 
            this.transactionGuidString = this.transactionGuid.ToString();
 
            // If this is being created as part of a Reenlist and we already know the 
            // outcome, then tell the application.
            switch (xactStatus) 
            {
                case OletxTransactionStatus.OLETX_TRANSACTION_STATUS_ABORTED :
                {
                    this.state = OletxEnlistmentState.Aborting; 
                    if ( DiagnosticTrace.Verbose )
                    { 
                        EnlistmentNotificationCallTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                            this.InternalTraceIdentifier,
                            NotificationCall.Rollback 
                            );
                    }

                    iEnlistmentNotification.Rollback( this ); 
                    break;
                } 
 
                case OletxTransactionStatus.OLETX_TRANSACTION_STATUS_COMMITTED :
                { 
                    this.state = OletxEnlistmentState.Committing;
                    // We are going to send the notification to the RM.  We need to put the
                    // enlistment on the reenlistPendingList.  We lock the reenlistList because
                    // we have decided that is the lock that protects both lists.  The entry will 
                    // be taken off the reenlistPendingList when the enlistment has
                    // EnlistmentDone called on it.  The enlistment will call 
                    // RemoveFromReenlistPending. 
                    lock( oletxResourceManager.reenlistList )
                    { 
                        oletxResourceManager.reenlistPendingList.Add( this );
                    }

                    if ( DiagnosticTrace.Verbose ) 
                    {
                        EnlistmentNotificationCallTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                            this.InternalTraceIdentifier, 
                            NotificationCall.Commit
                            ); 
                    }

                    iEnlistmentNotification.Commit( this );
                    break; 
                }
 
                case OletxTransactionStatus.OLETX_TRANSACTION_STATUS_PREPARED : 
                {
                    this.state = OletxEnlistmentState.Prepared; 
                    lock( oletxResourceManager.reenlistList )
                    {
                        oletxResourceManager.reenlistList.Add( this );
                        oletxResourceManager.StartReenlistThread(); 
                    }
                    break; 
                } 

                default : 
                {
                    if ( DiagnosticTrace.Critical )
                    {
                        InternalErrorTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                            SR.GetString( SR.OletxEnlistmentUnexpectedTransactionStatus )
                            ); 
                    } 

                    throw TransactionException.Create( SR.GetString( SR.TraceSourceOletx ), SR.GetString( SR.OletxEnlistmentUnexpectedTransactionStatus ), null ); 
                }
            }

            if ( DiagnosticTrace.Information ) 
            {
                EnlistmentTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                    this.InternalTraceIdentifier, 
                    EnlistmentType.Durable,
                    EnlistmentOptions.None 
                    );
            }

            // Always do this last in case anything prior to this fails. 
            AddToEnlistmentTable();
        } 
        #endregion 

 
        internal IEnlistmentNotificationInternal EnlistmentNotification
        {
            get
            { 
                return iEnlistmentNotification;
            } 
        } 

 
        internal IEnlistmentShim EnlistmentShim
        {
            get{ return this.enlistmentShim; }
            set{ this.enlistmentShim = value; } 
        }
 
 
        internal IPhase0EnlistmentShim Phase0EnlistmentShim
        { 
            get{ return this.phase0Shim; }
            set
            {
                lock( this ) 
                {
                    // If this.aborting is set to true, then we must have already received a 
                    // Phase0Request.  This could happen if the transaction aborts after the 
                    // enlistment is made, but before we are given the shim.
                    if ( ( null != value ) && ( this.aborting || this.tmWentDown ) ) 
                    {
                        value.Phase0Done( false );
                    }
                    this.phase0Shim = value; 
                }
            } 
        } 

 
        internal OletxEnlistmentState State
        {
            get{ return state; }
            set{ state = value; } 
        }
 
        internal byte[] ProxyPrepareInfoByteArray 
        {
            get{ return proxyPrepareInfoByteArray; } 
        }


        internal void FinishEnlistment() 
        {
            lock( this ) 
            { 
                // If we don't have a wrappedTransactionEnlistmentAsync, we may
                // need to remove ourselves from the reenlistPendingList in the 
                // resource manager.
                if ( null == this.enlistmentShim )
                {
                    oletxResourceManager.RemoveFromReenlistPending( this ); 
                }
                this.iEnlistmentNotification = null; 
 
                RemoveFromEnlistmentTable();
            } 
        }

        internal void TMDownFromInternalRM( OletxTransactionManager oletxTm )
        { 
            lock( this )
            { 
                // If we don't have an oletxTransaction or the passed oletxTm matches that of my oletxTransaction, the TM went down. 
                if ( ( null == this.oletxTransaction ) || ( oletxTm == this.oletxTransaction.realOletxTransaction.OletxTransactionManagerInstance ) )
                { 
                    this.tmWentDown = true;
                }
            }
 
            return;
        } 
 

        #region ITransactionResourceAsync methods 

        // ITranactionResourceAsync.PrepareRequest
        public bool PrepareRequest(
            bool singlePhase, 
            byte[] prepareInfo
            ) 
        { 
            IEnlistmentShim localEnlistmentShim = null;
            OletxEnlistmentState localState = OletxEnlistmentState.Active; 
            IEnlistmentNotificationInternal localEnlistmentNotification = null;
            OletxRecoveryInformation oletxRecoveryInformation = null;
            bool enlistmentDone;
 
            lock( this )
            { 
                if ( OletxEnlistmentState.Active == state ) 
                {
                    localState = state = OletxEnlistmentState.Preparing; 
                }
                else
                {
                    // We must have done the prepare work in Phase0, so just remember what state we are 
                    // in now.
                    localState = state; 
                } 

                localEnlistmentNotification = iEnlistmentNotification; 

                localEnlistmentShim = this.EnlistmentShim;

                this.oletxTransaction.realOletxTransaction.TooLateForEnlistments = true; 
            }
 
            // If we went to Preparing state, send the app 
            // a prepare request.
            if ( OletxEnlistmentState.Preparing == localState ) 
            {
                oletxRecoveryInformation = new OletxRecoveryInformation( prepareInfo );
                this.isSinglePhase = singlePhase;
 
                // Store the prepare info we are given.
                Debug.Assert(this.proxyPrepareInfoByteArray == null, "Unexpected value in this.proxyPrepareInfoByteArray"); 
                long arrayLength = prepareInfo.Length; 
                this.proxyPrepareInfoByteArray = new Byte [arrayLength];
                Array.Copy(prepareInfo, this.proxyPrepareInfoByteArray, arrayLength); 

                if ( this.isSinglePhase && this.canDoSinglePhase )
                {
                    ISinglePhaseNotificationInternal singlePhaseNotification = (ISinglePhaseNotificationInternal) localEnlistmentNotification; 
                    state = OletxEnlistmentState.SinglePhaseCommitting;
                    // We don't call DecrementUndecidedEnlistments for Phase1 enlistments. 
                    if ( DiagnosticTrace.Verbose ) 
                    {
                        EnlistmentNotificationCallTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                            this.InternalTraceIdentifier,
                            NotificationCall.SinglePhaseCommit
                            );
                    } 

                    singlePhaseNotification.SinglePhaseCommit( this ); 
                    enlistmentDone = true; 
                }
                else 
                {
                    // We need to turn the oletxRecoveryInformation into a byte array.
                    byte[] oletxRecoveryInformationByteArray = TransactionManager.ConvertToByteArray( oletxRecoveryInformation );
 
                    state = OletxEnlistmentState.Preparing;
 
                    // 
                    this.prepareInfoByteArray = TransactionManager.GetRecoveryInformation(
                        oletxResourceManager.oletxTransactionManager.CreationNodeName, 
                        oletxRecoveryInformationByteArray
                        );

                    if ( DiagnosticTrace.Verbose ) 
                    {
                        EnlistmentNotificationCallTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                            this.InternalTraceIdentifier, 
                            NotificationCall.Prepare
                            ); 
                    }

                    localEnlistmentNotification.Prepare(
                        this 
                        );
                    enlistmentDone = false; 
                } 
            }
            else if ( OletxEnlistmentState.Prepared == localState ) 
            {
                // We must have done our prepare work during Phase0 so just vote Yes.
                try
                { 
                    localEnlistmentShim.PrepareRequestDone( OletxPrepareVoteType.Prepared );
                    enlistmentDone = false; 
                } 
                catch ( COMException comException )
                { 
                    OletxTransactionManager.ProxyException( comException );
                    throw;
                }
            } 
            else if ( OletxEnlistmentState.Done == localState )
            { 
                try 
                {
                    // This was an early vote.  Respond ReadOnly 
                    try
                    {
                        localEnlistmentShim.PrepareRequestDone( OletxPrepareVoteType.ReadOnly );
                        enlistmentDone = true; 
                    }
                    finally 
                    { 
                        FinishEnlistment();
                    } 
                }
                catch ( COMException comException )
                {
                    OletxTransactionManager.ProxyException( comException ); 
                    throw;
                } 
            } 
            else
            { 
                // Any other state means we should vote NO to the proxy.
                try
                {
                    localEnlistmentShim.PrepareRequestDone( OletxPrepareVoteType.Failed ); 
                }
                catch ( COMException ex ) 
                { 
                    // No point in rethrowing this.  We are not on an app thread and we have already told
                    // the app that the transaction is aborting.  When the app calls EnlistmentDone, we will 
                    // do the final release of the ITransactionEnlistmentAsync.
                    if ( DiagnosticTrace.Verbose )
                    {
                        ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                            ex );
                    } 
                } 
                enlistmentDone = true;
            } 

            return enlistmentDone;
        }
 

        public void CommitRequest() 
        { 
            OletxEnlistmentState localState = OletxEnlistmentState.Active;
            IEnlistmentNotificationInternal localEnlistmentNotification = null; 
            IEnlistmentShim localEnlistmentShim = null;
            bool finishEnlistment = false;

            lock( this ) 
            {
                if ( OletxEnlistmentState.Prepared == state ) 
                { 
                    localState = state = OletxEnlistmentState.Committing;
                    localEnlistmentNotification = iEnlistmentNotification; 
                }
                else
                {
                    // We must have received an EnlistmentDone already. 
                    localState = state;
                    localEnlistmentShim = this.EnlistmentShim; 
                    finishEnlistment = true; 
                }
            } 

            if ( null != localEnlistmentNotification )
            {
                if ( DiagnosticTrace.Verbose ) 
                {
                    EnlistmentNotificationCallTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                        this.InternalTraceIdentifier, 
                        NotificationCall.Commit
                        ); 
                }

                localEnlistmentNotification.Commit( this );
            } 
            else if ( null != localEnlistmentShim )
            { 
                // We need to respond to the proxy now. 
                try
                { 
                    localEnlistmentShim.CommitRequestDone();
                }
                catch ( COMException ex )
                { 
                    // If the TM went down during our call, there is nothing special we have to do because
                    // the App doesn't expect any more notifications.  We do want to mark the enlistment 
                    // to finish, however. 
                    if ( ( NativeMethods.XACT_E_CONNECTION_DOWN == ex.ErrorCode ) ||
                        ( NativeMethods.XACT_E_TMNOTAVAILABLE == ex.ErrorCode ) 
                        )
                    {
                        finishEnlistment = true;
                        if ( DiagnosticTrace.Verbose ) 
                        {
                            ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                                ex ); 
                        }
                    } 
                    else
                    {
                        throw;
                    } 
                }
                finally 
                { 
                    if ( finishEnlistment )
                    { 
                        FinishEnlistment();
                    }
                }
            } 
            return;
        } 
 
        public void AbortRequest()
        { 
            OletxEnlistmentState localState = OletxEnlistmentState.Active;
            IEnlistmentNotificationInternal localEnlistmentNotification = null;
            IEnlistmentShim localEnlistmentShim = null;
            bool finishEnlistment = false; 

            lock( this ) 
            { 
                if ( ( OletxEnlistmentState.Active == state ) ||
                     ( OletxEnlistmentState.Prepared == state ) 
                   )
                {
                    localState = state = OletxEnlistmentState.Aborting;
                    localEnlistmentNotification = iEnlistmentNotification; 
                }
                else 
                { 
                    // We must have received an EnlistmentDone already or we have
                    // a notification outstanding (Phase0 prepare). 
                    localState = state;
                    if ( OletxEnlistmentState.Phase0Preparing == state )
                    {
                        this.fabricateRollback = true; 
                    }
                    else 
                    { 
                        finishEnlistment = true;
                    } 

                    localEnlistmentShim = this.EnlistmentShim;
                }
            } 

            if ( null != localEnlistmentNotification ) 
            { 
                if ( DiagnosticTrace.Verbose )
                { 
                    EnlistmentNotificationCallTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                        this.InternalTraceIdentifier,
                        NotificationCall.Rollback
                        ); 
                }
 
                localEnlistmentNotification.Rollback( this ); 
            }
            else if ( null != localEnlistmentShim ) 
            {
                // We need to respond to the proxy now.
                try
                { 
                    localEnlistmentShim.AbortRequestDone();
                } 
                catch ( COMException ex ) 
                {
                    // If the TM went down during our call, there is nothing special we have to do because 
                    // the App doesn't expect any more notifications.  We do want to mark the enlistment
                    // to finish, however.
                    if ( ( NativeMethods.XACT_E_CONNECTION_DOWN == ex.ErrorCode ) ||
                        ( NativeMethods.XACT_E_TMNOTAVAILABLE == ex.ErrorCode ) 
                        )
                    { 
                        finishEnlistment = true; 
                        if ( DiagnosticTrace.Verbose )
                        { 
                            ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                                ex );
                        }
                    } 
                    else
                    { 
                        throw; 
                    }
                } 
                finally
                {
                    if ( finishEnlistment )
                    { 
                        FinishEnlistment();
                    } 
                } 
            }
            return; 
        }

        public void TMDown()
        { 
            // We aren't telling our enlistments about TMDown, only
            // resource managers. 
            // Put this enlistment on the Reenlist list.  The Reenlist thread will get 
            // started when the RMSink gets the TMDown notification.
            lock ( oletxResourceManager.reenlistList ) 
            {
                lock( this )
                {
                    // Remember that we got the TMDown in case we get a Phase0Request after so we 
                    // can avoid doing a Prepare to the app.
                    this.tmWentDown = true; 
 
                    // Only move Prepared and Committing enlistments to the ReenlistList.  All others
                    // do not require a Reenlist to figure out what to do.  We save off Committing 
                    // enlistments because the RM has not acknowledged the commit, so we can't
                    // call RecoveryComplete on the proxy until that has happened.  The Reenlist thread
                    // will loop until the reenlist list is empty and it will leave a Committing
                    // enlistment on the list until it is done, but will NOT call Reenlist on the proxy. 
                    if ( ( OletxEnlistmentState.Prepared == state ) ||
                         ( OletxEnlistmentState.Committing == state ) 
                       ) 
                    {
                        oletxResourceManager.reenlistList.Add( this ); 
                    }
                }
            }
 
            return;
        } 
 
        #endregion
 
        #region ITransactionPhase0NotifyAsync methods

        // ITransactionPhase0NotifyAsync
        public void Phase0Request( 
            bool abortingHint
            ) 
        { 
            IEnlistmentNotificationInternal localEnlistmentNotification = null;
            OletxEnlistmentState localState = OletxEnlistmentState.Active; 
            OletxCommittableTransaction committableTx = null;
            bool commitNotYetCalled = false;

            if ( DiagnosticTrace.Verbose ) 
            {
                MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                    "OletxEnlistment.Phase0Request" 
                    );
            } 

            committableTx = this.oletxTransaction.realOletxTransaction.committableTransaction;
            if ( null != committableTx )
            { 
                // We are dealing with the committable transaction.  If Commit or BeginCommit has NOT been
                // called, then we are dealing with a situation where the TM went down and we are getting 
                // a bogus Phase0Request with abortHint = false (COMPlus bug 36760/36758).  This is an attempt 
                // to not send the app a Prepare request when we know the transaction is going to abort.
                if (!committableTx.CommitCalled ) 
                {
                    commitNotYetCalled = true;
                }
            } 

            lock( this ) 
            { 
                this.aborting = abortingHint;
 
                // The app may have already called EnlistmentDone.  If this occurs, don't bother sending
                // the notification to the app and we don't need to tell the proxy.
                if ( OletxEnlistmentState.Active == state )
                { 
                    // If we got an abort hint or we are the committable transaction and Commit has not yet been called or the TM went down,
                    // we don't want to do any more work on the transaction.  The abort notifications will be sent by the phase 1 
                    // enlistment 
                    if ( ( this.aborting ) || ( commitNotYetCalled ) || ( this.tmWentDown ) )
                    { 
                        // There is a possible ---- where we could get the Phase0Request before we are given the
                        // shim.  In that case, we will vote "no" when we are given the shim.
                        if ( null != this.phase0Shim )
                        { 
                            try
                            { 
                                this.phase0Shim.Phase0Done( false ); 
                            }
                            // I am not going to check for XACT_E_PROTOCOL here because that check is a workaround for a bug 
                            // that only shows up if abortingHint is false.
                            catch ( COMException ex )
                            {
                                if ( DiagnosticTrace.Verbose ) 
                                {
                                    ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                                        ex ); 
                                }
                            } 
                        }
                    }
                    else
                    { 
                        localState = state = OletxEnlistmentState.Phase0Preparing;
                        localEnlistmentNotification = iEnlistmentNotification; 
                    } 
                }
            } 

            // Tell the application to do the work.
            if ( null != localEnlistmentNotification )
            { 
                if ( OletxEnlistmentState.Phase0Preparing == localState )
                { 
                    byte[] txGuidArray = transactionGuid.ToByteArray(); 
                    byte[] rmGuidArray = oletxResourceManager.resourceManagerIdentifier.ToByteArray();
 
                    byte[] temp = new byte[ txGuidArray.Length +
                        rmGuidArray.Length
                        ];
                    Thread.MemoryBarrier(); 
                    this.proxyPrepareInfoByteArray = temp;
                    int index = 0; 
                    for ( index = 0; index < txGuidArray.Length; index++ ) 
                    {
                        proxyPrepareInfoByteArray[index] = 
                            txGuidArray[index];
                    }

                    for ( index = 0; index < rmGuidArray.Length; index++ ) 
                    {
                        proxyPrepareInfoByteArray[txGuidArray.Length + index] = 
                            rmGuidArray[index]; 
                    }
 
                    OletxRecoveryInformation oletxRecoveryInformation = new OletxRecoveryInformation(
                                                                            proxyPrepareInfoByteArray
                                                                            );
                    byte[] oletxRecoveryInformationByteArray = TransactionManager.ConvertToByteArray( oletxRecoveryInformation ); 

                    // 
                    this.prepareInfoByteArray = TransactionManager.GetRecoveryInformation( 
                        oletxResourceManager.oletxTransactionManager.CreationNodeName,
                        oletxRecoveryInformationByteArray 
                        );

                    if ( DiagnosticTrace.Verbose )
                    { 
                        EnlistmentNotificationCallTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                            this.InternalTraceIdentifier, 
                            NotificationCall.Prepare 
                            );
                    } 

                    localEnlistmentNotification.Prepare( this );
                }
                else 
                {
                    // We must have had a ---- between EnlistmentDone and the proxy telling 
                    // us Phase0Request.  Just return. 
                    if ( DiagnosticTrace.Verbose )
                    { 
                        MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                            "OletxEnlistment.Phase0Request"
                            );
                    } 

                    return; 
                } 

            } 
            if ( DiagnosticTrace.Verbose )
            {
                MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                    "OletxEnlistment.Phase0Request" 
                    );
            } 
 
        }
 
        #endregion

        public void EnlistmentDone()
        { 
            if ( DiagnosticTrace.Verbose )
            { 
                MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                    "OletxEnlistment.EnlistmentDone"
                    ); 
                EnlistmentCallbackPositiveTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                    this.InternalTraceIdentifier,
                    EnlistmentCallback.Done
                    ); 
            }
 
            IEnlistmentShim localEnlistmentShim = null; 
            IPhase0EnlistmentShim localPhase0Shim = null;
            OletxEnlistmentState localState = OletxEnlistmentState.Active; 
            bool finishEnlistment;
            bool localFabricateRollback = false;

            lock( this ) 
            {
                localState = state; 
                if ( OletxEnlistmentState.Active == state ) 
                {
                    // Early vote.  If we are doing Phase0, we need to unenlist.  Otherwise, just 
                    // remember.
                    localPhase0Shim = this.Phase0EnlistmentShim;
                    if ( null != localPhase0Shim )
                    { 
                        // We are a Phase0 enlistment and we have a vote - decrement the undecided enlistment count.
                        // We only do this for Phase0 because we don't count Phase1 durable enlistments. 
                        oletxTransaction.realOletxTransaction.DecrementUndecidedEnlistments(); 
                    }
                    finishEnlistment = false; 
                }
                else if ( OletxEnlistmentState.Preparing == state )
                {
                    // Read only vote.  Tell the proxy and go to the Done state. 
                    localEnlistmentShim = this.EnlistmentShim;
                    // We don't decrement the undecided enlistment count for Preparing because we only count 
                    // Phase0 enlistments and we are in Phase1 in Preparing state. 
                    finishEnlistment = true;
                } 
                else if ( OletxEnlistmentState.Phase0Preparing == state )
                {
                    // Read only vote to Phase0.  Tell the proxy okay and go to the Done state.
                    localPhase0Shim = this.Phase0EnlistmentShim; 
                    // We are a Phase0 enlistment and we have a vote - decrement the undecided enlistment count.
                    // We only do this for Phase0 because we don't count Phase1 durable enlistments. 
                    oletxTransaction.realOletxTransaction.DecrementUndecidedEnlistments(); 

                    // If we would have fabricated a rollback then we have already received an abort request 
                    // from proxy and will not receive any more notifications.  Otherwise more notifications
                    // will be coming.
                    if( this.fabricateRollback )
                    { 
                        finishEnlistment = true;
                    } 
                    else 
                    {
                        finishEnlistment = false; 
                    }
                }
                else if ( ( OletxEnlistmentState.Committing == state ) ||
                    ( OletxEnlistmentState.Aborting == state ) || 
                    ( OletxEnlistmentState.SinglePhaseCommitting == state )
                    ) 
                { 
                    localEnlistmentShim = this.EnlistmentShim;
                    finishEnlistment = true; 
                    // We don't decrement the undecided enlistment count for SinglePhaseCommitting because we only
                    // do it for Phase0 enlistments.
                }
                else 
                {
                    throw TransactionException.CreateEnlistmentStateException( SR.GetString( SR.TraceSourceOletx ), null ); 
                } 

                // If this.fabricateRollback is true, it means that we are fabricating this 
                // AbortRequest, rather than having the proxy tell us.  So we don't need
                // to respond to the proxy with AbortRequestDone.
                localFabricateRollback = this.fabricateRollback;
 
                state = OletxEnlistmentState.Done;
            } 
 
            try
            { 
                if ( null != localEnlistmentShim )
                {
                    if ( OletxEnlistmentState.Preparing == localState )
                    { 
                        try
                        { 
                            localEnlistmentShim.PrepareRequestDone( OletxPrepareVoteType.ReadOnly ); 
                        }
                        finally 
                        {
                            HandleTable.FreeHandle( this.phase1Handle );
                        }
                    } 
                    else if ( OletxEnlistmentState.Committing == localState )
                    { 
                        localEnlistmentShim.CommitRequestDone(); 
                    }
                    else if ( OletxEnlistmentState.Aborting == localState ) 
                    {
                        // If localFabricatRollback is true, it means that we are fabricating this
                        // AbortRequest, rather than having the proxy tell us.  So we don't need
                        // to respond to the proxy with AbortRequestDone. 
                        if ( ! localFabricateRollback )
                        { 
                            localEnlistmentShim.AbortRequestDone(); 
                        }
                    } 
                    else if ( OletxEnlistmentState.SinglePhaseCommitting == localState )
                    {
                        localEnlistmentShim.PrepareRequestDone( OletxPrepareVoteType.SinglePhase );
                    } 
                    else
                    { 
                        throw TransactionException.CreateEnlistmentStateException( SR.GetString( SR.TraceSourceOletx ), null ); 
                    }
                } 
                else if ( null != localPhase0Shim )
                {
                    if ( OletxEnlistmentState.Active == localState )
                    { 
                        localPhase0Shim.Unenlist();
                    } 
                    else if ( OletxEnlistmentState.Phase0Preparing == localState ) 
                    {
                        localPhase0Shim.Phase0Done( true ); 
                    }
                    else
                    {
                        throw TransactionException.CreateEnlistmentStateException( SR.GetString( SR.TraceSourceOletx ), null ); 
                    }
                } 
 
            }
            catch ( COMException ex ) 
            {
                // If we get an error talking to the proxy, there is nothing special we have to do because
                // the App doesn't expect any more notifications.  We do want to mark the enlistment
                // to finish, however. 
                finishEnlistment = true;
 
                if ( DiagnosticTrace.Verbose ) 
                {
                    ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                        ex );
                }
            }
            finally 
            {
                if ( finishEnlistment ) 
                { 
                    FinishEnlistment();
                } 
            }
            if ( DiagnosticTrace.Verbose )
            {
                MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                    "OletxEnlistment.EnlistmentDone"
                    ); 
            } 
        }
 

        public EnlistmentTraceIdentifier EnlistmentTraceId
        {
            get 
            {
                if ( DiagnosticTrace.Verbose ) 
                { 
                    MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                        "OletxEnlistment.get_TraceIdentifier" 
                        );
                    MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                        "OletxEnlistment.get_TraceIdentifier"
                        ); 
                }
 
                return this.InternalTraceIdentifier; 
            }
        } 

        public void Prepared()
        {
            int hrResult = NativeMethods.S_OK; 
            IEnlistmentShim localEnlistmentShim = null;
            IPhase0EnlistmentShim localPhase0Shim = null; 
            bool localFabricateRollback = false; 

            if ( DiagnosticTrace.Verbose ) 
            {
                MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                    "OletxPreparingEnlistment.Prepared"
                    ); 
                EnlistmentCallbackPositiveTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                    this.InternalTraceIdentifier, 
                    EnlistmentCallback.Prepared 
                    );
            } 

            lock( this )
            {
                if ( OletxEnlistmentState.Preparing == state ) 
                {
                    localEnlistmentShim = this.EnlistmentShim; 
                } 
                else if ( OletxEnlistmentState.Phase0Preparing == state )
                { 
                    // If the transaction is doomed or we have fabricateRollback is true because the
                    // transaction aborted while the Phase0 Prepare request was outstanding,
                    // release the WrappedTransactionPhase0EnlistmentAsync and remember that
                    // we have a pending rollback. 
                    localPhase0Shim = this.Phase0EnlistmentShim;
                    if ( ( oletxTransaction.realOletxTransaction.Doomed ) || 
                         ( this.fabricateRollback ) 
                       )
                    { 
                        // Set fabricateRollback in case we got here because the transaction is doomed.
                        this.fabricateRollback = true;
                        localFabricateRollback = this.fabricateRollback;
                    } 
                }
                else 
                { 
                    throw TransactionException.CreateEnlistmentStateException( SR.GetString( SR.TraceSourceOletx ), null );
                } 

                state = OletxEnlistmentState.Prepared;

            } 

            try 
            { 
                if ( null != localEnlistmentShim )
                { 
                    localEnlistmentShim.PrepareRequestDone( OletxPrepareVoteType.Prepared );
                }
                else if ( null != localPhase0Shim )
                { 
                    // We have a vote - decrement the undecided enlistment count.  We do
                    // this after checking Doomed because ForceRollback will decrement also. 
                    // We also do this only for Phase0 enlistments. 
                    oletxTransaction.realOletxTransaction.DecrementUndecidedEnlistments();
 
                    localPhase0Shim.Phase0Done( !localFabricateRollback );
                }

                else 
                    // The TM must have gone down, thus causing our interface pointer to be
                    // invalidated.  So we need to drive abort of the enlistment as if we 
                    // received an AbortRequest. 
                {
                    localFabricateRollback = true; 
                }

                if ( localFabricateRollback )
                { 
                    AbortRequest();
                } 
            } 
            catch ( COMException ex )
            { 
                // If the TM went down during our call, the TMDown notification to the enlistment
                // and RM will put this enlistment on the ReenlistList, if appropriate.  The outcome
                // will be obtained by the ReenlistThread.
                if ( ( NativeMethods.XACT_E_CONNECTION_DOWN == ex.ErrorCode ) || 
                    ( NativeMethods.XACT_E_TMNOTAVAILABLE == ex.ErrorCode )
                    ) 
                { 
                    if ( DiagnosticTrace.Verbose )
                    { 
                        ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                            ex );
                    }
                } 
                // In the case of Phase0, there is a bug in the proxy that causes an XACT_E_PROTOCOL
                // error if the TM goes down while the enlistment is still active.  The Phase0Request is 
                // sent out with abortHint false, but the state of the proxy object is not changed, causing 
                // Phase0Done request to fail with XACT_E_PROTOCOL.
                // For Prepared, we want to make sure the proxy aborts the transaction.  We don't need 
                // to drive the abort to the application here because the Phase1 enlistment will do that.
                // In other words, treat this as if the proxy said Phase0Request( abortingHint = true ).
                else if ( NativeMethods.XACT_E_PROTOCOL == ex.ErrorCode )
                { 
                    this.Phase0EnlistmentShim = null;
                    if ( DiagnosticTrace.Verbose ) 
                    { 
                        ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                            ex ); 
                    }
                }
                else
                { 
                    throw;
                } 
            } 
            if ( DiagnosticTrace.Verbose )
            { 
                MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                    "OletxPreparingEnlistment.Prepared"
                    );
            } 
        }
 
 
        public void ForceRollback()
        { 
            ForceRollback( null );
        }

        public void ForceRollback( Exception e ) 
        {
            IEnlistmentShim localEnlistmentShim = null; 
            IPhase0EnlistmentShim localPhase0Shim = null; 

            if ( DiagnosticTrace.Verbose ) 
            {
                MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                    "OletxPreparingEnlistment.ForceRollback"
                    ); 
            }
 
            if ( DiagnosticTrace.Warning ) 
            {
                EnlistmentCallbackNegativeTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                    this.InternalTraceIdentifier,
                    EnlistmentCallback.ForceRollback
                    );
            } 

            lock( this ) 
            { 
                if ( OletxEnlistmentState.Preparing == state )
                { 
                    localEnlistmentShim = this.EnlistmentShim;
                }
                else if ( OletxEnlistmentState.Phase0Preparing == state )
                { 
                    localPhase0Shim = this.Phase0EnlistmentShim;
                    if ( null != localPhase0Shim ) 
                    { 

                        // We have a vote - decrement the undecided enlistment count.  We only do this 
                        // if we are Phase0 enlistment.
                        oletxTransaction.realOletxTransaction.DecrementUndecidedEnlistments();
                    }
 
                }
                else 
                { 
                    throw TransactionException.CreateEnlistmentStateException( SR.GetString( SR.TraceSourceOletx ), null );
                } 

                state = OletxEnlistmentState.Aborted;
            }
 
            Interlocked.CompareExchange( ref this.oletxTransaction.realOletxTransaction.innerException, e, null );
 
            try 
            {
                if ( null != localEnlistmentShim ) 
                {
                    try
                    {
                        localEnlistmentShim.PrepareRequestDone( OletxPrepareVoteType.Failed ); 
                    }
                    finally 
                    { 
                        HandleTable.FreeHandle( this.phase1Handle );
                    } 
                }

                if ( null != localPhase0Shim )
                { 
                    localPhase0Shim.Phase0Done( false );
                } 
//                else 
                    // The TM must have gone down, thus causing our interface pointer to be
                    // invalidated.  The App doesn't expect any more notifications, so we can 
                    // just finish the enlistment.
            }
            catch ( COMException ex )
            { 
                // If the TM went down during our call, there is nothing special we have to do because
                // the App doesn't expect any more notifications. 
                if ( ( NativeMethods.XACT_E_CONNECTION_DOWN == ex.ErrorCode ) || 
                    ( NativeMethods.XACT_E_TMNOTAVAILABLE == ex.ErrorCode )
                    ) 
                {
                    if ( DiagnosticTrace.Verbose )
                    {
                        ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                            ex );
                    } 
                } 
                else
                { 
                    throw;
                }
            }
            finally 
            {
                FinishEnlistment(); 
            } 
            if ( DiagnosticTrace.Verbose )
            { 
                MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                    "OletxPreparingEnlistment.ForceRollback"
                    );
            } 
        }
 
        public void Committed() 
        {
            IEnlistmentShim localEnlistmentShim = null; 
            if ( DiagnosticTrace.Verbose )
            {
                MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                    "OletxSinglePhaseEnlistment.Committed" 
                    );
                EnlistmentCallbackPositiveTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                    this.InternalTraceIdentifier, 
                    EnlistmentCallback.Committed
                    ); 
            }

            lock( this )
            { 
                if (!isSinglePhase || (OletxEnlistmentState.SinglePhaseCommitting != state))
                { 
                    throw TransactionException.CreateEnlistmentStateException( SR.GetString( SR.TraceSourceOletx ), null ); 
                }
                state = OletxEnlistmentState.Committed; 
                localEnlistmentShim = this.EnlistmentShim;
            }

            try 
            {
                // This may be the result of a reenlist, which means we don't have a 
                // reference to the proxy. 
                if ( null != localEnlistmentShim )
                { 
                    localEnlistmentShim.PrepareRequestDone( OletxPrepareVoteType.SinglePhase );
                }
            }
            catch ( COMException ex ) 
            {
                // If the TM went down during our call, there is nothing special we have to do because 
                // the App doesn't expect any more notifications. 
                if ( ( NativeMethods.XACT_E_CONNECTION_DOWN == ex.ErrorCode ) ||
                    ( NativeMethods.XACT_E_TMNOTAVAILABLE == ex.ErrorCode ) 
                    )
                {
                    if ( DiagnosticTrace.Verbose )
                    { 
                        ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                            ex ); 
                    } 
                }
                else 
                {
                    throw;
                }
            } 
            finally
            { 
                FinishEnlistment(); 
            }
            if ( DiagnosticTrace.Verbose ) 
            {
                MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                    "OletxSinglePhaseEnlistment.Committed"
                    ); 
            }
        } 
 

        public void Aborted() 
        {
            Aborted( null );
        }
 

        public void Aborted(Exception e) 
        { 
            IEnlistmentShim localEnlistmentShim = null;
            if ( DiagnosticTrace.Verbose ) 
            {
                MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                    "OletxSinglePhaseEnlistment.Aborted"
                    ); 
            }
 
            if ( DiagnosticTrace.Warning ) 
            {
                EnlistmentCallbackNegativeTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                    this.InternalTraceIdentifier,
                    EnlistmentCallback.Aborted
                    );
            } 

            lock( this ) 
            { 
                if (!isSinglePhase || (OletxEnlistmentState.SinglePhaseCommitting != state))
                { 
                    throw TransactionException.CreateEnlistmentStateException( SR.GetString( SR.TraceSourceOletx ), null );
                }
                state = OletxEnlistmentState.Aborted;
 
                localEnlistmentShim = this.EnlistmentShim;
            } 
 
            Interlocked.CompareExchange( ref this.oletxTransaction.realOletxTransaction.innerException, e, null );
 
            try
            {
                if ( null != localEnlistmentShim )
                { 
                    localEnlistmentShim.PrepareRequestDone( OletxPrepareVoteType.Failed );
                } 
            } 
            catch ( COMException ex )
            { 
                // If the TM went down during our call, there is nothing special we have to do because
                // the App doesn't expect any more notifications.
                if ( ( NativeMethods.XACT_E_CONNECTION_DOWN == ex.ErrorCode ) ||
                    ( NativeMethods.XACT_E_TMNOTAVAILABLE == ex.ErrorCode ) 
                    )
                { 
                    if ( DiagnosticTrace.Verbose ) 
                    {
                        ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                            ex );
                    }
                }
                else 
                {
                    throw; 
                } 
            }
            finally 
            {
                FinishEnlistment();
            }
            if ( DiagnosticTrace.Verbose ) 
            {
                MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                    "OletxSinglePhaseEnlistment.Aborted" 
                    );
            } 
        }


        public void InDoubt() 
        {
            InDoubt( null ); 
        } 

 
        public void InDoubt(Exception e)
        {
            IEnlistmentShim localEnlistmentShim = null;
            if ( DiagnosticTrace.Verbose ) 
            {
                MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                    "OletxSinglePhaseEnlistment.InDoubt" 
                    );
            } 

            if ( DiagnosticTrace.Warning )
            {
                EnlistmentCallbackNegativeTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                    this.InternalTraceIdentifier,
                    EnlistmentCallback.InDoubt 
                    ); 
            }
 
            lock( this )
            {
                if (!isSinglePhase || (OletxEnlistmentState.SinglePhaseCommitting != state))
                { 
                    throw TransactionException.CreateEnlistmentStateException( SR.GetString( SR.TraceSourceOletx ), null );
                } 
                state = OletxEnlistmentState.InDoubt; 
                localEnlistmentShim = this.EnlistmentShim;
            } 

            lock( this.oletxTransaction.realOletxTransaction )
            {
                if( this.oletxTransaction.realOletxTransaction.innerException == null ) 
                {
                    this.oletxTransaction.realOletxTransaction.innerException = e; 
                } 
            }
 
            try
            {
                if ( null != localEnlistmentShim )
                { 
                    localEnlistmentShim.PrepareRequestDone( OletxPrepareVoteType.InDoubt );
                } 
            } 
            catch ( COMException ex )
            { 
                // If the TM went down during our call, there is nothing special we have to do because
                // the App doesn't expect any more notifications.
                if ( ( NativeMethods.XACT_E_CONNECTION_DOWN == ex.ErrorCode ) ||
                    ( NativeMethods.XACT_E_TMNOTAVAILABLE == ex.ErrorCode ) 
                    )
                { 
                    if ( DiagnosticTrace.Verbose ) 
                    {
                        ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                            ex );
                    }
                }
                else 
                {
                    throw; 
                } 
            }
            finally 
            {
                FinishEnlistment();
            }
            if ( DiagnosticTrace.Verbose ) 
            {
                MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                    "OletxSinglePhaseEnlistment.InDoubt" 
                    );
            } 
        }

        public byte[] GetRecoveryInformation()
        { 
            if( this.prepareInfoByteArray == null )
            { 
                Debug.Assert( false, string.Format( null, "this.prepareInfoByteArray == null in RecoveryInformation()" )); 
                throw TransactionException.CreateEnlistmentStateException( SR.GetString( SR.TraceSourceOletx ), null );
            } 
            return this.prepareInfoByteArray;
        }

 
        public InternalEnlistment InternalEnlistment
        { 
            get 
            {
                return this.internalEnlistment; 
            }

            set
            { 
                this.internalEnlistment = value;
            } 
        } 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
using System; 
using System.Collections;
using System.Diagnostics;
using System.Globalization;
using System.IO; 
using System.Reflection;
using System.Runtime.InteropServices; 
using System.Security.Permissions; 
using System.Threading;
using System.Transactions; 
using System.Transactions.Diagnostics;

namespace System.Transactions.Oletx
{ 
    [Serializable]
    internal class OletxRecoveryInformation 
    { 
        internal byte[] proxyRecoveryInformation;
 
        internal OletxRecoveryInformation(
            byte[] proxyRecoveryInformation
            )
        { 
            this.proxyRecoveryInformation = proxyRecoveryInformation;
        } 
    } 

    class OletxEnlistment : OletxBaseEnlistment, 
        IPromotedEnlistment
    {
        internal enum OletxEnlistmentState
        { 
            Active,
            Phase0Preparing, 
            Preparing, 
            SinglePhaseCommitting,
            Prepared, 
            Committing,
            Committed,
            Aborting,
            Aborted, 
            InDoubt,
            Done 
        } 

        IEnlistmentShim enlistmentShim; 
        IPhase0EnlistmentShim phase0Shim;
        bool canDoSinglePhase;
        IEnlistmentNotificationInternal iEnlistmentNotification;
        // The information that comes from/goes to the proxy. 
        byte[] proxyPrepareInfoByteArray = null;
 
        OletxEnlistmentState state = OletxEnlistmentState.Active; 

        bool isSinglePhase = false; 
        Guid transactionGuid = Guid.Empty;

        // We need to keep track of the handle for the phase 1 notifications
        // so that if the enlistment terminates the conversation due for instance 
        // to a force rollback the handle can be cleaned up.
        internal IntPtr phase1Handle = IntPtr.Zero; 
 
        // Set to true if we receive an AbortRequest while we still have
        // another notification, like prepare, outstanding.  It indicates that 
        // we need to fabricate a rollback to the app after it responds to Prepare.
        bool fabricateRollback = false;

        bool tmWentDown = false; 
        bool aborting = false;
 
        byte [] prepareInfoByteArray; 

        internal Guid TransactionIdentifier 
        {
            get
            {
                return transactionGuid; 
            }
        } 
 
        #region Constructor
 
        internal OletxEnlistment(
            bool canDoSinglePhase,
            IEnlistmentNotificationInternal enlistmentNotification,
            Guid transactionGuid, 
            EnlistmentOptions enlistmentOptions,
            OletxResourceManager oletxResourceManager, 
            OletxTransaction oletxTransaction 
            ) : base( oletxResourceManager, oletxTransaction )
        { 
            Guid myGuid = Guid.Empty;

            // This will get set later by the creator of this object after it
            // has enlisted with the proxy. 
            this.enlistmentShim = null;
            this.phase0Shim = null; 
 
            this.canDoSinglePhase = canDoSinglePhase;
            this.iEnlistmentNotification = enlistmentNotification; 
            this.state = OletxEnlistmentState.Active;
            this.transactionGuid = transactionGuid;

            this.proxyPrepareInfoByteArray = null; 

            if ( DiagnosticTrace.Information ) 
            { 
                EnlistmentTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                    this.InternalTraceIdentifier, 
                    EnlistmentType.Durable,
                    enlistmentOptions
                    );
            } 

            // Always do this last incase anything earlier fails. 
            AddToEnlistmentTable(); 
        }
 

        internal OletxEnlistment(
            IEnlistmentNotificationInternal enlistmentNotification,
            OletxTransactionStatus xactStatus, 
            byte[] prepareInfoByteArray,
            OletxResourceManager oletxResourceManager 
            ) : base( oletxResourceManager, null ) 
        {
            Guid myGuid = Guid.Empty; 

            // This will get set later by the creator of this object after it
            // has enlisted with the proxy.
            this.enlistmentShim = null; 
            this.phase0Shim = null;
 
            this.canDoSinglePhase = false; 
            this.iEnlistmentNotification = enlistmentNotification;
            this.state = OletxEnlistmentState.Active; 

            // Do this before we do any tracing because it will affect the trace identifiers that we generate.
            Debug.Assert( ( null != prepareInfoByteArray ),
                "OletxEnlistment.ctor - null oletxTransaction without a prepareInfoByteArray" ); 

            int prepareInfoLength = prepareInfoByteArray.Length; 
            this.proxyPrepareInfoByteArray = new byte[ prepareInfoLength ]; 
            Array.Copy(prepareInfoByteArray, proxyPrepareInfoByteArray, prepareInfoLength);
 
            byte[] txGuidByteArray = new byte[ 16 ];
            Array.Copy(proxyPrepareInfoByteArray, txGuidByteArray, 16);

            this.transactionGuid = new Guid( txGuidByteArray ); 
            this.transactionGuidString = this.transactionGuid.ToString();
 
            // If this is being created as part of a Reenlist and we already know the 
            // outcome, then tell the application.
            switch (xactStatus) 
            {
                case OletxTransactionStatus.OLETX_TRANSACTION_STATUS_ABORTED :
                {
                    this.state = OletxEnlistmentState.Aborting; 
                    if ( DiagnosticTrace.Verbose )
                    { 
                        EnlistmentNotificationCallTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                            this.InternalTraceIdentifier,
                            NotificationCall.Rollback 
                            );
                    }

                    iEnlistmentNotification.Rollback( this ); 
                    break;
                } 
 
                case OletxTransactionStatus.OLETX_TRANSACTION_STATUS_COMMITTED :
                { 
                    this.state = OletxEnlistmentState.Committing;
                    // We are going to send the notification to the RM.  We need to put the
                    // enlistment on the reenlistPendingList.  We lock the reenlistList because
                    // we have decided that is the lock that protects both lists.  The entry will 
                    // be taken off the reenlistPendingList when the enlistment has
                    // EnlistmentDone called on it.  The enlistment will call 
                    // RemoveFromReenlistPending. 
                    lock( oletxResourceManager.reenlistList )
                    { 
                        oletxResourceManager.reenlistPendingList.Add( this );
                    }

                    if ( DiagnosticTrace.Verbose ) 
                    {
                        EnlistmentNotificationCallTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                            this.InternalTraceIdentifier, 
                            NotificationCall.Commit
                            ); 
                    }

                    iEnlistmentNotification.Commit( this );
                    break; 
                }
 
                case OletxTransactionStatus.OLETX_TRANSACTION_STATUS_PREPARED : 
                {
                    this.state = OletxEnlistmentState.Prepared; 
                    lock( oletxResourceManager.reenlistList )
                    {
                        oletxResourceManager.reenlistList.Add( this );
                        oletxResourceManager.StartReenlistThread(); 
                    }
                    break; 
                } 

                default : 
                {
                    if ( DiagnosticTrace.Critical )
                    {
                        InternalErrorTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                            SR.GetString( SR.OletxEnlistmentUnexpectedTransactionStatus )
                            ); 
                    } 

                    throw TransactionException.Create( SR.GetString( SR.TraceSourceOletx ), SR.GetString( SR.OletxEnlistmentUnexpectedTransactionStatus ), null ); 
                }
            }

            if ( DiagnosticTrace.Information ) 
            {
                EnlistmentTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                    this.InternalTraceIdentifier, 
                    EnlistmentType.Durable,
                    EnlistmentOptions.None 
                    );
            }

            // Always do this last in case anything prior to this fails. 
            AddToEnlistmentTable();
        } 
        #endregion 

 
        internal IEnlistmentNotificationInternal EnlistmentNotification
        {
            get
            { 
                return iEnlistmentNotification;
            } 
        } 

 
        internal IEnlistmentShim EnlistmentShim
        {
            get{ return this.enlistmentShim; }
            set{ this.enlistmentShim = value; } 
        }
 
 
        internal IPhase0EnlistmentShim Phase0EnlistmentShim
        { 
            get{ return this.phase0Shim; }
            set
            {
                lock( this ) 
                {
                    // If this.aborting is set to true, then we must have already received a 
                    // Phase0Request.  This could happen if the transaction aborts after the 
                    // enlistment is made, but before we are given the shim.
                    if ( ( null != value ) && ( this.aborting || this.tmWentDown ) ) 
                    {
                        value.Phase0Done( false );
                    }
                    this.phase0Shim = value; 
                }
            } 
        } 

 
        internal OletxEnlistmentState State
        {
            get{ return state; }
            set{ state = value; } 
        }
 
        internal byte[] ProxyPrepareInfoByteArray 
        {
            get{ return proxyPrepareInfoByteArray; } 
        }


        internal void FinishEnlistment() 
        {
            lock( this ) 
            { 
                // If we don't have a wrappedTransactionEnlistmentAsync, we may
                // need to remove ourselves from the reenlistPendingList in the 
                // resource manager.
                if ( null == this.enlistmentShim )
                {
                    oletxResourceManager.RemoveFromReenlistPending( this ); 
                }
                this.iEnlistmentNotification = null; 
 
                RemoveFromEnlistmentTable();
            } 
        }

        internal void TMDownFromInternalRM( OletxTransactionManager oletxTm )
        { 
            lock( this )
            { 
                // If we don't have an oletxTransaction or the passed oletxTm matches that of my oletxTransaction, the TM went down. 
                if ( ( null == this.oletxTransaction ) || ( oletxTm == this.oletxTransaction.realOletxTransaction.OletxTransactionManagerInstance ) )
                { 
                    this.tmWentDown = true;
                }
            }
 
            return;
        } 
 

        #region ITransactionResourceAsync methods 

        // ITranactionResourceAsync.PrepareRequest
        public bool PrepareRequest(
            bool singlePhase, 
            byte[] prepareInfo
            ) 
        { 
            IEnlistmentShim localEnlistmentShim = null;
            OletxEnlistmentState localState = OletxEnlistmentState.Active; 
            IEnlistmentNotificationInternal localEnlistmentNotification = null;
            OletxRecoveryInformation oletxRecoveryInformation = null;
            bool enlistmentDone;
 
            lock( this )
            { 
                if ( OletxEnlistmentState.Active == state ) 
                {
                    localState = state = OletxEnlistmentState.Preparing; 
                }
                else
                {
                    // We must have done the prepare work in Phase0, so just remember what state we are 
                    // in now.
                    localState = state; 
                } 

                localEnlistmentNotification = iEnlistmentNotification; 

                localEnlistmentShim = this.EnlistmentShim;

                this.oletxTransaction.realOletxTransaction.TooLateForEnlistments = true; 
            }
 
            // If we went to Preparing state, send the app 
            // a prepare request.
            if ( OletxEnlistmentState.Preparing == localState ) 
            {
                oletxRecoveryInformation = new OletxRecoveryInformation( prepareInfo );
                this.isSinglePhase = singlePhase;
 
                // Store the prepare info we are given.
                Debug.Assert(this.proxyPrepareInfoByteArray == null, "Unexpected value in this.proxyPrepareInfoByteArray"); 
                long arrayLength = prepareInfo.Length; 
                this.proxyPrepareInfoByteArray = new Byte [arrayLength];
                Array.Copy(prepareInfo, this.proxyPrepareInfoByteArray, arrayLength); 

                if ( this.isSinglePhase && this.canDoSinglePhase )
                {
                    ISinglePhaseNotificationInternal singlePhaseNotification = (ISinglePhaseNotificationInternal) localEnlistmentNotification; 
                    state = OletxEnlistmentState.SinglePhaseCommitting;
                    // We don't call DecrementUndecidedEnlistments for Phase1 enlistments. 
                    if ( DiagnosticTrace.Verbose ) 
                    {
                        EnlistmentNotificationCallTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                            this.InternalTraceIdentifier,
                            NotificationCall.SinglePhaseCommit
                            );
                    } 

                    singlePhaseNotification.SinglePhaseCommit( this ); 
                    enlistmentDone = true; 
                }
                else 
                {
                    // We need to turn the oletxRecoveryInformation into a byte array.
                    byte[] oletxRecoveryInformationByteArray = TransactionManager.ConvertToByteArray( oletxRecoveryInformation );
 
                    state = OletxEnlistmentState.Preparing;
 
                    // 
                    this.prepareInfoByteArray = TransactionManager.GetRecoveryInformation(
                        oletxResourceManager.oletxTransactionManager.CreationNodeName, 
                        oletxRecoveryInformationByteArray
                        );

                    if ( DiagnosticTrace.Verbose ) 
                    {
                        EnlistmentNotificationCallTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                            this.InternalTraceIdentifier, 
                            NotificationCall.Prepare
                            ); 
                    }

                    localEnlistmentNotification.Prepare(
                        this 
                        );
                    enlistmentDone = false; 
                } 
            }
            else if ( OletxEnlistmentState.Prepared == localState ) 
            {
                // We must have done our prepare work during Phase0 so just vote Yes.
                try
                { 
                    localEnlistmentShim.PrepareRequestDone( OletxPrepareVoteType.Prepared );
                    enlistmentDone = false; 
                } 
                catch ( COMException comException )
                { 
                    OletxTransactionManager.ProxyException( comException );
                    throw;
                }
            } 
            else if ( OletxEnlistmentState.Done == localState )
            { 
                try 
                {
                    // This was an early vote.  Respond ReadOnly 
                    try
                    {
                        localEnlistmentShim.PrepareRequestDone( OletxPrepareVoteType.ReadOnly );
                        enlistmentDone = true; 
                    }
                    finally 
                    { 
                        FinishEnlistment();
                    } 
                }
                catch ( COMException comException )
                {
                    OletxTransactionManager.ProxyException( comException ); 
                    throw;
                } 
            } 
            else
            { 
                // Any other state means we should vote NO to the proxy.
                try
                {
                    localEnlistmentShim.PrepareRequestDone( OletxPrepareVoteType.Failed ); 
                }
                catch ( COMException ex ) 
                { 
                    // No point in rethrowing this.  We are not on an app thread and we have already told
                    // the app that the transaction is aborting.  When the app calls EnlistmentDone, we will 
                    // do the final release of the ITransactionEnlistmentAsync.
                    if ( DiagnosticTrace.Verbose )
                    {
                        ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                            ex );
                    } 
                } 
                enlistmentDone = true;
            } 

            return enlistmentDone;
        }
 

        public void CommitRequest() 
        { 
            OletxEnlistmentState localState = OletxEnlistmentState.Active;
            IEnlistmentNotificationInternal localEnlistmentNotification = null; 
            IEnlistmentShim localEnlistmentShim = null;
            bool finishEnlistment = false;

            lock( this ) 
            {
                if ( OletxEnlistmentState.Prepared == state ) 
                { 
                    localState = state = OletxEnlistmentState.Committing;
                    localEnlistmentNotification = iEnlistmentNotification; 
                }
                else
                {
                    // We must have received an EnlistmentDone already. 
                    localState = state;
                    localEnlistmentShim = this.EnlistmentShim; 
                    finishEnlistment = true; 
                }
            } 

            if ( null != localEnlistmentNotification )
            {
                if ( DiagnosticTrace.Verbose ) 
                {
                    EnlistmentNotificationCallTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                        this.InternalTraceIdentifier, 
                        NotificationCall.Commit
                        ); 
                }

                localEnlistmentNotification.Commit( this );
            } 
            else if ( null != localEnlistmentShim )
            { 
                // We need to respond to the proxy now. 
                try
                { 
                    localEnlistmentShim.CommitRequestDone();
                }
                catch ( COMException ex )
                { 
                    // If the TM went down during our call, there is nothing special we have to do because
                    // the App doesn't expect any more notifications.  We do want to mark the enlistment 
                    // to finish, however. 
                    if ( ( NativeMethods.XACT_E_CONNECTION_DOWN == ex.ErrorCode ) ||
                        ( NativeMethods.XACT_E_TMNOTAVAILABLE == ex.ErrorCode ) 
                        )
                    {
                        finishEnlistment = true;
                        if ( DiagnosticTrace.Verbose ) 
                        {
                            ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                                ex ); 
                        }
                    } 
                    else
                    {
                        throw;
                    } 
                }
                finally 
                { 
                    if ( finishEnlistment )
                    { 
                        FinishEnlistment();
                    }
                }
            } 
            return;
        } 
 
        public void AbortRequest()
        { 
            OletxEnlistmentState localState = OletxEnlistmentState.Active;
            IEnlistmentNotificationInternal localEnlistmentNotification = null;
            IEnlistmentShim localEnlistmentShim = null;
            bool finishEnlistment = false; 

            lock( this ) 
            { 
                if ( ( OletxEnlistmentState.Active == state ) ||
                     ( OletxEnlistmentState.Prepared == state ) 
                   )
                {
                    localState = state = OletxEnlistmentState.Aborting;
                    localEnlistmentNotification = iEnlistmentNotification; 
                }
                else 
                { 
                    // We must have received an EnlistmentDone already or we have
                    // a notification outstanding (Phase0 prepare). 
                    localState = state;
                    if ( OletxEnlistmentState.Phase0Preparing == state )
                    {
                        this.fabricateRollback = true; 
                    }
                    else 
                    { 
                        finishEnlistment = true;
                    } 

                    localEnlistmentShim = this.EnlistmentShim;
                }
            } 

            if ( null != localEnlistmentNotification ) 
            { 
                if ( DiagnosticTrace.Verbose )
                { 
                    EnlistmentNotificationCallTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                        this.InternalTraceIdentifier,
                        NotificationCall.Rollback
                        ); 
                }
 
                localEnlistmentNotification.Rollback( this ); 
            }
            else if ( null != localEnlistmentShim ) 
            {
                // We need to respond to the proxy now.
                try
                { 
                    localEnlistmentShim.AbortRequestDone();
                } 
                catch ( COMException ex ) 
                {
                    // If the TM went down during our call, there is nothing special we have to do because 
                    // the App doesn't expect any more notifications.  We do want to mark the enlistment
                    // to finish, however.
                    if ( ( NativeMethods.XACT_E_CONNECTION_DOWN == ex.ErrorCode ) ||
                        ( NativeMethods.XACT_E_TMNOTAVAILABLE == ex.ErrorCode ) 
                        )
                    { 
                        finishEnlistment = true; 
                        if ( DiagnosticTrace.Verbose )
                        { 
                            ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                                ex );
                        }
                    } 
                    else
                    { 
                        throw; 
                    }
                } 
                finally
                {
                    if ( finishEnlistment )
                    { 
                        FinishEnlistment();
                    } 
                } 
            }
            return; 
        }

        public void TMDown()
        { 
            // We aren't telling our enlistments about TMDown, only
            // resource managers. 
            // Put this enlistment on the Reenlist list.  The Reenlist thread will get 
            // started when the RMSink gets the TMDown notification.
            lock ( oletxResourceManager.reenlistList ) 
            {
                lock( this )
                {
                    // Remember that we got the TMDown in case we get a Phase0Request after so we 
                    // can avoid doing a Prepare to the app.
                    this.tmWentDown = true; 
 
                    // Only move Prepared and Committing enlistments to the ReenlistList.  All others
                    // do not require a Reenlist to figure out what to do.  We save off Committing 
                    // enlistments because the RM has not acknowledged the commit, so we can't
                    // call RecoveryComplete on the proxy until that has happened.  The Reenlist thread
                    // will loop until the reenlist list is empty and it will leave a Committing
                    // enlistment on the list until it is done, but will NOT call Reenlist on the proxy. 
                    if ( ( OletxEnlistmentState.Prepared == state ) ||
                         ( OletxEnlistmentState.Committing == state ) 
                       ) 
                    {
                        oletxResourceManager.reenlistList.Add( this ); 
                    }
                }
            }
 
            return;
        } 
 
        #endregion
 
        #region ITransactionPhase0NotifyAsync methods

        // ITransactionPhase0NotifyAsync
        public void Phase0Request( 
            bool abortingHint
            ) 
        { 
            IEnlistmentNotificationInternal localEnlistmentNotification = null;
            OletxEnlistmentState localState = OletxEnlistmentState.Active; 
            OletxCommittableTransaction committableTx = null;
            bool commitNotYetCalled = false;

            if ( DiagnosticTrace.Verbose ) 
            {
                MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                    "OletxEnlistment.Phase0Request" 
                    );
            } 

            committableTx = this.oletxTransaction.realOletxTransaction.committableTransaction;
            if ( null != committableTx )
            { 
                // We are dealing with the committable transaction.  If Commit or BeginCommit has NOT been
                // called, then we are dealing with a situation where the TM went down and we are getting 
                // a bogus Phase0Request with abortHint = false (COMPlus bug 36760/36758).  This is an attempt 
                // to not send the app a Prepare request when we know the transaction is going to abort.
                if (!committableTx.CommitCalled ) 
                {
                    commitNotYetCalled = true;
                }
            } 

            lock( this ) 
            { 
                this.aborting = abortingHint;
 
                // The app may have already called EnlistmentDone.  If this occurs, don't bother sending
                // the notification to the app and we don't need to tell the proxy.
                if ( OletxEnlistmentState.Active == state )
                { 
                    // If we got an abort hint or we are the committable transaction and Commit has not yet been called or the TM went down,
                    // we don't want to do any more work on the transaction.  The abort notifications will be sent by the phase 1 
                    // enlistment 
                    if ( ( this.aborting ) || ( commitNotYetCalled ) || ( this.tmWentDown ) )
                    { 
                        // There is a possible ---- where we could get the Phase0Request before we are given the
                        // shim.  In that case, we will vote "no" when we are given the shim.
                        if ( null != this.phase0Shim )
                        { 
                            try
                            { 
                                this.phase0Shim.Phase0Done( false ); 
                            }
                            // I am not going to check for XACT_E_PROTOCOL here because that check is a workaround for a bug 
                            // that only shows up if abortingHint is false.
                            catch ( COMException ex )
                            {
                                if ( DiagnosticTrace.Verbose ) 
                                {
                                    ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                                        ex ); 
                                }
                            } 
                        }
                    }
                    else
                    { 
                        localState = state = OletxEnlistmentState.Phase0Preparing;
                        localEnlistmentNotification = iEnlistmentNotification; 
                    } 
                }
            } 

            // Tell the application to do the work.
            if ( null != localEnlistmentNotification )
            { 
                if ( OletxEnlistmentState.Phase0Preparing == localState )
                { 
                    byte[] txGuidArray = transactionGuid.ToByteArray(); 
                    byte[] rmGuidArray = oletxResourceManager.resourceManagerIdentifier.ToByteArray();
 
                    byte[] temp = new byte[ txGuidArray.Length +
                        rmGuidArray.Length
                        ];
                    Thread.MemoryBarrier(); 
                    this.proxyPrepareInfoByteArray = temp;
                    int index = 0; 
                    for ( index = 0; index < txGuidArray.Length; index++ ) 
                    {
                        proxyPrepareInfoByteArray[index] = 
                            txGuidArray[index];
                    }

                    for ( index = 0; index < rmGuidArray.Length; index++ ) 
                    {
                        proxyPrepareInfoByteArray[txGuidArray.Length + index] = 
                            rmGuidArray[index]; 
                    }
 
                    OletxRecoveryInformation oletxRecoveryInformation = new OletxRecoveryInformation(
                                                                            proxyPrepareInfoByteArray
                                                                            );
                    byte[] oletxRecoveryInformationByteArray = TransactionManager.ConvertToByteArray( oletxRecoveryInformation ); 

                    // 
                    this.prepareInfoByteArray = TransactionManager.GetRecoveryInformation( 
                        oletxResourceManager.oletxTransactionManager.CreationNodeName,
                        oletxRecoveryInformationByteArray 
                        );

                    if ( DiagnosticTrace.Verbose )
                    { 
                        EnlistmentNotificationCallTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                            this.InternalTraceIdentifier, 
                            NotificationCall.Prepare 
                            );
                    } 

                    localEnlistmentNotification.Prepare( this );
                }
                else 
                {
                    // We must have had a ---- between EnlistmentDone and the proxy telling 
                    // us Phase0Request.  Just return. 
                    if ( DiagnosticTrace.Verbose )
                    { 
                        MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                            "OletxEnlistment.Phase0Request"
                            );
                    } 

                    return; 
                } 

            } 
            if ( DiagnosticTrace.Verbose )
            {
                MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                    "OletxEnlistment.Phase0Request" 
                    );
            } 
 
        }
 
        #endregion

        public void EnlistmentDone()
        { 
            if ( DiagnosticTrace.Verbose )
            { 
                MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                    "OletxEnlistment.EnlistmentDone"
                    ); 
                EnlistmentCallbackPositiveTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                    this.InternalTraceIdentifier,
                    EnlistmentCallback.Done
                    ); 
            }
 
            IEnlistmentShim localEnlistmentShim = null; 
            IPhase0EnlistmentShim localPhase0Shim = null;
            OletxEnlistmentState localState = OletxEnlistmentState.Active; 
            bool finishEnlistment;
            bool localFabricateRollback = false;

            lock( this ) 
            {
                localState = state; 
                if ( OletxEnlistmentState.Active == state ) 
                {
                    // Early vote.  If we are doing Phase0, we need to unenlist.  Otherwise, just 
                    // remember.
                    localPhase0Shim = this.Phase0EnlistmentShim;
                    if ( null != localPhase0Shim )
                    { 
                        // We are a Phase0 enlistment and we have a vote - decrement the undecided enlistment count.
                        // We only do this for Phase0 because we don't count Phase1 durable enlistments. 
                        oletxTransaction.realOletxTransaction.DecrementUndecidedEnlistments(); 
                    }
                    finishEnlistment = false; 
                }
                else if ( OletxEnlistmentState.Preparing == state )
                {
                    // Read only vote.  Tell the proxy and go to the Done state. 
                    localEnlistmentShim = this.EnlistmentShim;
                    // We don't decrement the undecided enlistment count for Preparing because we only count 
                    // Phase0 enlistments and we are in Phase1 in Preparing state. 
                    finishEnlistment = true;
                } 
                else if ( OletxEnlistmentState.Phase0Preparing == state )
                {
                    // Read only vote to Phase0.  Tell the proxy okay and go to the Done state.
                    localPhase0Shim = this.Phase0EnlistmentShim; 
                    // We are a Phase0 enlistment and we have a vote - decrement the undecided enlistment count.
                    // We only do this for Phase0 because we don't count Phase1 durable enlistments. 
                    oletxTransaction.realOletxTransaction.DecrementUndecidedEnlistments(); 

                    // If we would have fabricated a rollback then we have already received an abort request 
                    // from proxy and will not receive any more notifications.  Otherwise more notifications
                    // will be coming.
                    if( this.fabricateRollback )
                    { 
                        finishEnlistment = true;
                    } 
                    else 
                    {
                        finishEnlistment = false; 
                    }
                }
                else if ( ( OletxEnlistmentState.Committing == state ) ||
                    ( OletxEnlistmentState.Aborting == state ) || 
                    ( OletxEnlistmentState.SinglePhaseCommitting == state )
                    ) 
                { 
                    localEnlistmentShim = this.EnlistmentShim;
                    finishEnlistment = true; 
                    // We don't decrement the undecided enlistment count for SinglePhaseCommitting because we only
                    // do it for Phase0 enlistments.
                }
                else 
                {
                    throw TransactionException.CreateEnlistmentStateException( SR.GetString( SR.TraceSourceOletx ), null ); 
                } 

                // If this.fabricateRollback is true, it means that we are fabricating this 
                // AbortRequest, rather than having the proxy tell us.  So we don't need
                // to respond to the proxy with AbortRequestDone.
                localFabricateRollback = this.fabricateRollback;
 
                state = OletxEnlistmentState.Done;
            } 
 
            try
            { 
                if ( null != localEnlistmentShim )
                {
                    if ( OletxEnlistmentState.Preparing == localState )
                    { 
                        try
                        { 
                            localEnlistmentShim.PrepareRequestDone( OletxPrepareVoteType.ReadOnly ); 
                        }
                        finally 
                        {
                            HandleTable.FreeHandle( this.phase1Handle );
                        }
                    } 
                    else if ( OletxEnlistmentState.Committing == localState )
                    { 
                        localEnlistmentShim.CommitRequestDone(); 
                    }
                    else if ( OletxEnlistmentState.Aborting == localState ) 
                    {
                        // If localFabricatRollback is true, it means that we are fabricating this
                        // AbortRequest, rather than having the proxy tell us.  So we don't need
                        // to respond to the proxy with AbortRequestDone. 
                        if ( ! localFabricateRollback )
                        { 
                            localEnlistmentShim.AbortRequestDone(); 
                        }
                    } 
                    else if ( OletxEnlistmentState.SinglePhaseCommitting == localState )
                    {
                        localEnlistmentShim.PrepareRequestDone( OletxPrepareVoteType.SinglePhase );
                    } 
                    else
                    { 
                        throw TransactionException.CreateEnlistmentStateException( SR.GetString( SR.TraceSourceOletx ), null ); 
                    }
                } 
                else if ( null != localPhase0Shim )
                {
                    if ( OletxEnlistmentState.Active == localState )
                    { 
                        localPhase0Shim.Unenlist();
                    } 
                    else if ( OletxEnlistmentState.Phase0Preparing == localState ) 
                    {
                        localPhase0Shim.Phase0Done( true ); 
                    }
                    else
                    {
                        throw TransactionException.CreateEnlistmentStateException( SR.GetString( SR.TraceSourceOletx ), null ); 
                    }
                } 
 
            }
            catch ( COMException ex ) 
            {
                // If we get an error talking to the proxy, there is nothing special we have to do because
                // the App doesn't expect any more notifications.  We do want to mark the enlistment
                // to finish, however. 
                finishEnlistment = true;
 
                if ( DiagnosticTrace.Verbose ) 
                {
                    ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                        ex );
                }
            }
            finally 
            {
                if ( finishEnlistment ) 
                { 
                    FinishEnlistment();
                } 
            }
            if ( DiagnosticTrace.Verbose )
            {
                MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                    "OletxEnlistment.EnlistmentDone"
                    ); 
            } 
        }
 

        public EnlistmentTraceIdentifier EnlistmentTraceId
        {
            get 
            {
                if ( DiagnosticTrace.Verbose ) 
                { 
                    MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                        "OletxEnlistment.get_TraceIdentifier" 
                        );
                    MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                        "OletxEnlistment.get_TraceIdentifier"
                        ); 
                }
 
                return this.InternalTraceIdentifier; 
            }
        } 

        public void Prepared()
        {
            int hrResult = NativeMethods.S_OK; 
            IEnlistmentShim localEnlistmentShim = null;
            IPhase0EnlistmentShim localPhase0Shim = null; 
            bool localFabricateRollback = false; 

            if ( DiagnosticTrace.Verbose ) 
            {
                MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                    "OletxPreparingEnlistment.Prepared"
                    ); 
                EnlistmentCallbackPositiveTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                    this.InternalTraceIdentifier, 
                    EnlistmentCallback.Prepared 
                    );
            } 

            lock( this )
            {
                if ( OletxEnlistmentState.Preparing == state ) 
                {
                    localEnlistmentShim = this.EnlistmentShim; 
                } 
                else if ( OletxEnlistmentState.Phase0Preparing == state )
                { 
                    // If the transaction is doomed or we have fabricateRollback is true because the
                    // transaction aborted while the Phase0 Prepare request was outstanding,
                    // release the WrappedTransactionPhase0EnlistmentAsync and remember that
                    // we have a pending rollback. 
                    localPhase0Shim = this.Phase0EnlistmentShim;
                    if ( ( oletxTransaction.realOletxTransaction.Doomed ) || 
                         ( this.fabricateRollback ) 
                       )
                    { 
                        // Set fabricateRollback in case we got here because the transaction is doomed.
                        this.fabricateRollback = true;
                        localFabricateRollback = this.fabricateRollback;
                    } 
                }
                else 
                { 
                    throw TransactionException.CreateEnlistmentStateException( SR.GetString( SR.TraceSourceOletx ), null );
                } 

                state = OletxEnlistmentState.Prepared;

            } 

            try 
            { 
                if ( null != localEnlistmentShim )
                { 
                    localEnlistmentShim.PrepareRequestDone( OletxPrepareVoteType.Prepared );
                }
                else if ( null != localPhase0Shim )
                { 
                    // We have a vote - decrement the undecided enlistment count.  We do
                    // this after checking Doomed because ForceRollback will decrement also. 
                    // We also do this only for Phase0 enlistments. 
                    oletxTransaction.realOletxTransaction.DecrementUndecidedEnlistments();
 
                    localPhase0Shim.Phase0Done( !localFabricateRollback );
                }

                else 
                    // The TM must have gone down, thus causing our interface pointer to be
                    // invalidated.  So we need to drive abort of the enlistment as if we 
                    // received an AbortRequest. 
                {
                    localFabricateRollback = true; 
                }

                if ( localFabricateRollback )
                { 
                    AbortRequest();
                } 
            } 
            catch ( COMException ex )
            { 
                // If the TM went down during our call, the TMDown notification to the enlistment
                // and RM will put this enlistment on the ReenlistList, if appropriate.  The outcome
                // will be obtained by the ReenlistThread.
                if ( ( NativeMethods.XACT_E_CONNECTION_DOWN == ex.ErrorCode ) || 
                    ( NativeMethods.XACT_E_TMNOTAVAILABLE == ex.ErrorCode )
                    ) 
                { 
                    if ( DiagnosticTrace.Verbose )
                    { 
                        ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                            ex );
                    }
                } 
                // In the case of Phase0, there is a bug in the proxy that causes an XACT_E_PROTOCOL
                // error if the TM goes down while the enlistment is still active.  The Phase0Request is 
                // sent out with abortHint false, but the state of the proxy object is not changed, causing 
                // Phase0Done request to fail with XACT_E_PROTOCOL.
                // For Prepared, we want to make sure the proxy aborts the transaction.  We don't need 
                // to drive the abort to the application here because the Phase1 enlistment will do that.
                // In other words, treat this as if the proxy said Phase0Request( abortingHint = true ).
                else if ( NativeMethods.XACT_E_PROTOCOL == ex.ErrorCode )
                { 
                    this.Phase0EnlistmentShim = null;
                    if ( DiagnosticTrace.Verbose ) 
                    { 
                        ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                            ex ); 
                    }
                }
                else
                { 
                    throw;
                } 
            } 
            if ( DiagnosticTrace.Verbose )
            { 
                MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                    "OletxPreparingEnlistment.Prepared"
                    );
            } 
        }
 
 
        public void ForceRollback()
        { 
            ForceRollback( null );
        }

        public void ForceRollback( Exception e ) 
        {
            IEnlistmentShim localEnlistmentShim = null; 
            IPhase0EnlistmentShim localPhase0Shim = null; 

            if ( DiagnosticTrace.Verbose ) 
            {
                MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                    "OletxPreparingEnlistment.ForceRollback"
                    ); 
            }
 
            if ( DiagnosticTrace.Warning ) 
            {
                EnlistmentCallbackNegativeTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                    this.InternalTraceIdentifier,
                    EnlistmentCallback.ForceRollback
                    );
            } 

            lock( this ) 
            { 
                if ( OletxEnlistmentState.Preparing == state )
                { 
                    localEnlistmentShim = this.EnlistmentShim;
                }
                else if ( OletxEnlistmentState.Phase0Preparing == state )
                { 
                    localPhase0Shim = this.Phase0EnlistmentShim;
                    if ( null != localPhase0Shim ) 
                    { 

                        // We have a vote - decrement the undecided enlistment count.  We only do this 
                        // if we are Phase0 enlistment.
                        oletxTransaction.realOletxTransaction.DecrementUndecidedEnlistments();
                    }
 
                }
                else 
                { 
                    throw TransactionException.CreateEnlistmentStateException( SR.GetString( SR.TraceSourceOletx ), null );
                } 

                state = OletxEnlistmentState.Aborted;
            }
 
            Interlocked.CompareExchange( ref this.oletxTransaction.realOletxTransaction.innerException, e, null );
 
            try 
            {
                if ( null != localEnlistmentShim ) 
                {
                    try
                    {
                        localEnlistmentShim.PrepareRequestDone( OletxPrepareVoteType.Failed ); 
                    }
                    finally 
                    { 
                        HandleTable.FreeHandle( this.phase1Handle );
                    } 
                }

                if ( null != localPhase0Shim )
                { 
                    localPhase0Shim.Phase0Done( false );
                } 
//                else 
                    // The TM must have gone down, thus causing our interface pointer to be
                    // invalidated.  The App doesn't expect any more notifications, so we can 
                    // just finish the enlistment.
            }
            catch ( COMException ex )
            { 
                // If the TM went down during our call, there is nothing special we have to do because
                // the App doesn't expect any more notifications. 
                if ( ( NativeMethods.XACT_E_CONNECTION_DOWN == ex.ErrorCode ) || 
                    ( NativeMethods.XACT_E_TMNOTAVAILABLE == ex.ErrorCode )
                    ) 
                {
                    if ( DiagnosticTrace.Verbose )
                    {
                        ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                            ex );
                    } 
                } 
                else
                { 
                    throw;
                }
            }
            finally 
            {
                FinishEnlistment(); 
            } 
            if ( DiagnosticTrace.Verbose )
            { 
                MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                    "OletxPreparingEnlistment.ForceRollback"
                    );
            } 
        }
 
        public void Committed() 
        {
            IEnlistmentShim localEnlistmentShim = null; 
            if ( DiagnosticTrace.Verbose )
            {
                MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                    "OletxSinglePhaseEnlistment.Committed" 
                    );
                EnlistmentCallbackPositiveTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                    this.InternalTraceIdentifier, 
                    EnlistmentCallback.Committed
                    ); 
            }

            lock( this )
            { 
                if (!isSinglePhase || (OletxEnlistmentState.SinglePhaseCommitting != state))
                { 
                    throw TransactionException.CreateEnlistmentStateException( SR.GetString( SR.TraceSourceOletx ), null ); 
                }
                state = OletxEnlistmentState.Committed; 
                localEnlistmentShim = this.EnlistmentShim;
            }

            try 
            {
                // This may be the result of a reenlist, which means we don't have a 
                // reference to the proxy. 
                if ( null != localEnlistmentShim )
                { 
                    localEnlistmentShim.PrepareRequestDone( OletxPrepareVoteType.SinglePhase );
                }
            }
            catch ( COMException ex ) 
            {
                // If the TM went down during our call, there is nothing special we have to do because 
                // the App doesn't expect any more notifications. 
                if ( ( NativeMethods.XACT_E_CONNECTION_DOWN == ex.ErrorCode ) ||
                    ( NativeMethods.XACT_E_TMNOTAVAILABLE == ex.ErrorCode ) 
                    )
                {
                    if ( DiagnosticTrace.Verbose )
                    { 
                        ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                            ex ); 
                    } 
                }
                else 
                {
                    throw;
                }
            } 
            finally
            { 
                FinishEnlistment(); 
            }
            if ( DiagnosticTrace.Verbose ) 
            {
                MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                    "OletxSinglePhaseEnlistment.Committed"
                    ); 
            }
        } 
 

        public void Aborted() 
        {
            Aborted( null );
        }
 

        public void Aborted(Exception e) 
        { 
            IEnlistmentShim localEnlistmentShim = null;
            if ( DiagnosticTrace.Verbose ) 
            {
                MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                    "OletxSinglePhaseEnlistment.Aborted"
                    ); 
            }
 
            if ( DiagnosticTrace.Warning ) 
            {
                EnlistmentCallbackNegativeTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                    this.InternalTraceIdentifier,
                    EnlistmentCallback.Aborted
                    );
            } 

            lock( this ) 
            { 
                if (!isSinglePhase || (OletxEnlistmentState.SinglePhaseCommitting != state))
                { 
                    throw TransactionException.CreateEnlistmentStateException( SR.GetString( SR.TraceSourceOletx ), null );
                }
                state = OletxEnlistmentState.Aborted;
 
                localEnlistmentShim = this.EnlistmentShim;
            } 
 
            Interlocked.CompareExchange( ref this.oletxTransaction.realOletxTransaction.innerException, e, null );
 
            try
            {
                if ( null != localEnlistmentShim )
                { 
                    localEnlistmentShim.PrepareRequestDone( OletxPrepareVoteType.Failed );
                } 
            } 
            catch ( COMException ex )
            { 
                // If the TM went down during our call, there is nothing special we have to do because
                // the App doesn't expect any more notifications.
                if ( ( NativeMethods.XACT_E_CONNECTION_DOWN == ex.ErrorCode ) ||
                    ( NativeMethods.XACT_E_TMNOTAVAILABLE == ex.ErrorCode ) 
                    )
                { 
                    if ( DiagnosticTrace.Verbose ) 
                    {
                        ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                            ex );
                    }
                }
                else 
                {
                    throw; 
                } 
            }
            finally 
            {
                FinishEnlistment();
            }
            if ( DiagnosticTrace.Verbose ) 
            {
                MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                    "OletxSinglePhaseEnlistment.Aborted" 
                    );
            } 
        }


        public void InDoubt() 
        {
            InDoubt( null ); 
        } 

 
        public void InDoubt(Exception e)
        {
            IEnlistmentShim localEnlistmentShim = null;
            if ( DiagnosticTrace.Verbose ) 
            {
                MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                    "OletxSinglePhaseEnlistment.InDoubt" 
                    );
            } 

            if ( DiagnosticTrace.Warning )
            {
                EnlistmentCallbackNegativeTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                    this.InternalTraceIdentifier,
                    EnlistmentCallback.InDoubt 
                    ); 
            }
 
            lock( this )
            {
                if (!isSinglePhase || (OletxEnlistmentState.SinglePhaseCommitting != state))
                { 
                    throw TransactionException.CreateEnlistmentStateException( SR.GetString( SR.TraceSourceOletx ), null );
                } 
                state = OletxEnlistmentState.InDoubt; 
                localEnlistmentShim = this.EnlistmentShim;
            } 

            lock( this.oletxTransaction.realOletxTransaction )
            {
                if( this.oletxTransaction.realOletxTransaction.innerException == null ) 
                {
                    this.oletxTransaction.realOletxTransaction.innerException = e; 
                } 
            }
 
            try
            {
                if ( null != localEnlistmentShim )
                { 
                    localEnlistmentShim.PrepareRequestDone( OletxPrepareVoteType.InDoubt );
                } 
            } 
            catch ( COMException ex )
            { 
                // If the TM went down during our call, there is nothing special we have to do because
                // the App doesn't expect any more notifications.
                if ( ( NativeMethods.XACT_E_CONNECTION_DOWN == ex.ErrorCode ) ||
                    ( NativeMethods.XACT_E_TMNOTAVAILABLE == ex.ErrorCode ) 
                    )
                { 
                    if ( DiagnosticTrace.Verbose ) 
                    {
                        ExceptionConsumedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                            ex );
                    }
                }
                else 
                {
                    throw; 
                } 
            }
            finally 
            {
                FinishEnlistment();
            }
            if ( DiagnosticTrace.Verbose ) 
            {
                MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ), 
                    "OletxSinglePhaseEnlistment.InDoubt" 
                    );
            } 
        }

        public byte[] GetRecoveryInformation()
        { 
            if( this.prepareInfoByteArray == null )
            { 
                Debug.Assert( false, string.Format( null, "this.prepareInfoByteArray == null in RecoveryInformation()" )); 
                throw TransactionException.CreateEnlistmentStateException( SR.GetString( SR.TraceSourceOletx ), null );
            } 
            return this.prepareInfoByteArray;
        }

 
        public InternalEnlistment InternalEnlistment
        { 
            get 
            {
                return this.internalEnlistment; 
            }

            set
            { 
                this.internalEnlistment = value;
            } 
        } 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK