File: System\ServiceModel\Channels\MsmqQueue.cs
Project: ndp\cdf\src\WCF\ServiceModel\System.ServiceModel.csproj (System.ServiceModel)
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------
namespace System.ServiceModel.Channels
{
    using System.Runtime;
    using System.Runtime.InteropServices;
    using System.Runtime.Versioning;
    using System.Security;
    using System.Security.Permissions;
    using System.ServiceModel;
    using System.ServiceModel.Diagnostics;
    using System.Text;
    using System.Threading;
    using System.Transactions;
 
    enum MsmqTransactionMode
    {
        None,               // do not use a transaction
        Single,             // create a single message transaction
        CurrentOrSingle,    // use Transaction.Current if set, Single otherwise
        CurrentOrNone,      // use Transaction.Current if set, None otherwise
        CurrentOrThrow,     // use Transaction.Current if set, throw otherwise
    }
 
    class MsmqQueue : IDisposable
    {
        MsmqQueueHandle handle;
        bool isBoundToCompletionPort;
        bool isAsyncEnabled;
 
        protected int shareMode;
        protected string formatName;
        protected int accessMode;
 
        public MsmqQueue(string formatName, int accessMode)
        {
            this.formatName = formatName;
            this.accessMode = accessMode;
            this.shareMode = UnsafeNativeMethods.MQ_DENY_NONE;
        }
 
        public MsmqQueue(string formatName, int accessMode, int shareMode)
        {
            this.formatName = formatName;
            this.accessMode = accessMode;
            this.shareMode = shareMode;
        }
 
        protected object ThisLock
        {
            get { return this; }
        }
 
        public string FormatName
        {
            get { return this.formatName; }
        }
 
        public override string ToString()
        {
            return this.formatName;
        }
 
        public void Dispose()
        {
            lock (this.ThisLock)
            {
                CloseQueue();
            }
        }
        
        internal void EnsureOpen()
        {
            GetHandle();
        }
 
        MsmqQueueHandle GetHandleForAsync(out bool useCompletionPort)
        {
            lock (this.ThisLock)
            {
                if (this.handle == null)
                {
                    this.handle = OpenQueue();
                }
                if (!this.isAsyncEnabled)
                {
                    if (IsCompletionPortSupported(this.handle))
                    {
                        ThreadPool.BindHandle(this.handle);
                        this.isBoundToCompletionPort = true;
                    }
                    this.isAsyncEnabled = true;
                }
                useCompletionPort = this.isBoundToCompletionPort;
                return this.handle;
            }
        }
 
        protected MsmqQueueHandle GetHandle()
        {
            lock (this.ThisLock)
            {
                if (this.handle == null)
                {
                    this.handle = OpenQueue();
                }
                return this.handle;
            }
        }
 
        static bool IsCompletionPortSupported(MsmqQueueHandle handle)
        {
            // if it's a kernel handle, then it supports completion ports
            int flags;
#pragma warning suppress 56523
            return UnsafeNativeMethods.GetHandleInformation(handle, out flags) != 0;
        }
 
        [ResourceConsumption(ResourceScope.Machine)]
        internal virtual MsmqQueueHandle OpenQueue()
        {
            MsmqQueueHandle handle;
            int error = UnsafeNativeMethods.MQOpenQueue(this.formatName, this.accessMode,
                                                        this.shareMode, out handle);
            if (error != 0)
            {
                Utility.CloseInvalidOutSafeHandle(handle);
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MsmqException(SR.GetString(SR.MsmqOpenError, MsmqError.GetErrorString(error)), error));
            }
 
            MsmqDiagnostics.QueueOpened(this.formatName);
            return handle;
        }
 
        public virtual void CloseQueue()
        {
            if (this.handle != null)
            {
                CloseQueue(this.handle);
                this.handle = null;
                this.isBoundToCompletionPort = false;
                this.isAsyncEnabled = false;
                MsmqDiagnostics.QueueClosed(this.formatName);
            }
        }
 
        void CloseQueue(MsmqQueueHandle handle)
        {
            handle.Dispose();
        }
 
        protected void HandleIsStale(MsmqQueueHandle handle)
        {
            lock (this.ThisLock)
            {
                if (this.handle == handle)
                {
                    CloseQueue();
                }
            }
        }
 
