File: System\Messaging\Message.cs
Project: ndp\cdf\src\NetFx20\System.Messaging\System.Messaging.csproj (System.Messaging)
//------------------------------------------------------------------------------
// <copyright file="Message.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
 
namespace System.Messaging
{
    using System.Runtime.Serialization.Formatters;
    using System.Text;
    using System.Configuration.Assemblies;
    using System.Runtime.InteropServices;
    using System.Runtime.Serialization;
    using System.ComponentModel;
    using System.Diagnostics;
    using System;
    using System.Globalization;
    using System.Messaging.Interop;
    using System.IO;
    using System.Security.Permissions;
    using Microsoft.Win32;
 
    /// <include file='doc\Message.uex' path='docs/doc[@for="Message"]/*' />
    /// <devdoc>
    ///    <para>
    ///       Provides access to the properties needed to define a
    ///       Message Queuing message.
    ///    </para>
    /// </devdoc>
    [Designer("System.Messaging.Design.MessageDesigner, " + AssemblyRef.SystemDesign)]
    public class Message : Component
    {
        private const int GenericIdSize = 16;
        private const int MessageIdSize = 20;
        private const int DefaultQueueNameSize = 255;
        private const int DefaultCryptographicProviderNameSize = 255;
        private const int DefaultDigitalSignatureSize = 255;
        private const int DefaultSenderCertificateSize = 255;
        private const int DefaultSenderIdSize = 255;
        private const int DefaultSymmetricKeySize = 255;
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.InfiniteTimeout"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Specifies that there is no timeout.
        ///    </para>
        /// </devdoc>
        public static readonly TimeSpan InfiniteTimeout = TimeSpan.FromSeconds(UInt32.MaxValue);
 
