File: System\Transactions\TransactionInterop.cs
Project: ndp\cdf\src\NetFx20\System.Transactions\System.Transactions.csproj (System.Transactions)
//-----------------------------------------------------------------------------
// <copyright file="TransactionInterop.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//-----------------------------------------------------------------------------
 
using System;
using System.Configuration;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Transactions.Oletx;
using System.Transactions.Configuration;
using System.Transactions.Diagnostics;
 
 
namespace System.Transactions
{
    // This is here for the "DTC" in the name.
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
    [ComImport,
    Guid("0fb15084-af41-11ce-bd2b-204c4f4f5020"),
    InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IDtcTransaction 
    {
        void Commit(int retaining,
            [MarshalAs(UnmanagedType.I4)] int commitType,
            int reserved);
 
        void Abort(IntPtr reason,
            int retaining,
            int async);
 
        void GetTransactionInfo( IntPtr transactionInformation);
    }
 
    
    [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name = "FullTrust")]
    public static class TransactionInterop
    {
        internal static OletxTransaction ConvertToOletxTransaction(
            Transaction transaction
            )
        {
            if ( null == transaction )
            {
                throw new ArgumentNullException( "transaction" );
            }
 
            if ( transaction.Disposed )
            {
                throw new ObjectDisposedException( "Transaction" );
            }
 
            if ( transaction.complete )
            {
                throw TransactionException.CreateTransactionCompletedException(SR.GetString(SR.TraceSourceLtm), transaction.DistributedTxId);
            }
 
            OletxTransaction oletxTx = transaction.Promote();
            System.Diagnostics.Debug.Assert( oletxTx != null, "transaction.Promote returned null instead of throwing." );
 
            return oletxTx;
        }
 
 
        /// <summary>
        /// This is the PromoterType value that indicates that the transaction is promoting to MSDTC.
        /// 
        /// If using the variation of Transaction.EnlistPromotableSinglePhase that takes a PromoterType and the
        /// ITransactionPromoter being used promotes to MSDTC, then this is the value that should be 
        /// specified for the PromoterType parameter to EnlistPromotableSinglePhase.
        /// 
        /// If using the variation of Transaction.EnlistPromotableSinglePhase that assumes promotion to MSDTC and
        /// it that returns false, the caller can compare this value with Transaction.PromoterType to
        /// verify that the transaction promoted, or will promote, to MSDTC. If the Transaction.PromoterType
        /// matches this value, then the caller can continue with its enlistment with MSDTC. But if it
        /// does not match, the caller will not be able to enlist with MSDTC.
        /// </summary>
        public static readonly Guid PromoterTypeDtc = new Guid("14229753-FFE1-428D-82B7-DF73045CB8DA");
 
        // This is here for the DangerousGetHandle call.  We need to do it.
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods")]
        public static byte[] GetExportCookie(
            Transaction transaction,
            byte[] whereabouts
            )
        {
            if ( !TransactionManager._platformValidated ) TransactionManager.ValidatePlatform();
 
            byte[] cookie = null;
 
            if ( null == transaction )
            {
                throw new ArgumentNullException( "transaction" );
            }
 
            if ( null == whereabouts )
            {
                throw new ArgumentNullException( "whereabouts" );
            }
 
            if ( DiagnosticTrace.Verbose )
            {
                MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                    "TransactionInterop.GetExportCookie"
                    );
            }
 
            // Copy the whereabouts so that it cannot be modified later.
            byte[] whereaboutsCopy = new byte[whereabouts.Length];
            Array.Copy(whereabouts, whereaboutsCopy, whereabouts.Length); 
            whereabouts = whereaboutsCopy;
 
            int cookieIndex = 0;
            UInt32 cookieSize = 0;
            CoTaskMemHandle cookieBuffer = null;
 
            // First, make sure we are working with an OletxTransaction.
            OletxTransaction oletxTx = TransactionInterop.ConvertToOletxTransaction( transaction );
 
