File: System\ServiceModel\Channels\NativeMsmqMessage.cs
Project: ndp\cdf\src\WCF\ServiceModel\System.ServiceModel.csproj (System.ServiceModel)
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------
namespace System.ServiceModel.Channels
{
    using System;
    using System.Globalization;
    using System.Text;
    using System.Threading;
    using System.Runtime.InteropServices;
    using System.Security;
    using System.Runtime.Serialization;
    using System.ServiceModel.Diagnostics;
    using Microsoft.Win32.SafeHandles;
 
    abstract class NativeMsmqMessage : IDisposable
    {
        UnsafeNativeMethods.MQPROPVARIANT[] variants;
        UnsafeNativeMethods.MQMSGPROPS nativeProperties;
        int[] ids;
        GCHandle nativePropertiesHandle;
        GCHandle variantsHandle;
        GCHandle idsHandle;
        MsmqProperty[] properties;
        bool disposed;
        object[] buffersForAsync;
 
        protected NativeMsmqMessage(int propertyCount)
        {
            this.properties = new MsmqProperty[propertyCount];
            this.nativeProperties = new UnsafeNativeMethods.MQMSGPROPS();
            this.ids = new int[propertyCount];
            this.variants = new UnsafeNativeMethods.MQPROPVARIANT[propertyCount];
 
            this.nativePropertiesHandle = GCHandle.Alloc(null, GCHandleType.Pinned);
            this.idsHandle = GCHandle.Alloc(null, GCHandleType.Pinned);
            this.variantsHandle = GCHandle.Alloc(null, GCHandleType.Pinned);
        }
 
        ~NativeMsmqMessage()
        {
            Dispose(false);
        }
 
        public virtual void GrowBuffers()
        {
        }
 
        public object[] GetBuffersForAsync()
        {
            if (null == this.buffersForAsync)
            {
                int propertyBuffersToPin = 0;
                for (int i = 0; i < this.nativeProperties.count; ++i)
                {
                    if (this.properties[i].MaintainsBuffer)
                        ++propertyBuffersToPin;
                }
                this.buffersForAsync = new object[propertyBuffersToPin + 3];
            }
 
            int bufferCount = 0;
            for (int i = 0; i < this.nativeProperties.count; ++i)
            {
                if (this.properties[i].MaintainsBuffer)
                {
                    this.buffersForAsync[bufferCount++] = this.properties[i].MaintainedBuffer;
                }
            }
            this.buffersForAsync[bufferCount++] = this.ids;
            this.buffersForAsync[bufferCount++] = this.variants;
            this.buffersForAsync[bufferCount] = this.nativeProperties;
 
            return this.buffersForAsync;
        }
 
        public IntPtr Pin()
        {
            for (int i = 0; i < this.nativeProperties.count; i++)
                properties[i].Pin();
 
            this.idsHandle.Target = this.ids;
            this.variantsHandle.Target = this.variants;
 
            this.nativeProperties.status = IntPtr.Zero;
            this.nativeProperties.variants = this.variantsHandle.AddrOfPinnedObject();
            this.nativeProperties.ids = this.idsHandle.AddrOfPinnedObject();
            this.nativePropertiesHandle.Target = this.nativeProperties;
 
            return nativePropertiesHandle.AddrOfPinnedObject();
        }
 
        public void Unpin()
        {
            this.nativePropertiesHandle.Target = null;
            this.idsHandle.Target = null;
            this.variantsHandle.Target = null;
 
            for (int i = 0; i < this.nativeProperties.count; i++)
                properties[i].Unpin();
        }
 
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
 
        void Dispose(bool disposing)
        {
            if (!this.disposed && disposing)
            {
                for (int i = 0; i < this.nativeProperties.count; i++)
                {
                    this.properties[i].Dispose();
                }
 
                this.disposed = true;
            }
            if (this.nativePropertiesHandle.IsAllocated)
                this.nativePropertiesHandle.Free();
            if (this.idsHandle.IsAllocated)
                this.idsHandle.Free();
            if (this.variantsHandle.IsAllocated)
                this.variantsHandle.Free();
        }
 
        public abstract class MsmqProperty : IDisposable
        {
            UnsafeNativeMethods.MQPROPVARIANT[] variants;
            int index;
 
            protected MsmqProperty(NativeMsmqMessage message, int id, ushort vt)
            {
                this.variants = message.variants;
                this.index = message.nativeProperties.count++;
                message.variants[this.index].vt = vt;
                message.ids[this.index] = id;
                message.properties[this.index] = this;
            }
 
            protected int Index
            {
                get { return this.index; }
            }
 
            public virtual bool MaintainsBuffer
            {
                get { return false; }
            }
 
            public virtual object MaintainedBuffer
            {
                get { return null; }
            }
 
            public virtual void Pin()
            {
            }
 
            public virtual void Unpin()
            {
            }
 
            public virtual void Dispose()
            {
            }
 
            protected UnsafeNativeMethods.MQPROPVARIANT[] Variants
            {
                get { return this.variants; }
            }
        }
 