        public static void GetMsmqInformation(ref Version version, ref bool activeDirectoryEnabled)
        {
            PrivateComputerProperties properties = new PrivateComputerProperties();
            using (properties)
            {
                IntPtr nativePropertiesPointer = properties.Pin();
                try
                {
                    int error = UnsafeNativeMethods.MQGetPrivateComputerInformation(null,
                                                                                    nativePropertiesPointer);
                    if (error != 0)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MsmqException(SR.GetString(
                                                                                                        SR.MsmqGetPrivateComputerInformationError, MsmqError.GetErrorString(error)), error));
                    }
                    int packedVersion = properties.Version.Value;
                    version = new Version(
                        packedVersion >> 24,
                        (packedVersion & 0x00FF0000) >> 16,
                        packedVersion & 0xFFFF);
                    activeDirectoryEnabled = properties.ActiveDirectory.Value;
                }
                finally
                {
                    properties.Unpin();
                }
            }
        }
 
        public static bool IsReadable(string formatName, out MsmqException ex)
        {
            return SupportsAccessMode(formatName, UnsafeNativeMethods.MQ_RECEIVE_ACCESS, out ex);
        }
 
        public static bool IsWriteable(string formatName)
        {
            MsmqException ex;
            return SupportsAccessMode(formatName, UnsafeNativeMethods.MQ_SEND_ACCESS, out ex);
        }
 
        public static bool IsMoveable(string formatName)
        {
            MsmqException ex;
            return SupportsAccessMode(formatName, UnsafeNativeMethods.MQ_MOVE_ACCESS, out ex);
        }
 
        internal static bool IsQueueOpenable(string formatName, int accessMode, int shareMode, out int error)
        {
            MsmqQueueHandle handle;
            error = UnsafeNativeMethods.MQOpenQueue(formatName, accessMode,
                                                        shareMode, out handle);
            if (error != 0)
            {
                Utility.CloseInvalidOutSafeHandle(handle);
                return false;
            }
 
            handle.Dispose();
            return true;
        }
 
        static bool SupportsAccessMode(string formatName, int accessType, out MsmqException msmqException)
        {
            msmqException = null;
 
            try
            {
                using (MsmqQueue msmqQueue = new MsmqQueue(formatName, accessType))
                {
                    msmqQueue.GetHandle();
                }
            }
            catch (Exception ex)
            {
                msmqException = ex as MsmqException;
                if (null != msmqException)
                {
                    return false;
                }
                throw;
            }
            return true;
        }
 
        public static bool TryGetIsTransactional(string formatName, out bool isTransactional)
        {
            using (QueueTransactionProperties properties = new QueueTransactionProperties())
            {
                IntPtr nativePropertiesPointer = properties.Pin();
                try
                {
                    if (UnsafeNativeMethods.MQGetQueueProperties(formatName,
                                                                 nativePropertiesPointer) == 0)
                    {
                        isTransactional = properties.Transaction.Value != UnsafeNativeMethods.MQ_TRANSACTIONAL_NONE;
                        return true;
                    }
                    else
                    {
                        isTransactional = false;
                        MsmqDiagnostics.QueueTransactionalStatusUnknown(formatName);
                        return false;
                    }
                }
                finally
                {
                    properties.Unpin();
                }
            }
        }
 
        protected static bool IsErrorDueToStaleHandle(int error)
        {
            switch (error)
            {
                case UnsafeNativeMethods.MQ_ERROR_STALE_HANDLE:
                case UnsafeNativeMethods.MQ_ERROR_INVALID_HANDLE:
                case UnsafeNativeMethods.MQ_ERROR_INVALID_PARAMETER:
                case UnsafeNativeMethods.MQ_ERROR_QUEUE_DELETED:
                    return true;
                default:
                    return false;
            }
        }
 
        protected static bool IsReceiveErrorDueToInsufficientBuffer(int error)
        {
            switch (error)
            {
                case UnsafeNativeMethods.MQ_ERROR_BUFFER_OVERFLOW:
                case UnsafeNativeMethods.MQ_INFORMATION_FORMATNAME_BUFFER_TOO_SMALL:
                case UnsafeNativeMethods.MQ_ERROR_FORMATNAME_BUFFER_TOO_SMALL:
                case UnsafeNativeMethods.MQ_ERROR_SENDERID_BUFFER_TOO_SMALL:
                case UnsafeNativeMethods.MQ_ERROR_SECURITY_DESCRIPTOR_TOO_SMALL:
                case UnsafeNativeMethods.MQ_ERROR_USER_BUFFER_TOO_SMALL:
                case UnsafeNativeMethods.MQ_ERROR_SENDER_CERT_BUFFER_TOO_SMALL:
                case UnsafeNativeMethods.MQ_ERROR_RESULT_BUFFER_TOO_SMALL:
                case UnsafeNativeMethods.MQ_ERROR_LABEL_BUFFER_TOO_SMALL:
                case UnsafeNativeMethods.MQ_ERROR_SYMM_KEY_BUFFER_TOO_SMALL:
                case UnsafeNativeMethods.MQ_ERROR_SIGNATURE_BUFFER_TOO_SMALL:
                case UnsafeNativeMethods.MQ_ERROR_PROV_NAME_BUFFER_TOO_SMALL:
                    return true;
                default:
                    return false;
            }
        }
 
        public void MarkMessageRejected(long lookupId)
        {
            MsmqQueueHandle handle = GetHandle();
            int error = 0;
            try
            {
                error = UnsafeNativeMethods.MQMarkMessageRejected(handle, lookupId);
            }
            catch (ObjectDisposedException ex)
            {
                MsmqDiagnostics.ExpectedException(ex);
            }
            if (error != 0)
            {
                if (IsErrorDueToStaleHandle(error))
                {
                    HandleIsStale(handle);
                }
 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MsmqException(SR.GetString(SR.MsmqSendError, MsmqError.GetErrorString(error)), error));
            }
        }
 
        int TryMoveMessageDtcTransacted(long lookupId, MsmqQueueHandle sourceQueueHandle, MsmqQueueHandle destinationQueueHandle, MsmqTransactionMode transactionMode)
        {
            IDtcTransaction dtcTransaction = GetNativeTransaction(transactionMode);
            if (dtcTransaction != null)
            {
                try
                {
                    return UnsafeNativeMethods.MQMoveMessage(sourceQueueHandle, destinationQueueHandle,
                                                             lookupId, dtcTransaction);
                }
                finally
                {
                    Marshal.ReleaseComObject(dtcTransaction);
                }
            }
            else
            {
                return UnsafeNativeMethods.MQMoveMessage(sourceQueueHandle, destinationQueueHandle,
                                                         lookupId, (IntPtr)GetTransactionConstant(transactionMode));
            }
        }
 
        public MoveReceiveResult TryMoveMessage(long lookupId, MsmqQueue destinationQueue, MsmqTransactionMode transactionMode)
        {
            MsmqQueueHandle sourceQueueHandle = GetHandle();
            MsmqQueueHandle destinationQueueHandle = destinationQueue.GetHandle();
            int error;
            try
            {
                if (RequiresDtcTransaction(transactionMode))
                {
                    error = TryMoveMessageDtcTransacted(lookupId, sourceQueueHandle, destinationQueueHandle, transactionMode);
                }
                else
                {
                    error = UnsafeNativeMethods.MQMoveMessage(sourceQueueHandle, destinationQueueHandle, 
                                                              lookupId, (IntPtr)GetTransactionConstant(transactionMode));
                }
            }
            catch (ObjectDisposedException ex)
            {
                MsmqDiagnostics.ExpectedException(ex);
                return MoveReceiveResult.Succeeded;
            }
            if (error != 0)
            {
                if (error == UnsafeNativeMethods.MQ_ERROR_MESSAGE_NOT_FOUND)
                    return MoveReceiveResult.MessageNotFound;
                else if (error == UnsafeNativeMethods.MQ_ERROR_MESSAGE_LOCKED_UNDER_TRANSACTION)
                    return MoveReceiveResult.MessageLockedUnderTransaction;
 
                else if (IsErrorDueToStaleHandle(error))
                {
                    HandleIsStale(sourceQueueHandle);
                    destinationQueue.HandleIsStale(destinationQueueHandle);
                }
 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MsmqException(SR.GetString(SR.MsmqSendError, 
                                                                                                         MsmqError.GetErrorString(error)), error));
            }
 
            return MoveReceiveResult.Succeeded;
        }
 
        public virtual ReceiveResult TryReceive(NativeMsmqMessage message, TimeSpan timeout, MsmqTransactionMode transactionMode)
        {
            return TryReceiveInternal(message, timeout, transactionMode, UnsafeNativeMethods.MQ_ACTION_RECEIVE);
        }
        
        ReceiveResult TryReceiveInternal(NativeMsmqMessage message, TimeSpan timeout, MsmqTransactionMode transactionMode, int action)
        {
            TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
            MsmqQueueHandle handle = GetHandle();
 
            while (true)
            {
                int error = ReceiveCore(handle, message, timeoutHelper.RemainingTime(), transactionMode, action);
 
                if (0 == error)
                    return ReceiveResult.MessageReceived;
 
                if (IsReceiveErrorDueToInsufficientBuffer(error))
                {
                    message.GrowBuffers();
                    continue;
                }
                else if (error == UnsafeNativeMethods.MQ_ERROR_IO_TIMEOUT)
                {
                    return ReceiveResult.Timeout;
                }
                else if (error == UnsafeNativeMethods.MQ_ERROR_OPERATION_CANCELLED)
                {
                    return ReceiveResult.OperationCancelled;
                }
                else if (error == UnsafeNativeMethods.MQ_ERROR_INVALID_HANDLE)
                {
                    // should happen only if racing with Close
                    return ReceiveResult.OperationCancelled;
                }
                else if (IsErrorDueToStaleHandle(error))
                {
                    HandleIsStale(handle);
                }
 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MsmqException(SR.GetString(SR.MsmqReceiveError, MsmqError.GetErrorString(error)), error));
            }
        }
 
        public MoveReceiveResult TryReceiveByLookupId(long lookupId, NativeMsmqMessage message, MsmqTransactionMode transactionMode)
        {
            return this.TryReceiveByLookupId(lookupId, message, transactionMode, UnsafeNativeMethods.MQ_LOOKUP_RECEIVE_CURRENT);
        }
 
        public MoveReceiveResult TryReceiveByLookupId(long lookupId, NativeMsmqMessage message, MsmqTransactionMode transactionMode, int action)
        {
            MsmqQueueHandle handle = GetHandle();
            int error = 0;
 
            while (true)
            {
                try
                {
                    error = ReceiveByLookupIdCore(handle, lookupId, message, transactionMode, action);
                }
                catch (ObjectDisposedException ex)
                {
                    // ---- with Close
                    MsmqDiagnostics.ExpectedException(ex);
                    return MoveReceiveResult.Succeeded;
                }
 
                if (0 == error)
                {
                    return MoveReceiveResult.Succeeded;
                }
                
                if (IsReceiveErrorDueToInsufficientBuffer(error))
                {
                    message.GrowBuffers();
                    continue;
                }
                else if (UnsafeNativeMethods.MQ_ERROR_MESSAGE_NOT_FOUND == error)
                {
                    return MoveReceiveResult.MessageNotFound;
                }
                else if (UnsafeNativeMethods.MQ_ERROR_MESSAGE_LOCKED_UNDER_TRANSACTION == error)
                {
                    return MoveReceiveResult.MessageLockedUnderTransaction;
                }
                else if (IsErrorDueToStaleHandle(error))
                {
                    HandleIsStale(handle);
                }
 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MsmqException(SR.GetString(SR.MsmqReceiveError, MsmqError.GetErrorString(error)), error));
            }
        }
    
        [PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
        protected unsafe int ReceiveByLookupIdCoreDtcTransacted(MsmqQueueHandle handle, long lookupId, NativeMsmqMessage message, MsmqTransactionMode transactionMode, int action)
        {
            IDtcTransaction dtcTransaction = GetNativeTransaction(transactionMode);
 
            IntPtr nativePropertiesPointer = message.Pin();
            try
            {
                if (dtcTransaction != null)
                {
                    try
                    {
                        return UnsafeNativeMethods.MQReceiveMessageByLookupId(handle, lookupId, action, nativePropertiesPointer, null, IntPtr.Zero, dtcTransaction);
                    }
                    finally
                    {
                        Marshal.ReleaseComObject(dtcTransaction);
                    }
                }
                else
                {
                    return UnsafeNativeMethods.MQReceiveMessageByLookupId(handle, lookupId, action, nativePropertiesPointer, null, IntPtr.Zero, (IntPtr)GetTransactionConstant(transactionMode));
                }
            }
            finally
            {
                message.Unpin();
            }
 
        }
 
        [PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
        unsafe int ReceiveByLookupIdCore(MsmqQueueHandle handle, long lookupId, NativeMsmqMessage message, MsmqTransactionMode transactionMode, int action)
        {
            if (RequiresDtcTransaction(transactionMode))
            {
                return ReceiveByLookupIdCoreDtcTransacted(handle, lookupId, message, transactionMode, action);
            }
            else
            {
                IntPtr nativePropertiesPointer = message.Pin();
                try
                {
                    return UnsafeNativeMethods.MQReceiveMessageByLookupId(handle, lookupId, action, nativePropertiesPointer, null, IntPtr.Zero, (IntPtr)GetTransactionConstant(transactionMode));
                }
                finally
                {
                    message.Unpin();
                }
            }
        }
 
        [PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
        unsafe int ReceiveCoreDtcTransacted(MsmqQueueHandle handle, NativeMsmqMessage message, TimeSpan timeout, MsmqTransactionMode transactionMode, int action)
        {
            IDtcTransaction dtcTransaction = GetNativeTransaction(transactionMode);
            int timeoutInMilliseconds = TimeoutHelper.ToMilliseconds(timeout);
 
            IntPtr nativePropertiesPointer = message.Pin();
            try
            {
                if (dtcTransaction != null)
                {
                    try
                    {
                        return UnsafeNativeMethods.MQReceiveMessage(handle.DangerousGetHandle(), timeoutInMilliseconds,
                                                                    action, nativePropertiesPointer, null, IntPtr.Zero, IntPtr.Zero, dtcTransaction);
                    }
                    finally
                    {
                        Marshal.ReleaseComObject(dtcTransaction);
                    }
                }
                else
                {
                    return UnsafeNativeMethods.MQReceiveMessage(handle.DangerousGetHandle(), timeoutInMilliseconds,
                                                                action, nativePropertiesPointer, null, IntPtr.Zero, IntPtr.Zero, (IntPtr)GetTransactionConstant(transactionMode));
                }
            }
            finally
            {
                message.Unpin();
            }
        }
 
        [PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
        unsafe int ReceiveCore(MsmqQueueHandle handle, NativeMsmqMessage message, TimeSpan timeout, MsmqTransactionMode transactionMode, int action)
        {
            if (RequiresDtcTransaction(transactionMode))
            {
                return ReceiveCoreDtcTransacted(handle, message, timeout, transactionMode, action);
            }
            else
            {
                int timeoutInMilliseconds = TimeoutHelper.ToMilliseconds(timeout);
                IntPtr nativePropertiesPointer = message.Pin();
                try
                {
                    return UnsafeNativeMethods.MQReceiveMessage(handle.DangerousGetHandle(), timeoutInMilliseconds,
                                                                action, nativePropertiesPointer, null, IntPtr.Zero, IntPtr.Zero, (IntPtr)GetTransactionConstant(transactionMode));
                }
                finally
                {
                    message.Unpin();
                }
            }
        }
 
        protected IDtcTransaction GetNativeTransaction(MsmqTransactionMode transactionMode)
        {
            Transaction transaction = Transaction.Current;
            if (transaction != null)
            {
                return TransactionInterop.GetDtcTransaction(transaction);
            }
            if (transactionMode == MsmqTransactionMode.CurrentOrThrow)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.MsmqTransactionRequired)));
            }
            return null;
        }
 
        public ReceiveResult TryPeek(NativeMsmqMessage message, TimeSpan timeout)
        {
            return TryReceiveInternal(message, timeout, MsmqTransactionMode.None, 
                              UnsafeNativeMethods.MQ_ACTION_PEEK_CURRENT);
        }
 
        bool RequiresDtcTransaction(MsmqTransactionMode transactionMode)
        {
            switch (transactionMode)
            {
                case MsmqTransactionMode.None:
                case MsmqTransactionMode.Single:
                    return false;
                case MsmqTransactionMode.CurrentOrSingle:
                case MsmqTransactionMode.CurrentOrNone:
                case MsmqTransactionMode.CurrentOrThrow:
                    return true;
                default:
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("transactionMode"));
            }
        }
 
        int GetTransactionConstant(MsmqTransactionMode transactionMode)
        {
            switch (transactionMode)
            {
                case MsmqTransactionMode.CurrentOrNone:
                case MsmqTransactionMode.None:
                    return UnsafeNativeMethods.MQ_NO_TRANSACTION;
                case MsmqTransactionMode.Single:
                case MsmqTransactionMode.CurrentOrSingle:
                    return UnsafeNativeMethods.MQ_SINGLE_MESSAGE;
                default:
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("transactionMode"));
            }
        }
 
        int SendDtcTransacted(NativeMsmqMessage message, MsmqTransactionMode transactionMode)
        {
            IDtcTransaction dtcTransaction = GetNativeTransaction(transactionMode);
 
            MsmqQueueHandle handle = GetHandle();
            IntPtr nativePropertiesPointer = message.Pin();
            try
            {
                if (dtcTransaction != null)
                {
                    try
                    {
                        return UnsafeNativeMethods.MQSendMessage(handle, nativePropertiesPointer,
                                                                 dtcTransaction);
                    }
                    finally
                    {
                        Marshal.ReleaseComObject(dtcTransaction);
                    }
                }
                else
                {
                    return UnsafeNativeMethods.MQSendMessage(handle, nativePropertiesPointer,
                                                             (IntPtr)GetTransactionConstant(transactionMode));
                }
            }
            finally
            {
                message.Unpin();
            }
        }
 
        public void Send(NativeMsmqMessage message, MsmqTransactionMode transactionMode)
        {
            int error = 0;
            if (RequiresDtcTransaction(transactionMode))
            {
                error = SendDtcTransacted(message, transactionMode);
            }
            else
            {
                MsmqQueueHandle handle = GetHandle();
                IntPtr nativePropertiesPointer = message.Pin();
                try
                {
                    error = UnsafeNativeMethods.MQSendMessage(handle, nativePropertiesPointer,
                                                              (IntPtr)GetTransactionConstant(transactionMode));
                }
                finally
                {
                    message.Unpin();
                }
            }
 
            if (error != 0)
            {
                if (IsErrorDueToStaleHandle(error))
                {
                    HandleIsStale(handle);
                }
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MsmqException(SR.GetString(SR.MsmqSendError, MsmqError.GetErrorString(error)), error));
            }
        }
 
        [PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
        unsafe int ReceiveCoreAsync(MsmqQueueHandle handle, IntPtr nativePropertiesPointer, TimeSpan timeout,
                                    int action, NativeOverlapped* nativeOverlapped,
                                    UnsafeNativeMethods.MQReceiveCallback receiveCallback)
        {
            int timeoutInMilliseconds = TimeoutHelper.ToMilliseconds(timeout);
 
            return UnsafeNativeMethods.MQReceiveMessage(handle, timeoutInMilliseconds, action,
                                                        nativePropertiesPointer, nativeOverlapped, receiveCallback,
                                                        IntPtr.Zero, (IntPtr)UnsafeNativeMethods.MQ_NO_TRANSACTION);
        }
 
        public IAsyncResult BeginTryReceive(NativeMsmqMessage message, TimeSpan timeout, 
                                            AsyncCallback callback, object state)
        {
            return new TryReceiveAsyncResult(this, message, timeout, 
                                             UnsafeNativeMethods.MQ_ACTION_RECEIVE, callback, state);
        }
 
        public ReceiveResult EndTryReceive(IAsyncResult result)
        {
            return TryReceiveAsyncResult.End(result);
        }
 
        public IAsyncResult BeginPeek(NativeMsmqMessage message, TimeSpan timeout, 
                                      AsyncCallback callback, object state)
        {
            return new TryReceiveAsyncResult(this, message, timeout, 
                                             UnsafeNativeMethods.MQ_ACTION_PEEK_CURRENT, callback, state);
        }
 
        public ReceiveResult EndPeek(IAsyncResult result)
        {
            return TryReceiveAsyncResult.End(result);
        }
 
        class TryReceiveAsyncResult : AsyncResult
        {
            MsmqQueue msmqQueue;
            int action;
            TimeoutHelper timeoutHelper;
            NativeMsmqMessage message;
            unsafe NativeOverlapped* nativeOverlapped = null;
            MsmqQueueHandle handle;
            ReceiveResult receiveResult;
            unsafe static IOCompletionCallback onPortedCompletion = Fx.ThunkCallback(new IOCompletionCallback(OnPortedCompletion));
            unsafe static UnsafeNativeMethods.MQReceiveCallback onNonPortedCompletion;
 
            [PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
            public TryReceiveAsyncResult(MsmqQueue msmqQueue, NativeMsmqMessage message, TimeSpan timeout,
                                         int action, AsyncCallback callback, object state)
                : base(callback, state)
            {
                this.msmqQueue = msmqQueue;
                this.message = message;
                this.action = action;
                this.timeoutHelper = new TimeoutHelper(timeout);
                StartReceive(true);
            }
 
            [PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
            unsafe ~TryReceiveAsyncResult()
            {
                if (null != this.nativeOverlapped
                    && ! Environment.HasShutdownStarted 
                    && ! AppDomain.CurrentDomain.IsFinalizingForUnload())
                {
                    Overlapped.Free(this.nativeOverlapped);
                }
            }
 
            [PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
            unsafe void StartReceive(bool synchronously)
            {
                bool useCompletionPort;
                try
                {
                    this.handle = this.msmqQueue.GetHandleForAsync(out useCompletionPort);
                }
                catch (MsmqException ex)
                {
                    OnCompletion(ex.ErrorCode, synchronously);
                    return;
                }
                if (null != nativeOverlapped)
                {
                    Fx.Assert("---- in StartReceive");
                }
 
                IntPtr nativePropertiesPointer = this.message.Pin();
                nativeOverlapped = new Overlapped(0, 0, IntPtr.Zero, this).UnsafePack(onPortedCompletion, this.message.GetBuffersForAsync());
 
                int error;
                try
                {
                    if (useCompletionPort)
                    {
                        error = msmqQueue.ReceiveCoreAsync(this.handle, nativePropertiesPointer, this.timeoutHelper.RemainingTime(), 
                                                           this.action, nativeOverlapped, null);
                    }
                    else
                    {
                        if (onNonPortedCompletion == null)
                        {
                            onNonPortedCompletion = new UnsafeNativeMethods.MQReceiveCallback(OnNonPortedCompletion);
                        }
                        error = msmqQueue.ReceiveCoreAsync(this.handle, nativePropertiesPointer, this.timeoutHelper.RemainingTime(), 
                                                           this.action, nativeOverlapped, onNonPortedCompletion);
                    }
                }
                catch (ObjectDisposedException ex)
                {
                    // if Close ----s with the async Receive, it is possible that SafeHandle will throw ObjectDisposedException
                    // the behavior should be same as if operation was just cancelled (the channel will return no message)
                    MsmqDiagnostics.ExpectedException(ex);
                    error = UnsafeNativeMethods.MQ_ERROR_OPERATION_CANCELLED;
                }
                if (error != 0)
                {
                    if (error != UnsafeNativeMethods.MQ_INFORMATION_OPERATION_PENDING)
                    {
                        Overlapped.Free(nativeOverlapped);
                        nativeOverlapped = null;
                        GC.SuppressFinalize(this);
                        OnCompletion(error, synchronously);
                    }
                }
            }
 
            [PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
            unsafe static void OnNonPortedCompletion(int error, IntPtr handle, int timeout,
                                                     int action, IntPtr props, NativeOverlapped* nativeOverlapped, IntPtr cursor)
            {
                ThreadPool.UnsafeQueueNativeOverlapped(nativeOverlapped);
            }
 
            [PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
            unsafe static void OnPortedCompletion(uint error, uint numBytes, NativeOverlapped* nativeOverlapped)
            {
                Overlapped overlapped = Overlapped.Unpack(nativeOverlapped);
                TryReceiveAsyncResult result = (TryReceiveAsyncResult)overlapped.AsyncResult;
                if (error != 0)
                {
                    error = (uint)UnsafeNativeMethods.MQGetOverlappedResult(nativeOverlapped);
                }
                Overlapped.Free(nativeOverlapped);
                result.nativeOverlapped = null;
#pragma warning suppress 56508 // Suppression justified. Presharp warning concerns different scenario.
                GC.SuppressFinalize(result);
                result.OnCompletion((int)error, false);
            }
 
            void OnCompletion(int error, bool completedSynchronously)
            {
                Exception completionException = null;
 
                this.receiveResult = ReceiveResult.MessageReceived;
 
                try
                {
                    if (error != 0)
                    {
                        if (error == UnsafeNativeMethods.MQ_ERROR_IO_TIMEOUT)
                        {
                            this.receiveResult = ReceiveResult.Timeout;
                        }
                        else if (error == UnsafeNativeMethods.MQ_ERROR_OPERATION_CANCELLED)
                        {
                            this.receiveResult = ReceiveResult.OperationCancelled;
                        }
                        else
                        {
                            if (IsReceiveErrorDueToInsufficientBuffer(error))
                            {
                                this.message.Unpin();
                                message.GrowBuffers();
                                StartReceive(completedSynchronously);
                                return;
                            }
                            else if (IsErrorDueToStaleHandle(error))
                            {
                                this.msmqQueue.HandleIsStale(this.handle);
                            }
 
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MsmqException(SR.GetString(SR.MsmqReceiveError, MsmqError.GetErrorString(error)), error));
                        }
                    }
                }
                catch (Exception e)
                {
                    if (e is NullReferenceException || e is SEHException)
                        throw;
                    completionException = e;
                }
 
                this.message.Unpin();
                Complete(completedSynchronously, completionException);
            }
 
            public static ReceiveResult End(IAsyncResult result)
            {
                TryReceiveAsyncResult thisPtr = AsyncResult.End<TryReceiveAsyncResult>(result);
                return thisPtr.receiveResult;
            }
        }
 
        class QueueTransactionProperties : NativeMsmqMessage
        {
            ByteProperty transaction;
 
            public QueueTransactionProperties() : base(1)
            {
                this.transaction = new ByteProperty(this, UnsafeNativeMethods.PROPID_Q_TRANSACTION);
            }
 
            public ByteProperty Transaction
            {
                get { return this.transaction; }
            }
        }
 
        class PrivateComputerProperties : NativeMsmqMessage
        {
            IntProperty version;
            BooleanProperty activeDirectory;
 
            public PrivateComputerProperties()
                : base(2)
            {
                this.version = new IntProperty(this, UnsafeNativeMethods.PROPID_PC_VERSION);
                this.activeDirectory = new BooleanProperty(this, UnsafeNativeMethods.PROPID_PC_DS_ENABLED);
            }
 
            public IntProperty Version
            {
                get { return this.version; }
            }
 
            public BooleanProperty ActiveDirectory
            {
                get { return this.activeDirectory; }
            }
        }
 
        public enum MoveReceiveResult
        {
            Unknown,
            Succeeded,
            MessageNotFound,
            MessageLockedUnderTransaction
        }
 
        internal enum ReceiveResult
        {
            Unknown,
            MessageReceived,
            Timeout,
            OperationCancelled
        }
    }
 
    static class MsmqFormatName
    {
        const string systemMessagingLabelPrefix = "LABEL:";
        const string systemMessagingFormatNamePrefix = "FORMATNAME:";
 
        public static string ToSystemMessagingQueueName(string formatName)
        {
            return systemMessagingFormatNamePrefix + formatName;
        }
 
        public static string FromQueuePath(string queuePath)
        {
            int len = 256;
            StringBuilder buffer = new StringBuilder(len);
            int error = UnsafeNativeMethods.MQPathNameToFormatName(queuePath, buffer, ref len);
            if (UnsafeNativeMethods.MQ_ERROR_FORMATNAME_BUFFER_TOO_SMALL == error)
            {
                buffer = new StringBuilder(len);
                error = UnsafeNativeMethods.MQPathNameToFormatName(queuePath, buffer, ref len);
            }
 
            if (0 != error)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                    new MsmqException(SR.GetString(SR.MsmqPathLookupError, queuePath, MsmqError.GetErrorString(error)), error));
            }
            
            return buffer.ToString();
        }
    }
}