            try
            {
                oletxTx.realOletxTransaction.TransactionShim.Export(
                    Convert.ToUInt32(whereabouts.Length),
                    whereabouts,
                    out cookieIndex,
                    out cookieSize,
                    out cookieBuffer );
 
                // allocate and fill in the cookie
                cookie = new byte[cookieSize];
                Marshal.Copy( cookieBuffer.DangerousGetHandle(), cookie, 0, Convert.ToInt32(cookieSize) );
            }
            catch (COMException comException)
            {
                OletxTransactionManager.ProxyException( comException );
 
                // We are unsure of what the exception may mean.  It is possible that 
                // we could get E_FAIL when trying to contact a transaction manager that is
                // being blocked by a fire wall.  On the other hand we may get a COMException
                // based on bad data.  The more common situation is that the data is fine
                // (since it is generated by Microsoft code) and the problem is with 
                // communication.  So in this case we default for unknown exceptions to
                // assume that the problem is with communication.
                throw TransactionManagerCommunicationException.Create( SR.GetString( SR.TraceSourceOletx ), comException );
            }
            finally
            {
                if ( null != cookieBuffer )
                {
                    cookieBuffer.Close();
                }
            }
 
            if ( DiagnosticTrace.Verbose )
            {
                MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                    "TransactionInterop.GetExportCookie"
                    );
            }
            