        public class ByteProperty : MsmqProperty
        {
            public ByteProperty(NativeMsmqMessage message, int id)
                : base(message, id, UnsafeNativeMethods.VT_UI1)
            {
            }
 
            public ByteProperty(NativeMsmqMessage message, int id, byte value)
                : this(message, id)
            {
                this.Value = value;
            }
 
            public byte Value
            {
                get
                {
                    return this.Variants[this.Index].byteValue;
                }
                set
                {
                    this.Variants[this.Index].byteValue = value;
                }
            }
        }
 
        public class ShortProperty : MsmqProperty
        {
            public ShortProperty(NativeMsmqMessage message, int id)
                : base(message, id, UnsafeNativeMethods.VT_UI2)
            {
            }
 
            public ShortProperty(NativeMsmqMessage message, int id, short value)
                : this(message, id)
            {
                this.Value = value;
            }
 
            public short Value
            {
                get
                {
                    return this.Variants[this.Index].shortValue;
                }
                set
                {
                    this.Variants[this.Index].shortValue = value;
                }
            }
        }
 
 
        public class BooleanProperty : MsmqProperty
        {
            public BooleanProperty(NativeMsmqMessage message, int id)
                : base(message, id, UnsafeNativeMethods.VT_BOOL)
            {
            }
 
            public BooleanProperty(NativeMsmqMessage message, int id, bool value)
                : this(message, id)
            {
                this.Value = value;
            }
 
            public bool Value
            {
                get
                {
                    return this.Variants[this.Index].shortValue != 0;
                }
                set
                {
                    this.Variants[this.Index].shortValue = value ? (short)-1 : (short)0;
                }
            }
        }
 
        public class IntProperty : MsmqProperty
        {
            public IntProperty(NativeMsmqMessage message, int id)
                : base(message, id, UnsafeNativeMethods.VT_UI4)
            {
            }
 
            public IntProperty(NativeMsmqMessage message, int id, int value)
                : this(message, id)
            {
                this.Value = value;
            }
 
            public int Value
            {
                get
                {
                    return this.Variants[this.Index].intValue;
                }
                set
                {
                    this.Variants[this.Index].intValue = value;
                }
            }
        }
 
        public class LongProperty : MsmqProperty
        {
            public LongProperty(NativeMsmqMessage message, int id)
                : base(message, id, UnsafeNativeMethods.VT_UI8)
            {
            }
 
            public LongProperty(NativeMsmqMessage message, int id, long value)
                : this(message, id)
            {
                this.Value = value;
            }
 
            public long Value
            {
                get
                {
                    return this.Variants[this.Index].longValue;
                }
                set
                {
                    this.Variants[this.Index].longValue = value;
                }
            }
        }
 
        public class BufferProperty : MsmqProperty
        {
            byte[] buffer;
            GCHandle bufferHandle;
 
            public BufferProperty(NativeMsmqMessage message, int id)
                : base(message, id, UnsafeNativeMethods.VT_UI1 | UnsafeNativeMethods.VT_VECTOR)
            {
                bufferHandle = GCHandle.Alloc(null, GCHandleType.Pinned);
            }
 
            public BufferProperty(NativeMsmqMessage message, int id, byte[] buffer)
                : this(message, id, buffer.Length)
            {
                System.Buffer.BlockCopy(buffer, 0, this.Buffer, 0, buffer.Length);
            }
 
            public BufferProperty(NativeMsmqMessage message, int id, int length)
                : this(message, id)
            {
                SetBufferReference(DiagnosticUtility.Utility.AllocateByteArray(length));
            }
 
            ~BufferProperty()
            {
                Dispose(false);
            }
 
            public override void Dispose()
            {
                base.Dispose();
                this.Dispose(true);
                GC.SuppressFinalize(this);
            }
 
            void Dispose(bool disposing)
            {
                if (bufferHandle.IsAllocated)
                    bufferHandle.Free();
            }
 
            public void SetBufferReference(byte[] buffer)
            {
                SetBufferReference(buffer, buffer.Length);
            }
 
            public void SetBufferReference(byte[] buffer, int length)
            {
                this.buffer = buffer;
                this.BufferLength = length;
            }
 
            public override bool MaintainsBuffer
            {
                get { return true; }
            }
 
            public override object MaintainedBuffer
            {
                get { return this.buffer; }
            }
 
            public override void Pin()
            {
                bufferHandle.Target = buffer;
                this.Variants[this.Index].byteArrayValue.intPtr = bufferHandle.AddrOfPinnedObject();
            }
 
            public override void Unpin()
            {
                this.Variants[this.Index].byteArrayValue.intPtr = IntPtr.Zero;
                bufferHandle.Target = null;
            }
 
            public byte[] GetBufferCopy(int length)
            {
                byte[] buffer = DiagnosticUtility.Utility.AllocateByteArray(length);
                System.Buffer.BlockCopy(this.buffer, 0, buffer, 0, length);
                return buffer;
            }
 