        private MessagePropertyFilter filter;
        private string machineName;
        private bool receiveCreated;
        private object cachedBodyObject;
        private Stream cachedBodyStream;
        private IMessageFormatter cachedFormatter;
        private MessageQueue cachedResponseQueue;
        private MessageQueue cachedTransactionStatusQueue;
        private MessageQueue cachedAdminQueue;
        private MessageQueue cachedDestinationQueue;
        internal MessagePropertyVariants properties;
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.Message"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Initializes a new instance of the <see cref='System.Messaging.Message'/> class with an empty body.
        ///    </para>
        /// </devdoc>
        public Message()
        {
            properties = new MessagePropertyVariants();
            receiveCreated = false;
            this.filter = new MessagePropertyFilter();
 
            //Always add Id
            properties.SetUI1Vector(NativeMethods.MESSAGE_PROPID_MSGID, new byte[MessageIdSize]);
            this.filter.Id = true;
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.Message1"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Initializes a new instance of the <see cref='System.Messaging.Message'/>
        ///       class, serializing the object passed as
        ///       an argument.
        ///    </para>
        /// </devdoc>
        public Message(object body)
            : this()
        {
            this.Body = body;
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.Message2"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public Message(object body, IMessageFormatter formatter)
            : this()
        {
            this.Formatter = formatter;
            this.Body = body;
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.Message3"]/*' />
        /// <internalonly/>                
        internal Message(MessagePropertyFilter filter)
        {
            properties = new MessagePropertyVariants();
            receiveCreated = true;
            this.filter = filter;
            if (filter.data1 != 0)
            {
                int data = filter.data1;
 
                if (0 != (data & MessagePropertyFilter.ACKNOWLEDGEMENT))
                    properties.SetUI2(NativeMethods.MESSAGE_PROPID_CLASS, (short)0);
 
                if (0 != (data & MessagePropertyFilter.ACKNOWLEDGE_TYPE))
                    properties.SetUI1(NativeMethods.MESSAGE_PROPID_ACKNOWLEDGE, (byte)0);
 
                if (0 != (data & MessagePropertyFilter.ADMIN_QUEUE))
                {
                    properties.SetString(NativeMethods.MESSAGE_PROPID_ADMIN_QUEUE, new byte[DefaultQueueNameSize * 2]);
                    properties.SetUI4(NativeMethods.MESSAGE_PROPID_ADMIN_QUEUE_LEN, DefaultQueueNameSize);
                }
                if (0 != (data & MessagePropertyFilter.BODY))
                {
                    properties.SetUI1Vector(NativeMethods.MESSAGE_PROPID_BODY, new byte[filter.bodySize]);
                    properties.SetUI4(NativeMethods.MESSAGE_PROPID_BODY_SIZE, filter.bodySize);
                    properties.SetUI4(NativeMethods.MESSAGE_PROPID_BODY_TYPE, 0);
                }
                if (0 != (data & MessagePropertyFilter.LABEL))
                {
                    properties.SetString(NativeMethods.MESSAGE_PROPID_LABEL, new byte[filter.labelSize * 2]);
                    properties.SetUI4(NativeMethods.MESSAGE_PROPID_LABEL_LEN, filter.labelSize);
                }
                if (0 != (data & MessagePropertyFilter.ID))
                    properties.SetUI1Vector(NativeMethods.MESSAGE_PROPID_MSGID, new byte[MessageIdSize]);
 
                if (0 != (data & MessagePropertyFilter.LOOKUP_ID))
                    properties.SetUI8(NativeMethods.MESSAGE_PROPID_LOOKUPID, (long)0);
 
                if (0 != (data & MessagePropertyFilter.USE_DEADLETTER_QUEUE))
                    properties.SetUI1(NativeMethods.MESSAGE_PROPID_JOURNAL, (byte)0);
 
                if (0 != (data & MessagePropertyFilter.RESPONSE_QUEUE))
                {
                    properties.SetString(NativeMethods.MESSAGE_PROPID_RESP_QUEUE, new byte[DefaultQueueNameSize * 2]);
                    properties.SetUI4(NativeMethods.MESSAGE_PROPID_RESP_QUEUE_LEN, DefaultQueueNameSize);
                }
                //Acknowledgment and MessageType are overloaded in MQ.
                if ((0 == (data & MessagePropertyFilter.ACKNOWLEDGEMENT)) && (0 != (data & MessagePropertyFilter.MESSAGE_TYPE)))
                    properties.SetUI2(NativeMethods.MESSAGE_PROPID_CLASS, (short)0);
 
                //Journaling and Deadletter are overloaded in MSMQ
                if ((0 == (data & MessagePropertyFilter.USE_DEADLETTER_QUEUE)) && (0 != (data & MessagePropertyFilter.USE_JOURNALING)))
                    properties.SetUI1(NativeMethods.MESSAGE_PROPID_JOURNAL, (byte)0);
            }
 
            if (filter.data2 != 0)
            {
                int data = filter.data2;
                if (0 != (data & MessagePropertyFilter.APP_SPECIFIC))
                    properties.SetUI4(NativeMethods.MESSAGE_PROPID_APPSPECIFIC, 0);
                if (0 != (data & MessagePropertyFilter.ARRIVED_TIME))
                    properties.SetUI4(NativeMethods.MESSAGE_PROPID_ARRIVEDTIME, 0);
                if (0 != (data & MessagePropertyFilter.ATTACH_SENDER_ID))
                    properties.SetUI4(NativeMethods.MESSAGE_PROPID_SENDERID_TYPE, 0);
                if (0 != (data & MessagePropertyFilter.AUTHENTICATED))
                    properties.SetUI1(NativeMethods.MESSAGE_PROPID_AUTHENTICATED, (byte)0);
 
                if (0 != (data & MessagePropertyFilter.CONNECTOR_TYPE))
                    properties.SetGuid(NativeMethods.MESSAGE_PROPID_CONNECTOR_TYPE, new byte[GenericIdSize]);
                if (0 != (data & MessagePropertyFilter.CORRELATION_ID))
                    properties.SetUI1Vector(NativeMethods.MESSAGE_PROPID_CORRELATIONID, new byte[MessageIdSize]);
                if (0 != (data & MessagePropertyFilter.CRYPTOGRAPHIC_PROVIDER_NAME))
                {
                    properties.SetString(NativeMethods.MESSAGE_PROPID_PROV_NAME, new byte[DefaultCryptographicProviderNameSize * 2]);
                    properties.SetUI4(NativeMethods.MESSAGE_PROPID_PROV_NAME_LEN, DefaultCryptographicProviderNameSize);
                }
                if (0 != (data & MessagePropertyFilter.CRYPTOGRAPHIC_PROVIDER_TYPE))
                    properties.SetUI4(NativeMethods.MESSAGE_PROPID_PROV_TYPE, 0);
                if (0 != (data & MessagePropertyFilter.IS_RECOVERABLE))
                    properties.SetUI1(NativeMethods.MESSAGE_PROPID_DELIVERY, (byte)0);
                if (0 != (data & MessagePropertyFilter.DESTINATION_QUEUE))
                {
                    properties.SetString(NativeMethods.MESSAGE_PROPID_DEST_QUEUE, new byte[DefaultQueueNameSize * 2]);
                    properties.SetUI4(NativeMethods.MESSAGE_PROPID_DEST_QUEUE_LEN, DefaultQueueNameSize);
                }
                if (0 != (data & MessagePropertyFilter.DIGITAL_SIGNATURE))
                {
                    properties.SetUI1Vector(NativeMethods.MESSAGE_PROPID_SIGNATURE, new byte[DefaultDigitalSignatureSize]);
                    properties.SetUI4(NativeMethods.MESSAGE_PROPID_SIGNATURE_LEN, DefaultDigitalSignatureSize);
                }
                if (0 != (data & MessagePropertyFilter.ENCRYPTION_ALGORITHM))
                    properties.SetUI4(NativeMethods.MESSAGE_PROPID_ENCRYPTION_ALG, 0);
                if (0 != (data & MessagePropertyFilter.EXTENSION))
                {
                    properties.SetUI1Vector(NativeMethods.MESSAGE_PROPID_EXTENSION, new byte[filter.extensionSize]);
                    properties.SetUI4(NativeMethods.MESSAGE_PROPID_EXTENSION_LEN, filter.extensionSize);
                }
                if (0 != (data & MessagePropertyFilter.FOREIGN_ADMIN_QUEUE))
                {
                    properties.SetString(NativeMethods.MESSAGE_PROPID_XACT_STATUS_QUEUE, new byte[DefaultQueueNameSize * 2]);
                    properties.SetUI4(NativeMethods.MESSAGE_PROPID_XACT_STATUS_QUEUE_LEN, DefaultQueueNameSize);
                }
                if (0 != (data & MessagePropertyFilter.HASH_ALGORITHM))
                    properties.SetUI4(NativeMethods.MESSAGE_PROPID_HASH_ALG, 0);
                if (0 != (data & MessagePropertyFilter.IS_FIRST_IN_TRANSACTION))
                    properties.SetUI1(NativeMethods.MESSAGE_PROPID_FIRST_IN_XACT, (byte)0);
                if (0 != (data & MessagePropertyFilter.IS_LAST_IN_TRANSACTION))
                    properties.SetUI1(NativeMethods.MESSAGE_PROPID_LAST_IN_XACT, (byte)0);
                if (0 != (data & MessagePropertyFilter.PRIORITY))
                    properties.SetUI1(NativeMethods.MESSAGE_PROPID_PRIORITY, (byte)0);
                if (0 != (data & MessagePropertyFilter.SENDER_CERTIFICATE))
                {
                    properties.SetUI1Vector(NativeMethods.MESSAGE_PROPID_SENDER_CERT, new byte[DefaultSenderCertificateSize]);
                    properties.SetUI4(NativeMethods.MESSAGE_PROPID_SENDER_CERT_LEN, DefaultSenderCertificateSize);
                }
                if (0 != (data & MessagePropertyFilter.SENDER_ID))
                {
                    properties.SetUI1Vector(NativeMethods.MESSAGE_PROPID_SENDERID, new byte[DefaultSenderIdSize]);
                    properties.SetUI4(NativeMethods.MESSAGE_PROPID_SENDERID_LEN, DefaultSenderIdSize);
                }
                if (0 != (data & MessagePropertyFilter.SENT_TIME))
                    properties.SetUI4(NativeMethods.MESSAGE_PROPID_SENTTIME, 0);
                if (0 != (data & MessagePropertyFilter.SOURCE_MACHINE))
                    properties.SetGuid(NativeMethods.MESSAGE_PROPID_SRC_MACHINE_ID, new byte[GenericIdSize]);
                if (0 != (data & MessagePropertyFilter.SYMMETRIC_KEY))
                {
                    properties.SetUI1Vector(NativeMethods.MESSAGE_PROPID_DEST_SYMM_KEY, new byte[DefaultSymmetricKeySize]);
                    properties.SetUI4(NativeMethods.MESSAGE_PROPID_DEST_SYMM_KEY_LEN, DefaultSymmetricKeySize);
                }
                if (0 != (data & MessagePropertyFilter.TIME_TO_BE_RECEIVED))
                    properties.SetUI4(NativeMethods.MESSAGE_PROPID_TIME_TO_BE_RECEIVED, 0);
                if (0 != (data & MessagePropertyFilter.TIME_TO_REACH_QUEUE))
                    properties.SetUI4(NativeMethods.MESSAGE_PROPID_TIME_TO_REACH_QUEUE, 0);
                if (0 != (data & MessagePropertyFilter.TRANSACTION_ID))
                    properties.SetUI1Vector(NativeMethods.MESSAGE_PROPID_XACTID, new byte[MessageIdSize]);
                if (0 != (data & MessagePropertyFilter.USE_AUTHENTICATION))
                    properties.SetUI4(NativeMethods.MESSAGE_PROPID_AUTH_LEVEL, 0);
                if (0 != (data & MessagePropertyFilter.USE_ENCRYPTION))
                    properties.SetUI4(NativeMethods.MESSAGE_PROPID_PRIV_LEVEL, 0);
                if (0 != (data & MessagePropertyFilter.USE_TRACING))
                    properties.SetUI1(NativeMethods.MESSAGE_PROPID_TRACE, (byte)0);
                if (0 != (data & MessagePropertyFilter.VERSION))
                    properties.SetUI4(NativeMethods.MESSAGE_PROPID_VERSION, 0);
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.Acknowledgment"]/*' />
        /// <devdoc>
        ///    <para>Gets the classification
        ///       of acknowledgment messages that Message Queuing posts.</para>
        /// </devdoc>
        [ReadOnly(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MessagingDescription(Res.MsgAcknowledgement)]
        public Acknowledgment Acknowledgment
        {
            get
            {
                if (!this.filter.Acknowledgment)
                {
                    //This message cannot be an acknowledgment, because it has not been sent.
                    if (!receiveCreated)
                        throw new InvalidOperationException(Res.GetString(Res.NotAcknowledgement));
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "Acknowledgment"));
                }
 
                //Casting unsigned short to int, mask off sign extension.
                int res = ((int)properties.GetUI2(NativeMethods.MESSAGE_PROPID_CLASS)) & 0x0000FFFF;
                return (Acknowledgment)res;
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.AcknowledgeType"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets the type of acknowledgment
        ///       message requested
        ///       from
        ///       Message Queuing when a message arrives in the queue.
        ///    </para>
        /// </devdoc>
        [ReadOnly(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MessagingDescription(Res.MsgAcknowledgeType)]
        public AcknowledgeTypes AcknowledgeType
        {
            get
            {
                if (!this.filter.AcknowledgeType)
                {
                    //Return the default.
                    if (!receiveCreated)
                        return AcknowledgeTypes.None;
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "AcknowledgeType"));
                }
 
                return (AcknowledgeTypes)this.properties.GetUI1(NativeMethods.MESSAGE_PROPID_ACKNOWLEDGE);
            }
 
            set
            {
                //If default
                if (value == AcknowledgeTypes.None)
                {
                    this.filter.AcknowledgeType = false;
                    properties.Remove(NativeMethods.MESSAGE_PROPID_ACKNOWLEDGE);
                }
                else
                {
                    this.filter.AcknowledgeType = true;
                    this.properties.SetUI1(NativeMethods.MESSAGE_PROPID_ACKNOWLEDGE, (byte)value);
                }
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.AdministrationQueue"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets the queue used for acknowledgment
        ///       messages.
        ///    </para>
        /// </devdoc>
        [ReadOnly(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MessagingDescription(Res.MsgAdministrationQueue)]
        public MessageQueue AdministrationQueue
        {
            get
            {
                if (!this.filter.AdministrationQueue)
                {
                    //This property has not been set, lets return an undefined value.
                    if (!receiveCreated)
                        return null;
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "AdministrationQueue"));
                }
 
                if (this.cachedAdminQueue == null)
                {
                    if (properties.GetUI4(NativeMethods.MESSAGE_PROPID_ADMIN_QUEUE_LEN) != 0)
                    {
                        string queueFormatName = StringFromBytes(properties.GetString(NativeMethods.MESSAGE_PROPID_ADMIN_QUEUE),
                                                                 properties.GetUI4(NativeMethods.MESSAGE_PROPID_ADMIN_QUEUE_LEN));
 
                        this.cachedAdminQueue = new MessageQueue("FORMATNAME:" + queueFormatName);
                    }
                }
 
                return this.cachedAdminQueue;
            }
 
            set
            {
                if (value != null)
                    this.filter.AdministrationQueue = true;
                else
                {
                    //If default
                    if (this.filter.AdministrationQueue)
                    {
                        this.filter.AdministrationQueue = false;
                        properties.Remove(NativeMethods.MESSAGE_PROPID_ADMIN_QUEUE);
                        properties.Remove(NativeMethods.MESSAGE_PROPID_ADMIN_QUEUE_LEN);
                    }
                }
 
                this.cachedAdminQueue = value;
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.AppSpecific"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets
        ///       application-generated information regarding the message.
        ///    </para>
        /// </devdoc>
        [ReadOnly(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MessagingDescription(Res.MsgAppSpecific)]
        public int AppSpecific
        {
            get
            {
                if (!this.filter.AppSpecific)
                {
                    //Return the default.
                    if (!receiveCreated)
                        return 0;
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "AppSpecific"));
                }
 
                return properties.GetUI4(NativeMethods.MESSAGE_PROPID_APPSPECIFIC);
            }
 
            set
            {
                //If default
                if (value == 0)
                {
                    this.filter.AppSpecific = false;
                    properties.Remove(NativeMethods.MESSAGE_PROPID_APPSPECIFIC);
                }
                else
                {
                    this.filter.AppSpecific = true;
                    properties.SetUI4(NativeMethods.MESSAGE_PROPID_APPSPECIFIC, value);
                }
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.ArrivedTime"]/*' />
        /// <devdoc>
        ///    Indicates when the message arrived at the queue.
        /// </devdoc>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MessagingDescription(Res.MsgArrivedTime)]
        public DateTime ArrivedTime
        {
            get
            {
                if (!this.filter.ArrivedTime)
                {
                    //Undefined at this point, throw an exception.
                    if (!receiveCreated)
                        throw new InvalidOperationException(Res.GetString(Res.ArrivedTimeNotSet));
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "ArrivedTime"));
                }
 
                //Number of seconds ellapsed since 1/1/1970
                DateTime time = new DateTime(1970, 1, 1);
                time = time.AddSeconds(properties.GetUI4(NativeMethods.MESSAGE_PROPID_ARRIVEDTIME)).ToLocalTime();
                return time;
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.AttachSenderId"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets a value indicating whether the sender ID is to be attached
        ///       to the message.
        ///    </para>
        /// </devdoc>
        [ReadOnly(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MessagingDescription(Res.MsgAttachSenderId)]
        public bool AttachSenderId
        {
            get
            {
                if (!this.filter.AttachSenderId)
                {
                    //SenderId is attached by default.
                    if (!receiveCreated)
                        return true;
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "AttachSenderId"));
                }
 
                int type = properties.GetUI4(NativeMethods.MESSAGE_PROPID_SENDERID_TYPE);
                if (type == NativeMethods.MESSAGE_SENDERID_TYPE_NONE)
                    return false;
 
                return true;
            }
 
            set
            {
                //If default.
                if (value)
                {
                    this.filter.AttachSenderId = false;
                    properties.Remove(NativeMethods.MESSAGE_PROPID_SENDERID_TYPE);
                }
                else
                {
                    this.filter.AttachSenderId = true;
                    if (value)
                        properties.SetUI4(NativeMethods.MESSAGE_PROPID_SENDERID_TYPE, NativeMethods.MESSAGE_SENDERID_TYPE_SID);
                    else
                        properties.SetUI4(NativeMethods.MESSAGE_PROPID_SENDERID_TYPE, NativeMethods.MESSAGE_SENDERID_TYPE_NONE);
                }
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.Authenticated"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets a value indicating whether the message was
        ///       authenticated.
        ///    </para>
        /// </devdoc>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MessagingDescription(Res.MsgAuthenticated)]
        public bool Authenticated
        {
            get
            {
                if (!this.filter.Authenticated)
                {
                    //Authentication is undefined, there is nothing to return here.
                    if (!receiveCreated)
                        throw new InvalidOperationException(Res.GetString(Res.AuthenticationNotSet));
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "Authenticated"));
                }
 
                return (properties.GetUI1(NativeMethods.MESSAGE_PROPID_AUTHENTICATED) != 0);
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.AuthenticationProviderName"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets the name of the cryptographic
        ///       provider used to generate the digital signature of the message.
        ///    </para>
        /// </devdoc>
        [ReadOnly(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MessagingDescription(Res.MsgAuthenticationProviderName)]
        public string AuthenticationProviderName
        {
            get
            {
                if (!this.filter.AuthenticationProviderName)
                {
                    //Return default
                    if (!receiveCreated)
                        return "Microsoft Base Cryptographic Provider, Ver. 1.0";
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "AuthenticationProviderName"));
                }
 
                if (this.properties.GetUI4(NativeMethods.MESSAGE_PROPID_PROV_NAME_LEN) != 0)
                    return StringFromBytes(this.properties.GetString(NativeMethods.MESSAGE_PROPID_PROV_NAME),
                                           properties.GetUI4(NativeMethods.MESSAGE_PROPID_PROV_NAME_LEN));
                else
                    return "";
            }
 
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");
 
                //Should not remove if default, the default value might change in future MQ clients
                //if (value.CompareTo("Microsoft Base Cryptographic Provider, Ver. 1.0") == 0) {                    
                //    this.filter.AuthenticationProviderName = false;
                //    properties.Remove(NativeMethods.MESSAGE_PROPID_PROV_NAME);
                //    properties.Remove(NativeMethods.MESSAGE_PROPID_PROV_NAME_LEN);                    
                //}
                //else {
                this.filter.AuthenticationProviderName = true;
                properties.SetString(NativeMethods.MESSAGE_PROPID_PROV_NAME, StringToBytes(value));
                properties.SetUI4(NativeMethods.MESSAGE_PROPID_PROV_NAME_LEN, value.Length);
                //}
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.AuthenticationProviderType"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets the type of cryptographic provider used to
        ///       generate the digital signature of the
        ///       message.
        ///    </para>
        /// </devdoc>
        [ReadOnly(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MessagingDescription(Res.MsgAuthenticationProviderType)]
        public CryptographicProviderType AuthenticationProviderType
        {
            get
            {
                //Return default
                if (!this.filter.AuthenticationProviderType)
                {
                    if (!receiveCreated)
                        return CryptographicProviderType.RsaFull;
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "AuthenticationProviderType"));
                }
 
                return (CryptographicProviderType)properties.GetUI4(NativeMethods.MESSAGE_PROPID_PROV_TYPE);
            }
 
            set
            {
                if (!ValidationUtility.ValidateCryptographicProviderType(value))
                    throw new InvalidEnumArgumentException("value", (int)value, typeof(CryptographicProviderType));
 
                //Should not remove if default, the default value might change in future MQ clients
                //if (value == CryptographicProviderType.RsaFull) {                    
                //    this.filter.AuthenticationProviderType = false;
                //    properties.Remove(NativeMethods.MESSAGE_PROPID_PROV_TYPE);                    
                //}
                //else {                    
                this.filter.AuthenticationProviderType = true;
                properties.SetUI4(NativeMethods.MESSAGE_PROPID_PROV_TYPE, (int)value);
                //}
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.Body"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets
        ///       or sets the serialized
        ///       contents of the message.
        ///    </para>
        /// </devdoc>
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public object Body
        {
            get
            {
                if (!this.filter.Body)
                {
                    if (!receiveCreated)
                        return null;
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "Body"));
                }
 
                if (this.cachedBodyObject == null)
                {
                    if (this.Formatter == null)
                        throw new InvalidOperationException(Res.GetString(Res.FormatterMissing));
 
                    this.cachedBodyObject = this.Formatter.Read(this);
                }
 
                return this.cachedBodyObject;
            }
 
            set
            {
                this.filter.Body = true;
                this.cachedBodyObject = value;
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.BodyStream"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets the information in the body of
        ///       the message.
        ///    </para>
        /// </devdoc>
        [ReadOnly(true),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
        Editor("System.ComponentModel.Design.BinaryEditor, " + AssemblyRef.SystemDesign, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing),
        MessagingDescription(Res.MsgBodyStream)]
        public Stream BodyStream
        {
            get
            {
                if (!this.filter.Body)
                {
                    if (!receiveCreated)
                    {
                        this.filter.Body = true;
                        if (this.cachedBodyStream == null)
                            this.cachedBodyStream = new MemoryStream();
 
                        return this.cachedBodyStream;
                    }
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "Body"));
                }
 
                if (this.cachedBodyStream == null)
                    this.cachedBodyStream = new MemoryStream(properties.GetUI1Vector(NativeMethods.MESSAGE_PROPID_BODY),
                                                                                                0, properties.GetUI4(NativeMethods.MESSAGE_PROPID_BODY_SIZE));
 
                return this.cachedBodyStream;
            }
 
            set
            {
                if (value != null)
                    this.filter.Body = true;
                else
                {
                    this.filter.Body = false;
                    properties.Remove(NativeMethods.MESSAGE_PROPID_BODY);
                    properties.Remove(NativeMethods.MESSAGE_PROPID_BODY_TYPE);
                    properties.Remove(NativeMethods.MESSAGE_PROPID_BODY_SIZE);
                }
 
                this.cachedBodyStream = value;
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.BodyType"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets
        ///       or sets the type of data the message body contains.
        ///    </para>
        /// </devdoc>
        [ReadOnly(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MessagingDescription(Res.MsgBodyType)]
        public int BodyType
        {
            get
            {
                if (!this.filter.Body)
                {
                    //Return default.
                    if (!receiveCreated)
                        return 0;
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "Body"));
                }
 
                return this.properties.GetUI4(NativeMethods.MESSAGE_PROPID_BODY_TYPE);
            }
 
            set
            {
                properties.SetUI4(NativeMethods.MESSAGE_PROPID_BODY_TYPE, value);
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.ConnectorType"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Is required whenever an application sets a message property that is
        ///       typically set by MSMQ. It is typically used in the following two cases.
        ///       Whenever a message is passed by a connector application, the connector
        ///       type is required so that the sending and receiving applications know how
        ///       to interpret the security and acknowledgment properties of the messages.
        ///       When sending application-encrypted messages, this property tells the
        ///       MSMQ run time to use the symmetric key.
        ///    </para>
        /// </devdoc>
        [ReadOnly(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MessagingDescription(Res.MsgConnectorType)]
        public Guid ConnectorType
        {
            get
            {
                if (!this.filter.ConnectorType)
                {
                    //Return default.
                    if (!receiveCreated)
                        return Guid.Empty;
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "ConnectorType"));
                }
 
                return new Guid(this.properties.GetGuid(NativeMethods.MESSAGE_PROPID_CONNECTOR_TYPE));
            }
 
            set
            {
                //If default
                if (value.Equals(Guid.Empty))
                {
                    this.filter.ConnectorType = false;
                    properties.Remove(NativeMethods.MESSAGE_PROPID_CONNECTOR_TYPE);
                }
                else
                {
                    this.filter.ConnectorType = true;
                    properties.SetGuid(NativeMethods.MESSAGE_PROPID_CONNECTOR_TYPE, ((Guid)value).ToByteArray());
                }
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.CorrelationId"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets the message identifier used by
        ///       acknowledgment and report messages to reference the original
        ///       message.
        ///    </para>
        /// </devdoc>
        [ReadOnly(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MessagingDescription(Res.MsgCorrelationId)]
        public string CorrelationId
        {
            get
            {
                if (!this.filter.CorrelationId)
                {
                    //Return default
                    if (!receiveCreated)
                        return String.Empty;
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "CorrelationId"));
                }
 
                return IdFromByteArray(this.properties.GetUI1Vector(NativeMethods.MESSAGE_PROPID_CORRELATIONID));
            }
 
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");
 
                //If default
                if (value.Length == 0)
                {
                    this.filter.CorrelationId = false;
                    properties.Remove(NativeMethods.MESSAGE_PROPID_CORRELATIONID);
                }
                else
                {
                    this.filter.CorrelationId = true;
                    properties.SetUI1Vector(NativeMethods.MESSAGE_PROPID_CORRELATIONID, IdToByteArray(value));
                }
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.DefaultBodySize"]/*' />
        /// <devdoc>
        ///    The default body  buffer size to create,  
        ///    when the message is received.
        /// </devdoc>
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        private int DefaultBodySize
        {
            get
            {
                return this.filter.DefaultBodySize;
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.DefaultExtensionSize"]/*' />
        /// <devdoc>
        ///    The default extension  buffer size to create,  
        ///    when the message is received.
        /// </devdoc>
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        private int DefaultExtensionSize
        {
            get
            {
                return this.filter.DefaultExtensionSize;
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.DefaultLabelSize"]/*' />
        /// <devdoc>
        ///    The default label  buffer size to create,  
        ///    when the message is received.
        /// </devdoc>
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        private int DefaultLabelSize
        {
            get
            {
                return this.filter.DefaultLabelSize;
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.DestinationQueue"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Identifies the original destination queue for a message. It is typically
        ///       used to determine the original destination of a message that is in a journal
        ///       or dead-letter queue, however it can also be used when sending a
        ///       response message back to a response queue.
        ///    </para>
        /// </devdoc>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MessagingDescription(Res.MsgDestinationQueue)]
        public MessageQueue DestinationQueue
        {
            get
            {
                if (!this.filter.DestinationQueue)
                {
                    if (!receiveCreated)
                        throw new InvalidOperationException(Res.GetString(Res.DestinationQueueNotSet));
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "DestinationQueue"));
                }
 
                if (this.cachedDestinationQueue == null)
                {
                    if (this.properties.GetUI4(NativeMethods.MESSAGE_PROPID_DEST_QUEUE_LEN) != 0)
                    {
                        string queueFormatName = StringFromBytes(properties.GetString(NativeMethods.MESSAGE_PROPID_DEST_QUEUE),
                                                                 properties.GetUI4(NativeMethods.MESSAGE_PROPID_DEST_QUEUE_LEN));
                        this.cachedDestinationQueue = new MessageQueue("FORMATNAME:" + queueFormatName);
                    }
                }
 
                return this.cachedDestinationQueue;
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.DestinationSymmetricKey"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets
        ///       or sets the symmetric key used to encrypt messages.
        ///    </para>
        /// </devdoc>
        [ReadOnly(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
        MessagingDescription(Res.MsgDestinationSymmetricKey)]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
        public byte[] DestinationSymmetricKey
        {
            get
            {
                if (!this.filter.DestinationSymmetricKey)
                {
                    if (!receiveCreated)
                        return new byte[0];
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "DestinationSymmetricKey"));
                }
 
                byte[] bytes = new byte[properties.GetUI4(NativeMethods.MESSAGE_PROPID_DEST_SYMM_KEY_LEN)];
                Array.Copy(properties.GetUI1Vector(NativeMethods.MESSAGE_PROPID_DEST_SYMM_KEY), bytes, bytes.Length);
                return bytes;
            }
 
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");
 
                //If default
                if (value.Length == 0)
                {
                    this.filter.DestinationSymmetricKey = false;
                    properties.Remove(NativeMethods.MESSAGE_PROPID_DEST_SYMM_KEY);
                    properties.Remove(NativeMethods.MESSAGE_PROPID_DEST_SYMM_KEY_LEN);
                }
                else
                {
                    this.filter.DestinationSymmetricKey = true;
                    properties.SetUI1Vector(NativeMethods.MESSAGE_PROPID_DEST_SYMM_KEY, value);
                    properties.SetUI4(NativeMethods.MESSAGE_PROPID_DEST_SYMM_KEY_LEN, value.Length);
                }
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.DigitalSignature"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or
        ///       sets the digital signature used to authenticate
        ///       the message.
        ///    </para>
        /// </devdoc>
        [ReadOnly(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MessagingDescription(Res.MsgDigitalSignature)]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
        public byte[] DigitalSignature
        {
            get
            {
                if (!this.filter.DigitalSignature)
                {
                    if (!receiveCreated)
                        return new byte[0];
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "DigitalSignature"));
                }
 
                byte[] bytes = new byte[properties.GetUI4(NativeMethods.MESSAGE_PROPID_SIGNATURE_LEN)];
                Array.Copy(properties.GetUI1Vector(NativeMethods.MESSAGE_PROPID_SIGNATURE), bytes, bytes.Length);
                return bytes;
            }
 
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");
 
                if (value.Length == 0)
                {
                    this.filter.DigitalSignature = false;
                    properties.Remove(NativeMethods.MESSAGE_PROPID_SIGNATURE);
                    properties.Remove(NativeMethods.MESSAGE_PROPID_SIGNATURE_LEN);
                }
                else
                {
                    this.filter.DigitalSignature = true;
                    this.filter.UseAuthentication = true;
 
                    properties.SetUI1Vector(NativeMethods.MESSAGE_PROPID_SIGNATURE, value);
                    properties.SetUI4(NativeMethods.MESSAGE_PROPID_SIGNATURE_LEN, value.Length);
                }
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.EncryptionAlgorithm"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets the encryption algorithm used to encrypt the
        ///       body of a private message.
        ///    </para>
        /// </devdoc>
        [ReadOnly(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MessagingDescription(Res.MsgEncryptionAlgorithm)]
        public EncryptionAlgorithm EncryptionAlgorithm
        {
            get
            {
                if (!this.filter.EncryptionAlgorithm)
                {
                    //Return default.
                    if (!receiveCreated)
                        return EncryptionAlgorithm.Rc2;
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "EncryptionAlgorithm"));
                }
 
                return (EncryptionAlgorithm)properties.GetUI4(NativeMethods.MESSAGE_PROPID_ENCRYPTION_ALG);
            }
            set
            {
                if (!ValidationUtility.ValidateEncryptionAlgorithm(value))
                    throw new InvalidEnumArgumentException("value", (int)value, typeof(EncryptionAlgorithm));
 
                //Should not remove if default, the default value might change in future MQ clients
                //if (value == EncryptionAlgorithm.Rc2) {                
                //    this.filter.EncryptionAlgorithm = false;
                //    properties.Remove(NativeMethods.MESSAGE_PROPID_ENCRYPTION_ALG);                    
                //}
                //else {                    
                this.filter.EncryptionAlgorithm = true;
                properties.SetUI4(NativeMethods.MESSAGE_PROPID_ENCRYPTION_ALG, (int)value);
                //}
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.Extension"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets
        ///       additional information associated with the message.
        ///    </para>
        /// </devdoc>
        [ReadOnly(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
        MessagingDescription(Res.MsgExtension)]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
        public byte[] Extension
        {
            get
            {
                if (!this.filter.Extension)
                {
                    //Return default.
                    if (!receiveCreated)
                        return new byte[0];
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "Extension"));
                }
 
                byte[] bytes = new byte[properties.GetUI4(NativeMethods.MESSAGE_PROPID_EXTENSION_LEN)];
                Array.Copy(properties.GetUI1Vector(NativeMethods.MESSAGE_PROPID_EXTENSION), bytes, bytes.Length);
                return bytes;
            }
 
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");
 
                //If default
                if (value.Length == 0)
                {
                    this.filter.Extension = false;
                    properties.Remove(NativeMethods.MESSAGE_PROPID_EXTENSION);
                    properties.Remove(NativeMethods.MESSAGE_PROPID_EXTENSION_LEN);
                }
                else
                {
                    this.filter.Extension = true;
                    properties.SetUI1Vector(NativeMethods.MESSAGE_PROPID_EXTENSION, value);
                    properties.SetUI4(NativeMethods.MESSAGE_PROPID_EXTENSION_LEN, value.Length);
                }
            }
        }
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.Formatter"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets
        ///       the formatter used to read or write an object into the message
        ///       body.
        ///    </para>
        /// </devdoc>
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public IMessageFormatter Formatter
        {
            get
            {
                return this.cachedFormatter;
            }
 
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");
 
                this.cachedFormatter = value;
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.HashAlgorithm"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets the hashing
        ///       algorithm used when authenticating messages.
        ///    </para>
        /// </devdoc>
        [ReadOnly(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MessagingDescription(Res.MsgHashAlgorithm)]
        public HashAlgorithm HashAlgorithm
        {
            get
            {
                if (!this.filter.HashAlgorithm)
                {
                    //This property has not been set, lets return an empty queue.
                    if (!receiveCreated)
                    {
                        if (LocalAppContextSwitches.UseMD5ForDefaultHashAlgorithm)
                        {
                            return HashAlgorithm.Md5;
                        }
                        return HashAlgorithm.Sha512;
                    }
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "HashAlgorithm"));
                }
 
                return (HashAlgorithm)properties.GetUI4(NativeMethods.MESSAGE_PROPID_HASH_ALG);
            }
 
            set
            {
                if (!ValidationUtility.ValidateHashAlgorithm(value))
                    throw new InvalidEnumArgumentException("value", (int)value, typeof(HashAlgorithm));
 
                //Should not remove if default since MQ3.0 changed the default algorithm
                //if (value == HashAlgorithm.Md5) {                    
                //    this.filter.HashAlgorithm = false;
                //    properties.Remove(NativeMethods.MESSAGE_PROPID_HASH_ALG);                    
                //}
                //else {                    
                this.filter.HashAlgorithm = true;
                properties.SetUI4(NativeMethods.MESSAGE_PROPID_HASH_ALG, (int)value);
                //}
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.Id"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets
        ///       the Message Queuing-generated identifier of the message.
        ///    </para>
        /// </devdoc>
        [ReadOnly(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MessagingDescription(Res.MsgId)]
        public string Id
        {
            get
            {
                if (!this.filter.Id)
                {
                    //The Id is undefined at this point
                    if (!receiveCreated)
                        throw new InvalidOperationException(Res.GetString(Res.IdNotSet));
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "Id"));
                }
 
                return IdFromByteArray(this.properties.GetUI1Vector(NativeMethods.MESSAGE_PROPID_MSGID));
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.IsFirstInTransaction"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets a value indicating
        ///       whether the message was the first message sent in a transaction.
        ///    </para>
        /// </devdoc>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MessagingDescription(Res.MsgIsFirstInTransaction)]
        public bool IsFirstInTransaction
        {
            get
            {
                if (!this.filter.IsFirstInTransaction)
                {
                    if (!receiveCreated)
                        return false;
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "IsFirstInTransaction"));
                }
 
                return (properties.GetUI1(NativeMethods.MESSAGE_PROPID_FIRST_IN_XACT) != 0);
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.IsLastInTransaction"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets a value indicating whether the message was
        ///       the last message sent in a transaction.
        ///    </para>
        /// </devdoc>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MessagingDescription(Res.MsgIsLastInTransaction)]
        public bool IsLastInTransaction
        {
            get
            {
                if (!this.filter.IsLastInTransaction)
                {
                    if (!receiveCreated)
                        return false;
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "IsLastInTransaction"));
                }
 
                return (properties.GetUI1(NativeMethods.MESSAGE_PROPID_LAST_IN_XACT) != 0);
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.Label"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets the message label.
        ///    </para>
        /// </devdoc>
        [ReadOnly(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MessagingDescription(Res.MsgLabel)]
        public string Label
        {
            get
            {
                if (!this.filter.Label)
                {
                    //Return default
                    if (!receiveCreated)
                        return String.Empty;
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "Label"));
                }
 
                if (properties.GetUI4(NativeMethods.MESSAGE_PROPID_LABEL_LEN) != 0)
                    return StringFromBytes(this.properties.GetString(NativeMethods.MESSAGE_PROPID_LABEL),
                                           properties.GetUI4(NativeMethods.MESSAGE_PROPID_LABEL_LEN));
                else
                    return "";
            }
 
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");
 
                //If default
                if (value.Length == 0)
                {
                    this.filter.Label = false;
                    properties.Remove(NativeMethods.MESSAGE_PROPID_LABEL);
                    properties.Remove(NativeMethods.MESSAGE_PROPID_LABEL_LEN);
                }
                else
                {
                    this.filter.Label = true;
                    properties.SetString(NativeMethods.MESSAGE_PROPID_LABEL, StringToBytes(value));
                    properties.SetUI4(NativeMethods.MESSAGE_PROPID_LABEL_LEN, value.Length);
                }
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.LookupId"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets the Message Queue-generated lookup identifier of the message.
        ///    </para>
        /// </devdoc>
        public long LookupId
        {
            get
            {
                if (!MessageQueue.Msmq3OrNewer)
                    throw new PlatformNotSupportedException(Res.GetString(Res.PlatformNotSupported));
 
                if (!this.filter.LookupId)
                {
                    //Return default
                    if (!receiveCreated)
                        throw new InvalidOperationException(Res.GetString(Res.LookupIdNotSet));
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "LookupId"));
                }
 
                return this.properties.GetUI8(NativeMethods.MESSAGE_PROPID_LOOKUPID);
            }
        }
 
        internal void SetLookupId(long value)
        {
            this.filter.LookupId = true;
            this.properties.SetUI8(NativeMethods.MESSAGE_PROPID_LOOKUPID, value);
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.MessageType"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets the type of the message (normal, acknowledgment, or report).
        ///    </para>
        /// </devdoc>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MessagingDescription(Res.MsgMessageType)]
        public MessageType MessageType
        {
            get
            {
                if (!this.filter.MessageType)
                {
                    //Return default
                    if (!receiveCreated)
                        throw new InvalidOperationException(Res.GetString(Res.MessageTypeNotSet));
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "MessageType"));
                }
 
                int cls = properties.GetUI2(NativeMethods.MESSAGE_PROPID_CLASS);
                if (cls == NativeMethods.MESSAGE_CLASS_NORMAL)
                    return MessageType.Normal;
 
                if (cls == NativeMethods.MESSAGE_CLASS_REPORT)
                    return MessageType.Report;
 
                return MessageType.Acknowledgment;
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.Priority"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets the message priority, used to determine
        ///       where the
        ///       message is placed in the
        ///       queue.
        ///    </para>
        /// </devdoc>
        [ReadOnly(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MessagingDescription(Res.MsgPriority)]
        public MessagePriority Priority
        {
            get
            {
                if (!this.filter.Priority)
                {
                    //Return default
                    if (!receiveCreated)
                        return MessagePriority.Normal;
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "Priority"));
                }
 
                return (MessagePriority)properties.GetUI1(NativeMethods.MESSAGE_PROPID_PRIORITY);
            }
 
            set
            {
                if (!ValidationUtility.ValidateMessagePriority(value))
                    throw new InvalidEnumArgumentException("value", (int)value, typeof(MessagePriority));
 
                //If default
                if (value == MessagePriority.Normal)
                {
                    this.filter.Priority = false;
                    properties.Remove(NativeMethods.MESSAGE_PROPID_PRIORITY);
                }
                else
                {
                    this.filter.Priority = true;
                    properties.SetUI1(NativeMethods.MESSAGE_PROPID_PRIORITY, (byte)value);
                }
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.Recoverable"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets a value
        ///       indicating whether the message is guaranteed to be delivered in the event of
        ///       a computer failure or network problem.
        ///    </para>
        /// </devdoc>
        [ReadOnly(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MessagingDescription(Res.MsgRecoverable)]
        public bool Recoverable
        {
            get
            {
                if (!this.filter.Recoverable)
                {
                    //Return default
                    if (!receiveCreated)
                        return false;
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "Recoverable"));
                }
 
                return properties.GetUI1(NativeMethods.MESSAGE_PROPID_DELIVERY) == NativeMethods.MESSAGE_DELIVERY_RECOVERABLE;
            }
 
            set
            {
                //If default
                if (!value)
                {
                    this.filter.Recoverable = false;
                    properties.Remove(NativeMethods.MESSAGE_PROPID_DELIVERY);
                }
                else
                {
                    this.filter.Recoverable = true;
                    properties.SetUI1(NativeMethods.MESSAGE_PROPID_DELIVERY, (byte)NativeMethods.MESSAGE_DELIVERY_RECOVERABLE);
                }
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.ResponseQueue"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets the queue which receives application-generated
        ///       response messages.
        ///    </para>
        /// </devdoc>
        [ReadOnly(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MessagingDescription(Res.MsgResponseQueue)]
        public MessageQueue ResponseQueue
        {
            get
            {
                if (!this.filter.ResponseQueue)
                {
                    //This property has not been set, lets return an undefined value.
                    if (!receiveCreated)
                        return null;
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "ResponseQueue"));
                }
 
                if (this.cachedResponseQueue == null)
                {
                    if (properties.GetUI4(NativeMethods.MESSAGE_PROPID_RESP_QUEUE_LEN) != 0)
                    {
                        string queueFormatName = StringFromBytes(properties.GetString(NativeMethods.MESSAGE_PROPID_RESP_QUEUE),
                                                                 properties.GetUI4(NativeMethods.MESSAGE_PROPID_RESP_QUEUE_LEN));
 
                        this.cachedResponseQueue = new MessageQueue("FORMATNAME:" + queueFormatName);
                    }
                }
 
                return this.cachedResponseQueue;
            }
 
            set
            {
                //If default
                if (value != null)
                    this.filter.ResponseQueue = true;
                else
                {
                    if (this.filter.ResponseQueue)
                    {
                        this.filter.ResponseQueue = false;
                        properties.Remove(NativeMethods.MESSAGE_PROPID_RESP_QUEUE);
                        properties.Remove(NativeMethods.MESSAGE_PROPID_RESP_QUEUE_LEN);
                    }
                }
 
                this.cachedResponseQueue = value;
            }
        }
 
 
        [ReadOnly(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public SecurityContext SecurityContext
        {
            get
            {
                if (!this.filter.SecurityContext)
                    return null;
 
                IntPtr handle = (IntPtr)(int)properties.GetUI4(NativeMethods.MESSAGE_PROPID_SECURITY_CONTEXT);
                return new SecurityContext(new SecurityContextHandle(handle));
            }
 
            set
            {
                if (value == null)
                {
                    this.filter.SecurityContext = false;
                    properties.Remove(NativeMethods.MESSAGE_PROPID_SECURITY_CONTEXT);
                }
                else
                {
                    this.filter.SecurityContext = true;
                    // Can't store IntPtr because property type is UI4, but IntPtr can be 64 bits  
                    int handle = value.Handle.DangerousGetHandle().ToInt32(); // this is safe because MSMQ always returns 32-bit handle
                    properties.SetUI4(NativeMethods.MESSAGE_PROPID_SECURITY_CONTEXT, handle);
                }
 
            }
        }
 
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.SenderCertificate"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Specifies the security certificate used to authenticate messages.
        ///    </para>
        /// </devdoc>
        [ReadOnly(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MessagingDescription(Res.MsgSenderCertificate)]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
        public byte[] SenderCertificate
        {
            get
            {
                if (!this.filter.SenderCertificate)
                {
                    //Return default
                    if (!receiveCreated)
                        return new byte[0];
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "SenderCertificate"));
                }
 
                byte[] bytes = new byte[properties.GetUI4(NativeMethods.MESSAGE_PROPID_SENDER_CERT_LEN)];
                Array.Copy(properties.GetUI1Vector(NativeMethods.MESSAGE_PROPID_SENDER_CERT), bytes, bytes.Length);
                return bytes;
            }
 
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");
 
                //If default
                if (value.Length == 0)
                {
                    this.filter.SenderCertificate = false;
                    properties.Remove(NativeMethods.MESSAGE_PROPID_SENDER_CERT);
                    properties.Remove(NativeMethods.MESSAGE_PROPID_SENDER_CERT_LEN);
                }
                else
                {
                    this.filter.SenderCertificate = true;
                    properties.SetUI1Vector(NativeMethods.MESSAGE_PROPID_SENDER_CERT, value);
                    properties.SetUI4(NativeMethods.MESSAGE_PROPID_SENDER_CERT_LEN, value.Length);
                }
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.SenderId"]/*' />
        /// <devdoc>
        ///    <para>
        ///       This property is set by MSMQ, and is used primarily by the
        ///       receiving Queue Manager when authenticating a message. The receiving
        ///       Queue Manager uses the sender identifier in this property to verify where
        ///       the message originated and to verify the sender has access rights to a queue.
        ///    </para>
        /// </devdoc>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MessagingDescription(Res.MsgSenderId)]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
        public byte[] SenderId
        {
            get
            {
                if (!this.filter.SenderId)
                {
                    if (!receiveCreated)
                        throw new InvalidOperationException(Res.GetString(Res.SenderIdNotSet));
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "SenderId"));
                }
 
                byte[] bytes = new byte[properties.GetUI4(NativeMethods.MESSAGE_PROPID_SENDERID_LEN)];
                Array.Copy(properties.GetUI1Vector(NativeMethods.MESSAGE_PROPID_SENDERID), bytes, bytes.Length);
                return bytes;
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.SenderVersion"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets the version of Message Queuing used to send the message.
        ///    </para>
        /// </devdoc>
        [ReadOnly(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MessagingDescription(Res.MsgSenderVersion)]
        public long SenderVersion
        {
            get
            {
                if (!this.filter.SenderVersion)
                {
                    if (!receiveCreated)
                        throw new InvalidOperationException(Res.GetString(Res.VersionNotSet));
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "SenderVersion"));
                }
 
                return (long)((uint)properties.GetUI4(NativeMethods.MESSAGE_PROPID_VERSION));
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.SentTime"]/*' />
        /// <devdoc>
        ///    Indicates the date and time that the message was sent by
        ///    the source Queue Manager.
        /// </devdoc>
        [ReadOnly(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MessagingDescription(Res.MsgSentTime)]
        public DateTime SentTime
        {
            get
            {
                if (!this.filter.SentTime)
                {
                    if (!receiveCreated)
                        throw new InvalidOperationException(Res.GetString(Res.SentTimeNotSet));
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "SentTime"));
                }
 
                //Number of seconds ellapsed since 1/1/1970
                DateTime time = new DateTime(1970, 1, 1);
                time = time.AddSeconds(properties.GetUI4(NativeMethods.MESSAGE_PROPID_SENTTIME)).ToLocalTime();
                return time;
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.SourceMachine"]/*' />
        /// <devdoc>
        ///    Specifies the computer where the message originated.
        /// </devdoc>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MessagingDescription(Res.MsgSourceMachine)]
        public string SourceMachine
        {
            get
            {
                if (!this.filter.SourceMachine)
                {
                    if (!receiveCreated)
                        throw new InvalidOperationException(Res.GetString(Res.SourceMachineNotSet));
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "SourceMachine"));
                }
 
                if (this.machineName == null)
                {
                    byte[] bytes = this.properties.GetGuid(NativeMethods.MESSAGE_PROPID_SRC_MACHINE_ID);
                    GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
 
                    MachinePropertyVariants machineProperties = new MachinePropertyVariants();
                    machineProperties.SetNull(NativeMethods.MACHINE_PATHNAME);
                    int status = UnsafeNativeMethods.MQGetMachineProperties(null, handle.AddrOfPinnedObject(), machineProperties.Lock());
                    machineProperties.Unlock();
                    handle.Free();
 
                    IntPtr memoryHandle = machineProperties.GetIntPtr(NativeMethods.MACHINE_PATHNAME);
                    if (memoryHandle != (IntPtr)0)
                    {
                        //Using Unicode API even on Win9x
                        this.machineName = Marshal.PtrToStringUni(memoryHandle);
                        SafeNativeMethods.MQFreeMemory(memoryHandle);
                    }
 
                    if (MessageQueue.IsFatalError(status))
                        throw new MessageQueueException(status);
                }
 
                return this.machineName;
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.TimeToBeReceived"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or
        ///       sets the time limit for the message to be retrieved from the target
        ///       queue.
        ///    </para>
        /// </devdoc>
        [ReadOnly(true),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
        TypeConverter(typeof(System.Messaging.Design.TimeoutConverter)),
        MessagingDescription(Res.MsgTimeToBeReceived)]
        public TimeSpan TimeToBeReceived
        {
            get
            {
                if (!this.filter.TimeToBeReceived)
                {
                    //Return default
                    if (!receiveCreated)
                        return InfiniteTimeout;
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "TimeToBeReceived"));
                }
 
                return TimeSpan.FromSeconds((uint)properties.GetUI4(NativeMethods.MESSAGE_PROPID_TIME_TO_BE_RECEIVED));
            }
 
            set
            {
                long timeoutInSeconds = (long)value.TotalSeconds;
                if (timeoutInSeconds < 0)
                    throw new ArgumentException(Res.GetString(Res.InvalidProperty, "TimeToBeReceived", value.ToString()));
 
                if (timeoutInSeconds > UInt32.MaxValue)
                    timeoutInSeconds = UInt32.MaxValue;
 
                //If default
                if (timeoutInSeconds == UInt32.MaxValue)
                {
                    this.filter.TimeToBeReceived = false;
                    properties.Remove(NativeMethods.MESSAGE_PROPID_TIME_TO_BE_RECEIVED);
                }
                else
                {
                    this.filter.TimeToBeReceived = true;
                    properties.SetUI4(NativeMethods.MESSAGE_PROPID_TIME_TO_BE_RECEIVED, (int)((uint)timeoutInSeconds));
                }
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.TimeToReachQueue"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets the time limit for the message to reach
        ///       the queue.
        ///    </para>
        /// </devdoc>
        [ReadOnly(true),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
        TypeConverter(typeof(System.Messaging.Design.TimeoutConverter)),
        MessagingDescription(Res.MsgTimeToReachQueue)]
        public TimeSpan TimeToReachQueue
        {
            get
            {
                if (!this.filter.TimeToReachQueue)
                {
                    //Return default
                    if (!receiveCreated)
                        return InfiniteTimeout;
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "TimeToReachQueue"));
                }
 
                return TimeSpan.FromSeconds((uint)properties.GetUI4(NativeMethods.MESSAGE_PROPID_TIME_TO_REACH_QUEUE));
            }
 
            set
            {
                long timeoutInSeconds = (long)value.TotalSeconds;
                if (timeoutInSeconds < 0)
                    throw new ArgumentException(Res.GetString(Res.InvalidProperty, "TimeToReachQueue", value.ToString()));
 
                if (timeoutInSeconds > UInt32.MaxValue)
                    timeoutInSeconds = UInt32.MaxValue;
 
                if (timeoutInSeconds == UInt32.MaxValue)
                {
                    this.filter.TimeToReachQueue = false;
                    properties.Remove(NativeMethods.MESSAGE_PROPID_TIME_TO_REACH_QUEUE);
                }
                else
                {
                    this.filter.TimeToReachQueue = true;
                    properties.SetUI4(NativeMethods.MESSAGE_PROPID_TIME_TO_REACH_QUEUE, (int)((uint)timeoutInSeconds));
                }
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.TransactionId"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets the
        ///       identifier for the transaction of which the message was a part.
        ///    </para>
        /// </devdoc>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MessagingDescription(Res.MsgTransactionId)]
        public string TransactionId
        {
            get
            {
                if (!this.filter.TransactionId)
                {
                    //Return default
                    if (!receiveCreated)
                        return String.Empty;
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "TransactionId"));
                }
 
                return IdFromByteArray(this.properties.GetUI1Vector(NativeMethods.MESSAGE_PROPID_XACTID));
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.TransactionStatusQueue"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets the
        ///       transaction status queue on the source computer.
        ///    </para>
        /// </devdoc>
        [ReadOnly(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MessagingDescription(Res.MsgTransactionStatusQueue)]
        public MessageQueue TransactionStatusQueue
        {
            get
            {
                if (!this.filter.TransactionStatusQueue)
                {
                    //This property has not been set, lets return an undefined value.
                    if (!receiveCreated)
                        return null;
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "TransactionStatusQueue"));
                }
 
                if (this.cachedTransactionStatusQueue == null)
                {
                    if (this.properties.GetUI4(NativeMethods.MESSAGE_PROPID_XACT_STATUS_QUEUE_LEN) != 0)
                    {
                        string queueFormatName = StringFromBytes(properties.GetString(NativeMethods.MESSAGE_PROPID_XACT_STATUS_QUEUE),
                                                                 properties.GetUI4(NativeMethods.MESSAGE_PROPID_XACT_STATUS_QUEUE_LEN));
 
                        this.cachedTransactionStatusQueue = new MessageQueue("FORMATNAME:" + queueFormatName);
                    }
                }
 
                return this.cachedTransactionStatusQueue;
            }
 
            set
            {
                //If default
                if (value != null)
                    this.filter.TransactionStatusQueue = true;
                else
                {
                    if (this.filter.TransactionStatusQueue)
                    {
                        this.filter.TransactionStatusQueue = false;
                        properties.Remove(NativeMethods.MESSAGE_PROPID_XACT_STATUS_QUEUE);
                        properties.Remove(NativeMethods.MESSAGE_PROPID_XACT_STATUS_QUEUE_LEN);
                    }
                }
 
                this.cachedTransactionStatusQueue = value;
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.UseAuthentication"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets
        ///       or sets a value indicating whether a message must be authenticated.
        ///    </para>
        /// </devdoc>
        [ReadOnly(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MessagingDescription(Res.MsgUseAuthentication)]
        public bool UseAuthentication
        {
            get
            {
                if (!this.filter.UseAuthentication)
                {
                    //Return default
                    if (!receiveCreated)
                    {
 
                        // Actually, we dont know what default is:
                        // Algorithm to determine whether or not messages 
                        // should be authenticated by default is non-trivial 
                        // and should not be reproduced in System.Messaging. 
                        // 
                        // One idea is to add a new native API, 
                        // MQGetDefaultPropertyValue, to retrieve default values.
                        // (Microsoft, Nov 3 2004)
                        return false;
                    }
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "UseAuthentication"));
                }
 
                return (properties.GetUI4(NativeMethods.MESSAGE_PROPID_AUTH_LEVEL) != NativeMethods.MESSAGE_AUTHENTICATION_LEVEL_NONE);
            }
 
            set
            {
                //default is different on different versions of MSMQ, 
                //so dont make any assumptions and explicitly pass what user requested
                this.filter.UseAuthentication = true;
                if (!value)
                {
                    properties.SetUI4(NativeMethods.MESSAGE_PROPID_AUTH_LEVEL, NativeMethods.MESSAGE_AUTHENTICATION_LEVEL_NONE);
                }
                else
                {
                    properties.SetUI4(NativeMethods.MESSAGE_PROPID_AUTH_LEVEL, NativeMethods.MESSAGE_AUTHENTICATION_LEVEL_ALWAYS);
                }
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.UseDeadLetterQueue"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets a value indicating whether
        ///       a copy of an undeliverable message should be sent to a dead-letter queue.
        ///    </para>
        /// </devdoc>
        [ReadOnly(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MessagingDescription(Res.MsgUseDeadLetterQueue)]
        public bool UseDeadLetterQueue
        {
            get
            {
                if (!this.filter.UseDeadLetterQueue)
                {
                    //Return default
                    if (!receiveCreated)
                        return false;
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "UseDeadLetterQueue"));
                }
 
                return ((properties.GetUI1(NativeMethods.MESSAGE_PROPID_JOURNAL) & NativeMethods.MESSAGE_JOURNAL_DEADLETTER) != 0);
            }
 
            set
            {
                //If Default
                if (!value)
                {
                    if (this.filter.UseDeadLetterQueue)
                    {
                        this.filter.UseDeadLetterQueue = false;
                        if (!this.filter.UseJournalQueue)
                            properties.Remove(NativeMethods.MESSAGE_PROPID_JOURNAL);
                        else
                            properties.SetUI1(NativeMethods.MESSAGE_PROPID_JOURNAL, (byte)(properties.GetUI1(NativeMethods.MESSAGE_PROPID_JOURNAL) & ~NativeMethods.MESSAGE_JOURNAL_DEADLETTER));
                    }
                }
                else
                {
                    if (!this.filter.UseDeadLetterQueue && !this.filter.UseJournalQueue)
                        properties.SetUI1(NativeMethods.MESSAGE_PROPID_JOURNAL, (byte)NativeMethods.MESSAGE_JOURNAL_DEADLETTER);
                    else
                        properties.SetUI1(NativeMethods.MESSAGE_PROPID_JOURNAL, (byte)(properties.GetUI1(NativeMethods.MESSAGE_PROPID_JOURNAL) | NativeMethods.MESSAGE_JOURNAL_DEADLETTER));
 
                    this.filter.UseDeadLetterQueue = true;
                }
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.UseEncryption"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets a value indicating whether to encrypt messages.
        ///    </para>
        /// </devdoc>
        [ReadOnly(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MessagingDescription(Res.MsgUseEncryption)]
        public bool UseEncryption
        {
            get
            {
                if (!this.filter.UseEncryption)
                {
                    //Return default
                    if (!receiveCreated)
                        return false;
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "UseEncryption"));
                }
 
                return (properties.GetUI4(NativeMethods.MESSAGE_PROPID_PRIV_LEVEL) != NativeMethods.MESSAGE_PRIVACY_LEVEL_NONE);
            }
 
            set
            {
                //If default
                if (!value)
                {
                    this.filter.UseEncryption = false;
                    properties.Remove(NativeMethods.MESSAGE_PROPID_PRIV_LEVEL);
                }
                else
                {
                    this.filter.UseEncryption = true;
                    properties.SetUI4(NativeMethods.MESSAGE_PROPID_PRIV_LEVEL, NativeMethods.MESSAGE_PRIVACY_LEVEL_BODY);
                }
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.UseJournalQueue"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets a value indicating whether a copy of the message should be kept in a machine
        ///       journal on the originating computer.
        ///    </para>
        /// </devdoc>
        [ReadOnly(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MessagingDescription(Res.MsgUseJournalQueue)]
        public bool UseJournalQueue
        {
            get
            {
                if (!this.filter.UseJournalQueue)
                {
                    //Return default
                    if (!receiveCreated)
                        return false;
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "UseJournalQueue"));
                }
 
                return ((properties.GetUI1(NativeMethods.MESSAGE_PROPID_JOURNAL) & NativeMethods.MESSAGE_JOURNAL_JOURNAL) != 0);
            }
 
            set
            {
                //If Default
                if (!value)
                {
                    if (this.filter.UseJournalQueue)
                    {
                        this.filter.UseJournalQueue = false;
                        if (!this.filter.UseDeadLetterQueue)
                            properties.Remove(NativeMethods.MESSAGE_PROPID_JOURNAL);
                        else
                            properties.SetUI1(NativeMethods.MESSAGE_PROPID_JOURNAL, (byte)(properties.GetUI1(NativeMethods.MESSAGE_PROPID_JOURNAL) & ~NativeMethods.MESSAGE_JOURNAL_JOURNAL));
                    }
                }
                else
                {
                    if (!this.filter.UseDeadLetterQueue && !this.filter.UseJournalQueue)
                        properties.SetUI1(NativeMethods.MESSAGE_PROPID_JOURNAL, (byte)NativeMethods.MESSAGE_JOURNAL_JOURNAL);
                    else
                        properties.SetUI1(NativeMethods.MESSAGE_PROPID_JOURNAL, (byte)(properties.GetUI1(NativeMethods.MESSAGE_PROPID_JOURNAL) | NativeMethods.MESSAGE_JOURNAL_JOURNAL));
 
                    this.filter.UseJournalQueue = true;
                }
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.UseTracing"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or
        ///       sets a value indicating whether to trace a message as
        ///       it moves toward its destination queue.
        ///    </para>
        /// </devdoc>
        [ReadOnly(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MessagingDescription(Res.MsgUseTracing)]
        public bool UseTracing
        {
            get
            {
                if (!this.filter.UseTracing)
                {
                    //Return default
                    if (!receiveCreated)
                        return false;
 
                    throw new InvalidOperationException(Res.GetString(Res.MissingProperty, "UseTracing"));
                }
 
                return (properties.GetUI1(NativeMethods.MESSAGE_PROPID_TRACE) != NativeMethods.MESSAGE_TRACE_NONE);
            }
 
            set
            {
                //If Default
                if (!value)
                {
                    this.filter.UseTracing = false;
                    properties.Remove(NativeMethods.MESSAGE_PROPID_TRACE);
                }
                else
                {
                    this.filter.UseTracing = true;
 
                    if (!value)
                        properties.SetUI1(NativeMethods.MESSAGE_PROPID_TRACE, (byte)NativeMethods.MESSAGE_TRACE_NONE);
                    else
                        properties.SetUI1(NativeMethods.MESSAGE_PROPID_TRACE, (byte)NativeMethods.MESSAGE_TRACE_SEND_ROUTE_TO_REPORT_QUEUE);
                }
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.AdjustMemory"]/*' />
        /// <internalonly/>        
        internal void AdjustMemory()
        {
            if (filter.AdministrationQueue)
            {
                int size = properties.GetUI4(NativeMethods.MESSAGE_PROPID_ADMIN_QUEUE_LEN);
                if (size > Message.DefaultQueueNameSize)
                    properties.SetString(NativeMethods.MESSAGE_PROPID_ADMIN_QUEUE, new byte[size * 2]);
            }
 
            if (filter.Body)
            {
                int size = properties.GetUI4(NativeMethods.MESSAGE_PROPID_BODY_SIZE);
                if (size > DefaultBodySize)
                    properties.SetUI1Vector(NativeMethods.MESSAGE_PROPID_BODY, new byte[size]);
            }
 
            if (filter.AuthenticationProviderName)
            {
                int size = properties.GetUI4(NativeMethods.MESSAGE_PROPID_PROV_NAME_LEN);
                if (size > Message.DefaultCryptographicProviderNameSize)
                    properties.SetString(NativeMethods.MESSAGE_PROPID_PROV_NAME, new byte[size * 2]);
            }
 
            if (filter.DestinationQueue)
            {
                int size = properties.GetUI4(NativeMethods.MESSAGE_PROPID_DEST_QUEUE_LEN);
                if (size > Message.DefaultQueueNameSize)
                    properties.SetString(NativeMethods.MESSAGE_PROPID_DEST_QUEUE, new byte[size * 2]);
            }
 
            if (filter.Extension)
            {
                int size = properties.GetUI4(NativeMethods.MESSAGE_PROPID_EXTENSION_LEN);
                if (size > DefaultExtensionSize)
                    properties.SetUI1Vector(NativeMethods.MESSAGE_PROPID_EXTENSION, new byte[size]);
            }
 
            if (filter.TransactionStatusQueue)
            {
                int size = properties.GetUI4(NativeMethods.MESSAGE_PROPID_XACT_STATUS_QUEUE_LEN);
                if (size > Message.DefaultQueueNameSize)
                    properties.SetString(NativeMethods.MESSAGE_PROPID_XACT_STATUS_QUEUE, new byte[size * 2]);
            }
 
            if (filter.Label)
            {
                int size = properties.GetUI4(NativeMethods.MESSAGE_PROPID_LABEL_LEN);
                if (size > DefaultLabelSize)
                    properties.SetString(NativeMethods.MESSAGE_PROPID_LABEL, new byte[size * 2]);
            }
 
            if (filter.ResponseQueue)
            {
                int size = properties.GetUI4(NativeMethods.MESSAGE_PROPID_RESP_QUEUE_LEN);
                if (size > Message.DefaultQueueNameSize)
                    properties.SetString(NativeMethods.MESSAGE_PROPID_RESP_QUEUE, new byte[size * 2]);
            }
 
            if (filter.SenderCertificate)
            {
                int size = properties.GetUI4(NativeMethods.MESSAGE_PROPID_SENDER_CERT_LEN);
                if (size > Message.DefaultSenderCertificateSize)
                    properties.SetUI1Vector(NativeMethods.MESSAGE_PROPID_SENDER_CERT, new byte[size]);
            }
 
            if (filter.SenderId)
            {
                int size = properties.GetUI4(NativeMethods.MESSAGE_PROPID_SENDERID_LEN);
                if (size > Message.DefaultSenderIdSize)
                    properties.SetUI1Vector(NativeMethods.MESSAGE_PROPID_SENDERID, new byte[size]);
            }
 
            if (filter.DestinationSymmetricKey)
            {
                int size = properties.GetUI4(NativeMethods.MESSAGE_PROPID_DEST_SYMM_KEY_LEN);
                if (size > Message.DefaultSymmetricKeySize)
                    properties.SetUI1Vector(NativeMethods.MESSAGE_PROPID_DEST_SYMM_KEY, new byte[size]);
            }
 
            if (filter.DigitalSignature)
            {
                int size = properties.GetUI4(NativeMethods.MESSAGE_PROPID_SIGNATURE_LEN);
                if (size > Message.DefaultDigitalSignatureSize)
                    properties.SetUI1Vector(NativeMethods.MESSAGE_PROPID_SIGNATURE, new byte[size]);
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.AdjustToSend"]/*' />
        /// <internalonly/>     
        internal void AdjustToSend()
        {
            //Write cached properties
            string queueFormatName;
            if (this.filter.AdministrationQueue && this.cachedAdminQueue != null)
            {
                queueFormatName = this.cachedAdminQueue.FormatName;
                properties.SetString(NativeMethods.MESSAGE_PROPID_ADMIN_QUEUE, StringToBytes(queueFormatName));
                properties.SetUI4(NativeMethods.MESSAGE_PROPID_ADMIN_QUEUE_LEN, queueFormatName.Length);
            }
 
            if (this.filter.ResponseQueue && this.cachedResponseQueue != null)
            {
                queueFormatName = this.cachedResponseQueue.FormatName;
                properties.SetString(NativeMethods.MESSAGE_PROPID_RESP_QUEUE, StringToBytes(queueFormatName));
                properties.SetUI4(NativeMethods.MESSAGE_PROPID_RESP_QUEUE_LEN, queueFormatName.Length);
            }
 
            if (this.filter.TransactionStatusQueue && this.cachedTransactionStatusQueue != null)
            {
                queueFormatName = this.cachedTransactionStatusQueue.FormatName;
                properties.SetString(NativeMethods.MESSAGE_PROPID_XACT_STATUS_QUEUE, StringToBytes(queueFormatName));
                properties.SetUI4(NativeMethods.MESSAGE_PROPID_XACT_STATUS_QUEUE_LEN, queueFormatName.Length);
            }
 
            if (this.filter.Body && this.cachedBodyObject != null)
            {
                if (this.Formatter == null)
                    this.Formatter = new XmlMessageFormatter();
 
                this.Formatter.Write(this, this.cachedBodyObject);
            }
 
            if (this.filter.Body && this.cachedBodyStream != null)
            {
                this.cachedBodyStream.Position = 0;
                byte[] bytes = new byte[(int)this.cachedBodyStream.Length];
                this.cachedBodyStream.Read(bytes, 0, bytes.Length);
                properties.SetUI1Vector(NativeMethods.MESSAGE_PROPID_BODY, bytes);
                properties.SetUI4(NativeMethods.MESSAGE_PROPID_BODY_SIZE, bytes.Length);
            }
 
            if (this.receiveCreated)
            {
                lock (this)
                {
                    if (this.receiveCreated)
                    {
                        //We don't want to send the buffers as they were allocated
                        //when receiving, they might be to big.
                        //Adjust sizes
                        if (this.filter.Body)
                        {
                            int bodySize = properties.GetUI4(NativeMethods.MESSAGE_PROPID_BODY_SIZE);
                            byte[] bodyArray = properties.GetUI1Vector(NativeMethods.MESSAGE_PROPID_BODY);
 
                            Debug.Assert(bodySize <= bodyArray.Length, "Allocated body array size is bigger than BODY_SIZE property");
 
                            if (bodySize < bodyArray.Length)
                            { // need to reallocate body array
 
                                byte[] bytes = new byte[bodySize];
                                Array.Copy(bodyArray, bytes, bodySize);
 
                                properties.SetUI1Vector(NativeMethods.MESSAGE_PROPID_BODY, bytes);
                            }
                        }
                        if (this.filter.Extension)
                        {
                            this.properties.AdjustSize(NativeMethods.MESSAGE_PROPID_EXTENSION,
                                                       this.properties.GetUI4(NativeMethods.MESSAGE_PROPID_EXTENSION_LEN));
                        }
                        if (this.filter.SenderCertificate)
                        {
                            this.properties.AdjustSize(NativeMethods.MESSAGE_PROPID_SENDER_CERT,
                                                       this.properties.GetUI4(NativeMethods.MESSAGE_PROPID_SENDER_CERT_LEN));
                        }
                        if (this.filter.DestinationSymmetricKey)
                        {
                            this.properties.AdjustSize(NativeMethods.MESSAGE_PROPID_DEST_SYMM_KEY,
                                                       this.properties.GetUI4(NativeMethods.MESSAGE_PROPID_DEST_SYMM_KEY_LEN));
                        }
 
                        //Ghost properties.
                        if (this.filter.Acknowledgment || this.filter.MessageType)
                            this.properties.Ghost(NativeMethods.MESSAGE_PROPID_CLASS);
                        if (this.filter.ArrivedTime)
                            this.properties.Ghost(NativeMethods.MESSAGE_PROPID_ARRIVEDTIME);
                        if (this.filter.Authenticated)
                            this.properties.Ghost(NativeMethods.MESSAGE_PROPID_AUTHENTICATED);
                        if (this.filter.DestinationQueue)
                        {
                            this.properties.Ghost(NativeMethods.MESSAGE_PROPID_DEST_QUEUE);
                            this.properties.Ghost(NativeMethods.MESSAGE_PROPID_DEST_QUEUE_LEN);
                            this.cachedDestinationQueue = null;
                        }
                        if (this.filter.IsFirstInTransaction)
                            this.properties.Ghost(NativeMethods.MESSAGE_PROPID_FIRST_IN_XACT);
                        if (this.filter.IsLastInTransaction)
                            this.properties.Ghost(NativeMethods.MESSAGE_PROPID_LAST_IN_XACT);
                        if (this.filter.SenderId)
                        {
                            this.properties.Ghost(NativeMethods.MESSAGE_PROPID_SENDERID);
                            this.properties.Ghost(NativeMethods.MESSAGE_PROPID_SENDERID_LEN);
                        }
                        if (this.filter.SentTime)
                            this.properties.Ghost(NativeMethods.MESSAGE_PROPID_SENTTIME);
                        if (this.filter.SourceMachine)
                            this.properties.Ghost(NativeMethods.MESSAGE_PROPID_SRC_MACHINE_ID);
                        if (this.filter.TransactionId)
                            this.properties.Ghost(NativeMethods.MESSAGE_PROPID_XACTID);
                        if (this.filter.SenderVersion)
                            this.properties.Ghost(NativeMethods.MESSAGE_PROPID_VERSION);
 
                        //Ghost invalid returned properties
 
                        if (this.filter.AdministrationQueue)
                        {
                            if (properties.GetUI4(NativeMethods.MESSAGE_PROPID_ADMIN_QUEUE_LEN) == 0)
                            {
                                this.properties.Ghost(NativeMethods.MESSAGE_PROPID_ADMIN_QUEUE);
                                this.properties.Ghost(NativeMethods.MESSAGE_PROPID_ADMIN_QUEUE_LEN);
                            }
                        }
                        //Encryption algorithm cannot be set if not using Encryption
                        if (this.filter.EncryptionAlgorithm)
                        {
                            if ((this.filter.UseEncryption && !this.UseEncryption) || !this.filter.UseEncryption)
                                this.properties.Ghost(NativeMethods.MESSAGE_PROPID_ENCRYPTION_ALG);
                        }
                        if (this.filter.DigitalSignature)
                        {
                            if (properties.GetUI4(NativeMethods.MESSAGE_PROPID_SIGNATURE_LEN) == 0)
                            {
                                this.properties.Ghost(NativeMethods.MESSAGE_PROPID_SIGNATURE);
                                this.properties.Ghost(NativeMethods.MESSAGE_PROPID_SIGNATURE_LEN);
                            }
                        }
                        if (this.filter.DestinationSymmetricKey)
                        {
                            if (properties.GetUI4(NativeMethods.MESSAGE_PROPID_DEST_SYMM_KEY_LEN) == 0)
                            {
                                this.properties.Ghost(NativeMethods.MESSAGE_PROPID_DEST_SYMM_KEY);
                                this.properties.Ghost(NativeMethods.MESSAGE_PROPID_DEST_SYMM_KEY_LEN);
                            }
                        }
                        if (this.filter.ResponseQueue)
                        {
                            if (properties.GetUI4(NativeMethods.MESSAGE_PROPID_RESP_QUEUE_LEN) == 0)
                            {
                                this.properties.Ghost(NativeMethods.MESSAGE_PROPID_RESP_QUEUE);
                                this.properties.Ghost(NativeMethods.MESSAGE_PROPID_RESP_QUEUE_LEN);
                            }
                        }
                        if (this.filter.TransactionStatusQueue)
                        {
                            if (properties.GetUI4(NativeMethods.MESSAGE_PROPID_XACT_STATUS_QUEUE_LEN) == 0)
                            {
                                this.properties.Ghost(NativeMethods.MESSAGE_PROPID_XACT_STATUS_QUEUE);
                                this.properties.Ghost(NativeMethods.MESSAGE_PROPID_XACT_STATUS_QUEUE_LEN);
                            }
                        }
 
                        this.receiveCreated = false;
                    }
                }
            }
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.IdFromByteArray"]/*' />
        /// <internalonly/>        
        private string IdFromByteArray(byte[] bytes)
        {
            StringBuilder result = new StringBuilder();
            byte[] guidBytes = new byte[GenericIdSize];
            Array.Copy(bytes, guidBytes, GenericIdSize);
            int id = BitConverter.ToInt32(bytes, GenericIdSize);
            result.Append((new Guid(guidBytes)).ToString());
            result.Append("\\");
            result.Append(id);
            return result.ToString();
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.IdToByteArray"]/*' />
        /// <internalonly/>        
        private byte[] IdToByteArray(string id)
        {
            string[] pieces = id.Split(new char[] { '\\' });
            if (pieces.Length != 2)
                throw new InvalidOperationException(Res.GetString(Res.InvalidId));
 
            Guid guid;
            try
            {
                guid = new Guid(pieces[0]);
            }
            catch (FormatException)
            {
                throw new InvalidOperationException(Res.GetString(Res.InvalidId));
            }
 
            int integerId;
            try
            {
                integerId = Convert.ToInt32(pieces[1], CultureInfo.InvariantCulture);
            }
            catch (FormatException)
            {
                throw new InvalidOperationException(Res.GetString(Res.InvalidId));
            }
            catch (OverflowException)
            {
                throw new InvalidOperationException(Res.GetString(Res.InvalidId));
            }
 
            byte[] bytes = new byte[MessageIdSize];
            Array.Copy(guid.ToByteArray(), bytes, GenericIdSize);
            Array.Copy(BitConverter.GetBytes(integerId), 0, bytes, GenericIdSize, 4);
            return bytes;
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.Lock"]/*' />
        /// <internalonly/>        
        internal MessagePropertyVariants.MQPROPS Lock()
        {
            return this.properties.Lock();
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.StringFromBytes"]/*' />
        /// <internalonly/>        
        internal static string StringFromBytes(byte[] bytes, int len)
        {
            //If the string ends with 0, lets trim it.
            if (len != 0 && bytes[len * 2 - 1] == 0 && bytes[len * 2 - 2] == 0)
                --len;
 
            char[] charBuffer = new char[len];
            Encoding.Unicode.GetChars(bytes, 0, len * 2, charBuffer, 0);
            return new String(charBuffer, 0, len);
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.StringToBytes"]/*' />
        /// <internalonly/>        
        internal static byte[] StringToBytes(string value)
        {
            int size = value.Length * 2 + 1;
            byte[] byteBuffer = new byte[size];
            byteBuffer[size - 1] = 0;
            Encoding.Unicode.GetBytes(value.ToCharArray(), 0, value.Length, byteBuffer, 0);
            return byteBuffer;
        }
 
        /// <include file='doc\Message.uex' path='docs/doc[@for="Message.Unlock"]/*' />
        /// <internalonly/>        
        internal void Unlock()
        {
            this.properties.Unlock();
        }
    }
}