            return cookie;
        }
 
        public static Transaction GetTransactionFromExportCookie(
            byte[] cookie
            )
        {
            if ( !TransactionManager._platformValidated ) TransactionManager.ValidatePlatform();
 
            if ( null == cookie )
            {
                throw new ArgumentNullException( "cookie" );
            }
 
            if ( cookie.Length < 32 )
            {
                throw new ArgumentException( SR.GetString( SR.InvalidArgument ), "cookie" );
            }
 
            if ( DiagnosticTrace.Verbose )
            {
                MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                    "TransactionInterop.GetTransactionFromExportCookie"
                    );
            }
 
            byte[] cookieCopy = new byte[cookie.Length];
            Array.Copy(cookie, cookieCopy, cookie.Length);
            cookie = cookieCopy;
 
            Transaction transaction = null;
            ITransactionShim transactionShim = null;
            Guid txIdentifier = Guid.Empty;
            OletxTransactionIsolationLevel oletxIsoLevel = OletxTransactionIsolationLevel.ISOLATIONLEVEL_SERIALIZABLE;
            OutcomeEnlistment outcomeEnlistment = null;
            OletxTransaction oleTx = null;
 
            // Extract the transaction guid from the propagation token to see if we already have a
            // transaction object for the transaction.
            byte[] guidByteArray = new byte[16];
            for (int i = 0; i < guidByteArray.Length; i++ )
            {
                // In a cookie, the transaction guid is preceeded by a signature guid.
                guidByteArray[i] = cookie[i + 16];
            }
 
            Guid txId = new Guid( guidByteArray );
 
            // First check to see if there is a promoted LTM transaction with the same ID.  If there
            // is, just return that.
            transaction = TransactionManager.FindPromotedTransaction( txId );
            if ( null != transaction )
            {
                if ( DiagnosticTrace.Verbose )
                {
                    MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                        "TransactionInterop.GetTransactionFromExportCookie"
                        );
                }
                return transaction;
            }
 
            // We need to create a new transaction
            RealOletxTransaction realTx = null;
            OletxTransactionManager oletxTm = TransactionManager.DistributedTransactionManager;
 
            oletxTm.dtcTransactionManagerLock.AcquireReaderLock( -1 );
            try
            {
                outcomeEnlistment = new OutcomeEnlistment();
                IntPtr outcomeEnlistmentHandle = IntPtr.Zero;
                RuntimeHelpers.PrepareConstrainedRegions();
                try
                {
                    outcomeEnlistmentHandle = HandleTable.AllocHandle( outcomeEnlistment );
                    oletxTm.DtcTransactionManager.ProxyShimFactory.Import(
                        Convert.ToUInt32(cookie.Length),
                        cookie,
                        outcomeEnlistmentHandle,
                        out txIdentifier,
                        out oletxIsoLevel,
                        out transactionShim );
                }
                finally
                {
                    if ( transactionShim == null && outcomeEnlistmentHandle != IntPtr.Zero )
                    {
                        HandleTable.FreeHandle( outcomeEnlistmentHandle );
                    }
                }
            }
            catch ( COMException comException )
            {
                OletxTransactionManager.ProxyException( comException );
 
                // We are unsure of what the exception may mean.  It is possible that 
                // we could get E_FAIL when trying to contact a transaction manager that is
                // being blocked by a fire wall.  On the other hand we may get a COMException
                // based on bad data.  The more common situation is that the data is fine
                // (since it is generated by Microsoft code) and the problem is with 
                // communication.  So in this case we default for unknown exceptions to
                // assume that the problem is with communication.
                throw TransactionManagerCommunicationException.Create( SR.GetString( SR.TraceSourceOletx ), comException );
            }
            finally
            {
                oletxTm.dtcTransactionManagerLock.ReleaseReaderLock();
            }
 
            // We need to create a new RealOletxTransaction.
            realTx = new RealOletxTransaction(
                oletxTm,
                transactionShim,
                outcomeEnlistment,
                txIdentifier,
                oletxIsoLevel,
                false );
 
            // Now create the associated OletxTransaction.
            oleTx = new OletxTransaction( realTx );
 
            // If a transaction is found then FindOrCreate will Dispose the oletx
            // created.
            transaction = TransactionManager.FindOrCreatePromotedTransaction( txId, oleTx );
 
            if ( DiagnosticTrace.Verbose )
            {
                MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                    "TransactionInterop.GetTransactionFromExportCookie"
                    );
            }
 
            return transaction;
        }
 
 
        public static byte[] GetTransmitterPropagationToken(
            Transaction transaction
            )
        {
            if ( !TransactionManager._platformValidated ) TransactionManager.ValidatePlatform();
 
            if ( null == transaction )
            {
                throw new ArgumentNullException( "transaction" );
            }
 
            if (DiagnosticTrace.Verbose)
            {
                MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                    "TransactionInterop.GetTransmitterPropagationToken"
                    );
            }
            
            // First, make sure we are working with an OletxTransaction.
            OletxTransaction oletxTx = TransactionInterop.ConvertToOletxTransaction( transaction );
 
            byte[] token = GetTransmitterPropagationToken( oletxTx );
 
            if ( DiagnosticTrace.Verbose )
            {
                MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                    "TransactionInterop.GetTransmitterPropagationToken"
                    );
            }
 
            return token;
        }
 
 
        // This is here for the DangerousGetHandle call.  We need to do it.
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods")]
        internal static byte[] GetTransmitterPropagationToken(
            OletxTransaction oletxTx
            )
        {
            byte[] propagationToken = null;
            CoTaskMemHandle propagationTokenBuffer = null;
            UInt32 tokenSize = 0;
 
            try
            {
                oletxTx.realOletxTransaction.TransactionShim.GetPropagationToken(
                    out tokenSize,
                    out propagationTokenBuffer );
                propagationToken = new byte[tokenSize];
                Marshal.Copy( propagationTokenBuffer.DangerousGetHandle(), propagationToken, 0, Convert.ToInt32(tokenSize) );
            }
            catch (COMException comException)
            {
                OletxTransactionManager.ProxyException( comException );
                throw;
            }
            finally
            {
                if ( null != propagationTokenBuffer )
                {
                    propagationTokenBuffer.Close();
                }
            }
 
            return propagationToken;
        }
 
 
 
        public static Transaction GetTransactionFromTransmitterPropagationToken(
            byte[] propagationToken
            )
        {
            if ( !TransactionManager._platformValidated ) TransactionManager.ValidatePlatform();
 
            Transaction returnValue = null;
 
            if ( null == propagationToken )
            {
                throw new ArgumentNullException( "propagationToken" );
            }
 
            if ( propagationToken.Length < 24 )
            {
                throw new ArgumentException( SR.GetString( SR.InvalidArgument ), "propagationToken" );
            }
            
            if ( DiagnosticTrace.Verbose )
            {
                MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                    "TransactionInterop.GetTransactionFromTransmitterPropagationToken"
                    );
            }
 
            // Extract the transaction guid from the propagation token to see if we already have a
            // transaction object for the transaction.
            byte[] guidByteArray = new byte[16];
            for (int i = 0; i < guidByteArray.Length; i++ )
            {
                // In a propagation token, the transaction guid is preceeded by two version DWORDs.
                guidByteArray[i] = propagationToken[i + 8];
            }
 
            Guid txId = new Guid( guidByteArray );
 
            // First check to see if there is a promoted LTM transaction with the same ID.  If there
            // is, just return that.
            Transaction tx = TransactionManager.FindPromotedTransaction( txId );
            if ( null != tx )
            {
                if ( DiagnosticTrace.Verbose )
                {
                    MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                        "TransactionInterop.GetTransactionFromTransmitterPropagationToken"
                        );
                }
                return tx;
            }
 
            OletxTransaction oleTx = TransactionInterop.GetOletxTransactionFromTransmitterPropigationToken( propagationToken );
 
            // If a transaction is found then FindOrCreate will Dispose the oletx
            // created.
            returnValue = TransactionManager.FindOrCreatePromotedTransaction( txId, oleTx );
 
            if ( DiagnosticTrace.Verbose )
            {
                MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                    "TransactionInterop.GetTransactionFromTransmitterPropagationToken"
                    );
            }
            return returnValue;
        }
 
 
 
        internal static OletxTransaction GetOletxTransactionFromTransmitterPropigationToken(
            byte[] propagationToken
            )
        {
            Guid identifier;
            OletxTransactionIsolationLevel oletxIsoLevel;
            OutcomeEnlistment outcomeEnlistment;
            ITransactionShim transactionShim = null;
 
            if ( null == propagationToken )
            {
                throw new ArgumentNullException( "propagationToken" );
            }
 
            if ( propagationToken.Length < 24 )
            {
                throw new ArgumentException( SR.GetString( SR.InvalidArgument ), "propagationToken" );
            }
 
            byte[] propagationTokenCopy = new byte[propagationToken.Length];
            Array.Copy(propagationToken, propagationTokenCopy, propagationToken.Length);
            propagationToken = propagationTokenCopy;
 
            // First we need to create an OletxTransactionManager from Config.
            OletxTransactionManager oletxTm = TransactionManager.DistributedTransactionManager;
 
            oletxTm.dtcTransactionManagerLock.AcquireReaderLock( -1 );
            try
            {
                outcomeEnlistment = new OutcomeEnlistment();
                IntPtr outcomeEnlistmentHandle = IntPtr.Zero;
                RuntimeHelpers.PrepareConstrainedRegions();
                try
                {
                    outcomeEnlistmentHandle = HandleTable.AllocHandle( outcomeEnlistment );
                    oletxTm.DtcTransactionManager.ProxyShimFactory.ReceiveTransaction(
                        Convert.ToUInt32(propagationToken.Length),
                        propagationToken,
                        outcomeEnlistmentHandle,
                        out identifier,
                        out oletxIsoLevel,
                        out transactionShim
                        );
                }
                finally
                {
                    if ( transactionShim == null && outcomeEnlistmentHandle != IntPtr.Zero )
                    {
                        HandleTable.FreeHandle( outcomeEnlistmentHandle );
                    }
                }
            }
            catch ( COMException comException )
            {
                OletxTransactionManager.ProxyException( comException );
 
                // We are unsure of what the exception may mean.  It is possible that 
                // we could get E_FAIL when trying to contact a transaction manager that is
                // being blocked by a fire wall.  On the other hand we may get a COMException
                // based on bad data.  The more common situation is that the data is fine
                // (since it is generated by Microsoft code) and the problem is with 
                // communication.  So in this case we default for unknown exceptions to
                // assume that the problem is with communication.
                throw TransactionManagerCommunicationException.Create( SR.GetString( SR.TraceSourceOletx ), comException );
            }
            finally
            {
                oletxTm.dtcTransactionManagerLock.ReleaseReaderLock();
            }
 
            RealOletxTransaction realTx = null;
 
            realTx = new RealOletxTransaction(
                oletxTm,
                transactionShim,
                outcomeEnlistment,
                identifier,
                oletxIsoLevel,
                false );
            
            return new OletxTransaction( realTx );
        }
 
 
        // This is here for the "Dtc" in the name.
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
        public static IDtcTransaction GetDtcTransaction(
            Transaction transaction
            )
        {
            if ( !TransactionManager._platformValidated ) TransactionManager.ValidatePlatform();
 
            if ( null == transaction )
            {
                throw new ArgumentNullException( "transaction" );
            }
 
            if (DiagnosticTrace.Verbose)
            {
                MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                    "TransactionInterop.GetDtcTransaction"
                    );
            }
            
            IDtcTransaction transactionNative = null;
 
            // First, make sure we are working with an OletxTransaction.
            OletxTransaction oletxTx = TransactionInterop.ConvertToOletxTransaction( transaction );
 
            try
            {
                oletxTx.realOletxTransaction.TransactionShim.GetITransactionNative( out transactionNative );
            }
            catch ( COMException comException )
            {
                OletxTransactionManager.ProxyException( comException );
                throw;
            }
 
            if ( DiagnosticTrace.Verbose )
            {
                MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                    "TransactionInterop.GetDtcTransaction"
                    );
            }
            return transactionNative;
        }
 
        // This is here for the "DTC" in the name.
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
        public static Transaction GetTransactionFromDtcTransaction(
            IDtcTransaction transactionNative
            )
        {
            if ( !TransactionManager._platformValidated ) TransactionManager.ValidatePlatform();
 
            bool tooLate = false;
            ITransactionShim transactionShim = null;
            Guid txIdentifier = Guid.Empty;
            OletxTransactionIsolationLevel oletxIsoLevel = OletxTransactionIsolationLevel.ISOLATIONLEVEL_SERIALIZABLE;
            OutcomeEnlistment outcomeEnlistment = null;
            RealOletxTransaction realTx = null;
            OletxTransaction oleTx = null;
 
            if ( null == transactionNative )
            {
                throw new ArgumentNullException( "transactionNative" );
            }
 
            Transaction transaction = null;
 
            if ( DiagnosticTrace.Verbose )
            {
                MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                    "TransactionInterop.GetTransactionFromDtc"
                    );
            }
 
            // Let's get the guid of the transaction from the proxy to see if we already
            // have an object.
            ITransactionNativeInternal myTransactionNative = transactionNative as ITransactionNativeInternal;
            if ( null == myTransactionNative )
            {
                throw new ArgumentException( SR.GetString( SR.InvalidArgument ), "transactionNative" );
            }
 
            OletxXactTransInfo xactInfo;
            try
            {
                myTransactionNative.GetTransactionInfo( out xactInfo );
            }
            catch ( COMException ex )
            {
                if ( Oletx.NativeMethods.XACT_E_NOTRANSACTION != ex.ErrorCode )
                {
                    throw;
                }
 
                // If we get here, the transaction has appraently already been committed or aborted.  Allow creation of the
                // OletxTransaction, but it will be marked with a status of InDoubt and attempts to get its Identifier
                // property will result in a TransactionException.
                tooLate = true;
                xactInfo.uow = Guid.Empty;
 
            }
 
            OletxTransactionManager oletxTm = TransactionManager.DistributedTransactionManager;
            if ( ! tooLate )
            {
                // First check to see if there is a promoted LTM transaction with the same ID.  If there
                // is, just return that.
                transaction = TransactionManager.FindPromotedTransaction( xactInfo.uow );
                if ( null != transaction )
                {
                    if ( DiagnosticTrace.Verbose )
                    {
                        MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                            "TransactionInterop.GetTransactionFromDtcTransaction"
                            );
                    }
                    return transaction;
                }
 
                // We need to create a new RealOletxTransaction...
                oletxTm.dtcTransactionManagerLock.AcquireReaderLock( -1 );
                try
                {
                    outcomeEnlistment = new OutcomeEnlistment();
                    IntPtr outcomeEnlistmentHandle = IntPtr.Zero;
                    RuntimeHelpers.PrepareConstrainedRegions();
                    try
                    {
                        outcomeEnlistmentHandle = HandleTable.AllocHandle( outcomeEnlistment );
                        oletxTm.DtcTransactionManager.ProxyShimFactory.CreateTransactionShim(
                            transactionNative,
                            outcomeEnlistmentHandle,
                            out txIdentifier,
                            out oletxIsoLevel,
                            out transactionShim );
                    }
                    finally
                    {
                        if ( transactionShim == null && outcomeEnlistmentHandle != IntPtr.Zero )
                        {
                            HandleTable.FreeHandle( outcomeEnlistmentHandle );
                        }
                    }
                }
                catch ( COMException comException )
                {
                    OletxTransactionManager.ProxyException( comException );
                    throw;
                }
                finally
                {
                    oletxTm.dtcTransactionManagerLock.ReleaseReaderLock();
                }
 
                // We need to create a new RealOletxTransaction.
                realTx = new RealOletxTransaction(
                    oletxTm,
                    transactionShim,
                    outcomeEnlistment,
                    txIdentifier,
                    oletxIsoLevel,
                    false );
 
                oleTx = new OletxTransaction( realTx );
 
                // If a transaction is found then FindOrCreate will Dispose the oletx
                // created.
                transaction = TransactionManager.FindOrCreatePromotedTransaction( xactInfo.uow, oleTx );
            }
            else
            {
                // It was too late to do a clone of the provided ITransactionNative, so we are just going to
                // create a RealOletxTransaction without a transaction shim or outcome enlistment.
                realTx = new RealOletxTransaction(
                    oletxTm,
                    null,
                    null,
                    txIdentifier,
                    OletxTransactionIsolationLevel.ISOLATIONLEVEL_SERIALIZABLE,
                    false );
 
                oleTx = new OletxTransaction( realTx );
                transaction = new Transaction( oleTx );
                TransactionManager.FireDistributedTransactionStarted( transaction );
                oleTx.savedLtmPromotedTransaction = transaction;
 
                InternalTransaction.DistributedTransactionOutcome(transaction.internalTransaction, TransactionStatus.InDoubt);
            }
 
 
            if ( DiagnosticTrace.Verbose )
            {
                MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                    "TransactionInterop.GetTransactionFromDtc"
                    );
            }
            return transaction;
        }
 
        public static byte[] GetWhereabouts( 
            )
        {
            if ( !TransactionManager._platformValidated ) TransactionManager.ValidatePlatform();
 
            byte[] returnValue = null;
 
            if ( DiagnosticTrace.Verbose )
            {
                MethodEnteredTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                    "TransactionInterop.GetWhereabouts"
                    );
            }
 
            OletxTransactionManager oletxTm = TransactionManager.DistributedTransactionManager;
            if ( null == oletxTm )
            {
                throw new ArgumentException( SR.GetString( SR.ArgumentWrongType ), "transactionManager" );
            }
 
            oletxTm.dtcTransactionManagerLock.AcquireReaderLock( -1 );
            try
            {
                returnValue = oletxTm.DtcTransactionManager.Whereabouts;
            }
            finally
            {
                oletxTm.dtcTransactionManagerLock.ReleaseReaderLock();
            }
 
            if ( DiagnosticTrace.Verbose )
            {
                MethodExitedTraceRecord.Trace( SR.GetString( SR.TraceSourceOletx ),
                    "TransactionInterop.GetWhereabouts"
                    );
            }
            return returnValue;
 
        }
    }
}