            public void EnsureBufferLength(int length)
            {
                if (this.buffer.Length < length)
                {
                    SetBufferReference(DiagnosticUtility.Utility.AllocateByteArray(length));
                }
            }
 
            public int BufferLength
            {
                get
                {
                    return this.Variants[this.Index].byteArrayValue.size;
                }
                set
                {
                    if (value > this.buffer.Length)
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value"));
                    this.Variants[this.Index].byteArrayValue.size = value;
                }
            }
 
            public byte[] Buffer
            {
                get
                {
                    return this.buffer;
                }
            }
        }
 
        public class StringProperty : MsmqProperty
        {
            char[] buffer;
            GCHandle bufferHandle;
 
            internal StringProperty(NativeMsmqMessage message, int id, string value)
                : this(message, id, value.Length + 1)
            {
                CopyValueToBuffer(value);
            }
 
            internal StringProperty(NativeMsmqMessage message, int id, int length)
                : base(message, id, UnsafeNativeMethods.VT_LPWSTR)
            {
                this.buffer = DiagnosticUtility.Utility.AllocateCharArray(length);
                this.bufferHandle = GCHandle.Alloc(null, GCHandleType.Pinned);
            }
 
            ~StringProperty()
            {
                Dispose(false);
            }
 
            public override bool MaintainsBuffer
            {
                get { return true; }
            }
 
            public override object MaintainedBuffer
            {
                get { return this.buffer; }
            }
 
            public override void Pin()
            {
                this.bufferHandle.Target = buffer;
                this.Variants[this.Index].intPtr = bufferHandle.AddrOfPinnedObject();
            }
 
            public override void Unpin()
            {
                this.Variants[this.Index].intPtr = IntPtr.Zero;
                this.bufferHandle.Target = null;
            }
 
            public override void Dispose()
            {
                base.Dispose();
                Dispose(true);
                GC.SuppressFinalize(this);
            }
 
            void Dispose(bool disposing)
            {
                if (bufferHandle.IsAllocated)
                    bufferHandle.Free();
            }
 
            public void EnsureValueLength(int length)
            {
                if (length > this.buffer.Length)
                {
                    this.buffer = DiagnosticUtility.Utility.AllocateCharArray(length);
                }
            }
 
            public void SetValue(string value)
            {
                if (null == value)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value");
                EnsureValueLength(value.Length + 1);
                CopyValueToBuffer(value);
            }
 
            void CopyValueToBuffer(string value)
            {
                value.CopyTo(0, this.buffer, 0, value.Length);
                this.buffer[value.Length] = '\0';
            }
 
            public string GetValue(int length)
            {
                if (length == 0)
                {
                    return null;
                }
                else
                {
                    return new string(this.buffer, 0, length - 1);
                }
            }
        }
    }
 
    static class MsmqMessageId
    {
        const int guidSize = 16;
 
        public static string ToString(byte[] messageId)
        {
            StringBuilder result = new StringBuilder();
            byte[] guid = new byte[guidSize];
            Array.Copy(messageId, guid, guidSize);
            int id = BitConverter.ToInt32(messageId, guidSize);
            result.Append((new Guid(guid)).ToString());
            result.Append("\\");
            result.Append(id);
            return result.ToString();
        }
 
        public static byte[] FromString(string messageId)
        {
            string[] pieces = messageId.Split(new char[] { '\\' });
            if (pieces.Length != 2)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.MsmqInvalidMessageId, messageId), "messageId"));
 
            Guid guid;
            if (!DiagnosticUtility.Utility.TryCreateGuid(pieces[0], out guid))
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.MsmqInvalidMessageId, messageId), "messageId"));
            }
 
            int integerId;
            try
            {
                integerId = Convert.ToInt32(pieces[1], CultureInfo.InvariantCulture);
            }
            catch (FormatException)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.MsmqInvalidMessageId, messageId), "messageId"));
            }
 
            byte[] bytes = new byte[UnsafeNativeMethods.PROPID_M_MSGID_SIZE];
            Array.Copy(guid.ToByteArray(), bytes, guidSize);
            Array.Copy(BitConverter.GetBytes(integerId), 0, bytes, guidSize, 4);
            return bytes;
        }
    }
 
    class MsmqEmptyMessage : NativeMsmqMessage
    {
        public MsmqEmptyMessage()
            : base(0)
        { }
    }
 
    static class MsmqDuration
    {
        public static int FromTimeSpan(TimeSpan timeSpan)
        {
            long totalSeconds = (long)timeSpan.TotalSeconds;
            if (totalSeconds > int.MaxValue)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
                                                                              SR.GetString(SR.MsmqTimeSpanTooLarge)));
            return (int)totalSeconds;
        }
 
        public static TimeSpan ToTimeSpan(int seconds)
        {
            return TimeSpan.FromSeconds(seconds);
        }
    }
 
    static class MsmqDateTime
    {
        public static DateTime ToDateTime(int seconds)
        {
            return new DateTime(1970, 1, 1).AddSeconds(seconds);
        }
    }
}