//------------------------------------------------------------ // 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); } } }