Support serializing/deserializing NMS properties on the wire.
Fix several resource leaks that were causing lock-ups when disposing of connection objects. Many more unit tests can now be run. Some unit tests will never succeed due to the nature of the ZMQ implementation not having a central broker.
Fixes [AMQNET-491]. (See https://issues.apache.org/jira/browse/AMQNET-491)

diff --git a/nmsprovider-test.config b/nmsprovider-test.config
index b9f4114..2ed149b 100644
--- a/nmsprovider-test.config
+++ b/nmsprovider-test.config
@@ -16,7 +16,7 @@
 * limitations under the License.
 -->
 <configuration>
-	<defaultURI value="zmq://localhost">
+	<defaultURI value="zmq:tcp://localhost:5556">
 		<factoryParams>
 			<param type="string" value="NMSTestClient"/>
 		</factoryParams>
diff --git a/src/main/csharp/Connection.cs b/src/main/csharp/Connection.cs
index 4def1c5..5cb620c 100644
--- a/src/main/csharp/Connection.cs
+++ b/src/main/csharp/Connection.cs
@@ -73,6 +73,16 @@
 			{

 				if(0 == --instanceCount)

 				{

+					lock(producerCacheLock)

+					{

+						foreach(KeyValuePair<string, ProducerRef> cacheItem in producerCache)

+						{

+							cacheItem.Value.producer.Unbind(cacheItem.Key);

+						}

+

+						producerCache.Clear();

+					}

+

 					Connection._context.Dispose();

 				}

 			}

@@ -248,16 +258,6 @@
         public void Close()

         {

             Stop();

-

-			lock(producerCacheLock)

-			{

-				foreach(KeyValuePair<string, ProducerRef> cacheItem in producerCache)

-				{

-					cacheItem.Value.producer.Unbind(cacheItem.Key);

-				}

-

-				producerCache.Clear();

-			}

 		}

 

         public void PurgeTempDestinations()

diff --git a/src/main/csharp/Destination.cs b/src/main/csharp/Destination.cs
index 2fb045f..5f0cecd 100644
--- a/src/main/csharp/Destination.cs
+++ b/src/main/csharp/Destination.cs
@@ -16,9 +16,9 @@
  */

 

 using System;

+using System.Diagnostics;

 using System.Text;

 using ZeroMQ;

-using System.Diagnostics;

 

 namespace Apache.NMS.ZMQ

 {

@@ -47,6 +47,7 @@
 		{

 			this.session = session;

 			this.destinationName = destName;

+			this.session.RegisterDestination(this);

 		}

 

 		~Destination()

@@ -103,6 +104,8 @@
 				this.session.Connection.ReleaseConsumer(this.consumerEndpoint);

 				this.consumerEndpoint = null;

 			}

+

+			this.session.UnregisterDestination(this);

 		}

 

 		public string Name

diff --git a/src/main/csharp/MessageConsumer.cs b/src/main/csharp/MessageConsumer.cs
index 486c992..ea502e5 100644
--- a/src/main/csharp/MessageConsumer.cs
+++ b/src/main/csharp/MessageConsumer.cs
@@ -19,6 +19,8 @@
 using System.Diagnostics;

 using System.Text;

 using System.Threading;

+using System.Net;

+using Apache.NMS.Util;

 

 namespace Apache.NMS.ZMQ

 {

@@ -121,10 +123,15 @@
 			if(size > 0)

 			{

 				// Strip off the subscribed destination name.

-				// TODO: Support decoding of all message types + all meta data (e.g., headers and properties)

-				int msgStart = this.rawDestinationName.Length;

-				int msgLength = receivedMsg.Length - msgStart;

-				string msgContent = Encoding.UTF8.GetString(receivedMsg, msgStart, msgLength);

+				int receivedMsgIndex = this.rawDestinationName.Length;

+				int msgLength = receivedMsg.Length - receivedMsgIndex;

+				byte[] msgContent = new byte[msgLength];

+

+				for(int index = 0; index < msgLength; index++, receivedMsgIndex++)

+				{

+					msgContent[index] = receivedMsg[receivedMsgIndex];

+				}

+

 				return ToNmsMessage(msgContent);

 			}

 

@@ -203,6 +210,7 @@
 			Tracer.InfoFormat("Starting dispatcher thread consumer: {0}", this.asyncDeliveryThread.Name);

 			TimeSpan receiveWait = TimeSpan.FromSeconds(2);

 

+			this.destination.InitReceiver();

 			// Signal that this thread has started.

 			asyncInit = true;

 

@@ -227,7 +235,7 @@
 						}

 						else

 						{

-							Thread.Sleep(0);

+							Thread.Sleep(1);

 						}

 					}

 				}

@@ -258,37 +266,219 @@
 		/// <returns>

 		/// nms message object

 		/// </returns>

-		protected virtual IMessage ToNmsMessage(string messageText)

+		protected virtual IMessage ToNmsMessage(byte[] msgData)

 		{

-			// Strip off the destination name prefix.

-			IMessage nmsMessage = new TextMessage(messageText);

+			IMessage nmsMessage = null;

+			int messageType = WireFormat.MT_UNKNOWN;

+			int fieldType = WireFormat.MFT_NONE;

+			DateTime messageTimestamp = DateTime.UtcNow;

+			string messageNMSType = null;

+			string messageCorrelationId = null;

+			IDestination messageReplyTo = null;

+			MsgDeliveryMode messageDeliveryMode = MsgDeliveryMode.NonPersistent;

+			MsgPriority messagePriority = MsgPriority.Normal;

+			TimeSpan messageTimeToLive = TimeSpan.FromTicks(0);

+			IPrimitiveMap messageProperties = null;

+			int fieldLen;

+			int index = 0;

+			string messageID = string.Empty;

+			byte[] messageBody = null;

 

 			try

 			{

-				nmsMessage.NMSMessageId = "";

-				nmsMessage.NMSDestination = this.destination;

-				nmsMessage.NMSDeliveryMode = MsgDeliveryMode.NonPersistent;

-				nmsMessage.NMSPriority = MsgPriority.Normal;

-				nmsMessage.NMSTimestamp = DateTime.Now;

-				nmsMessage.NMSTimeToLive = new TimeSpan(0);

-				nmsMessage.NMSType = "";

-			}

-			catch(InvalidOperationException)

-			{

-				// Log error

-			}

-

-			if(null != this.ConsumerTransformer)

-			{

-				IMessage transformedMessage = ConsumerTransformer(this.session, this, nmsMessage);

-

-				if(null != transformedMessage)

+				// Parse the commond message fields

+				do

 				{

-					nmsMessage = transformedMessage;

+					fieldType = ReadInt(msgData, ref index);

+					switch(fieldType)

+					{

+					case WireFormat.MFT_NONE:

+						break;

+

+					case WireFormat.MFT_MESSAGEID:

+						messageID = ReadString(msgData, ref index);

+						break;

+

+					case WireFormat.MFT_TIMESTAMP:

+						fieldLen = ReadInt(msgData, ref index);

+						Debug.Assert(sizeof(long) == fieldLen);

+						messageTimestamp = DateTime.FromBinary(ReadLong(msgData, ref index));

+						break;

+

+					case WireFormat.MFT_NMSTYPE:

+						messageNMSType = ReadString(msgData, ref index);

+						break;

+

+					case WireFormat.MFT_CORRELATIONID:

+						messageCorrelationId = ReadString(msgData, ref index);

+						break;

+

+					case WireFormat.MFT_REPLYTO:

+						string replyToDestName = ReadString(msgData, ref index);

+						messageReplyTo = this.session.GetDestination(replyToDestName);

+						break;

+

+					case WireFormat.MFT_DELIVERYMODE:

+						fieldLen = ReadInt(msgData, ref index);

+						Debug.Assert(sizeof(int) == fieldLen);

+						messageDeliveryMode = (MsgDeliveryMode) ReadInt(msgData, ref index);

+						break;

+

+					case WireFormat.MFT_PRIORITY:

+						fieldLen = ReadInt(msgData, ref index);

+						Debug.Assert(sizeof(int) == fieldLen);

+						messagePriority = (MsgPriority) ReadInt(msgData, ref index);

+						break;

+

+					case WireFormat.MFT_TIMETOLIVE:

+						fieldLen = ReadInt(msgData, ref index);

+						Debug.Assert(sizeof(long) == fieldLen);

+						messageTimeToLive = TimeSpan.FromTicks(ReadLong(msgData, ref index));

+						break;

+

+					case WireFormat.MFT_HEADERS:

+						fieldLen = ReadInt(msgData, ref index);

+						int numProperties = ReadInt(msgData, ref index);

+						if(numProperties > 0)

+						{

+							messageProperties = new PrimitiveMap();

+							while(numProperties-- > 0)

+							{

+								string propertyKey = ReadString(msgData, ref index);

+								byte[] propertyVal = ReadBytes(msgData, ref index);

+								messageProperties.SetBytes(propertyKey, propertyVal);

+							}

+						}

+						break;

+

+					case WireFormat.MFT_MSGTYPE:

+						fieldLen = ReadInt(msgData, ref index);

+						Debug.Assert(sizeof(int) == fieldLen);

+						messageType = ReadInt(msgData, ref index);

+						break;

+

+					case WireFormat.MFT_BODY:

+						messageBody = ReadBytes(msgData, ref index);

+						break;

+

+					default:

+						// Skip past this field.

+						Tracer.WarnFormat("Unknown message field type: {0}", fieldType);

+						fieldLen = ReadInt(msgData, ref index);

+						index += fieldLen;

+						break;

+					}

+				} while(WireFormat.MFT_NONE != fieldType && index < msgData.Length);

+			}

+			catch(Exception ex)

+			{

+				Tracer.ErrorFormat("Exception parsing message: {0}", ex.Message);

+			}

+

+			// Instantiate the message type

+			switch(messageType)

+			{

+			case WireFormat.MT_MESSAGE:

+				nmsMessage = new BaseMessage();

+				break;

+

+			case WireFormat.MT_TEXTMESSAGE:

+				nmsMessage = new TextMessage();

+				if(null != messageBody)

+				{

+					((TextMessage) nmsMessage).Text = Encoding.UTF8.GetString(messageBody);

+				}

+				break;

+

+			case WireFormat.MT_UNKNOWN:

+			default:

+				break;

+			}

+

+			// Set the common headers.

+			if(null != nmsMessage)

+			{

+				try

+				{

+					nmsMessage.NMSMessageId = messageID;

+					nmsMessage.NMSCorrelationID = messageCorrelationId;

+					nmsMessage.NMSDestination = this.destination;

+					nmsMessage.NMSReplyTo = messageReplyTo;

+					nmsMessage.NMSDeliveryMode = messageDeliveryMode;

+					nmsMessage.NMSPriority = messagePriority;

+					nmsMessage.NMSTimestamp = messageTimestamp;

+					nmsMessage.NMSTimeToLive = messageTimeToLive;

+					nmsMessage.NMSType = messageNMSType;

+					if(null != messageProperties)

+					{

+						foreach(string propertyKey in messageProperties.Keys)

+						{

+							nmsMessage.Properties.SetBytes(propertyKey, messageProperties.GetBytes(propertyKey));

+						}

+					}

+				}

+				catch(InvalidOperationException)

+				{

+					// Log error

+				}

+

+				if(null != this.ConsumerTransformer)

+				{

+					IMessage transformedMessage = ConsumerTransformer(this.session, this, nmsMessage);

+

+					if(null != transformedMessage)

+					{

+						nmsMessage = transformedMessage;

+					}

 				}

 			}

 

 			return nmsMessage;

 		}

+

+		private long ReadLong(byte[] msgData, ref int index)

+		{

+			long val = BitConverter.ToInt64(msgData, index);

+			index += sizeof(long);

+			return IPAddress.NetworkToHostOrder(val);

+		}

+

+		private int ReadInt(byte[] msgData, ref int index)

+		{

+			int val = BitConverter.ToInt32(msgData, index);

+			index += sizeof(int);

+			return IPAddress.NetworkToHostOrder(val);

+		}

+

+		private string ReadString(byte[] msgData, ref int index)

+		{

+			int stringLen = ReadInt(msgData, ref index);

+			string stringVal = string.Empty;

+

+			if(stringLen > 0)

+			{

+				stringVal = Encoding.UTF8.GetString(msgData, index, stringLen);

+				index += stringLen;

+			}

+

+			return stringVal;

+		}

+

+		private byte[] ReadBytes(byte[] msgData, ref int index)

+		{

+			int bytesLen = ReadInt(msgData, ref index);

+			byte[] bytesVal = null;

+

+			if(bytesLen >= 0)

+			{

+				bytesVal = new byte[bytesLen];

+				for(int byteIndex = 0; byteIndex < bytesLen; byteIndex++, index++)

+				{

+					bytesVal[byteIndex] = msgData[index];

+				}

+			}

+

+			return bytesVal;

+		}

 	}

 }

diff --git a/src/main/csharp/MessageProducer.cs b/src/main/csharp/MessageProducer.cs
index d1b3b18..2ea49c2 100644
--- a/src/main/csharp/MessageProducer.cs
+++ b/src/main/csharp/MessageProducer.cs
@@ -18,9 +18,9 @@
 #define PUBSUB

 

 using System;

+using System.Collections.Generic;

 using System.Text;

-using ZeroMQ;

-

+using System.Net;

 

 namespace Apache.NMS.ZMQ

 {

@@ -59,24 +59,30 @@
 

 		public void Send(IMessage message)

 		{

-			Send(this.destination, message);

-		}

-

-		public void Send(IMessage message, MsgDeliveryMode deliveryMode, MsgPriority priority, TimeSpan timeToLive)

-		{

-			Send(this.destination, message, deliveryMode, priority, timeToLive);

+			Send(this.destination, message, this.deliveryMode, this.priority, this.timeToLive, false);

 		}

 

 		public void Send(IDestination dest, IMessage message)

 		{

-			Send(dest, message, this.DeliveryMode, this.Priority, this.TimeToLive);

+			Send(dest, message, this.deliveryMode, this.priority, this.timeToLive, false);

+		}

+

+		public void Send(IMessage message, MsgDeliveryMode deliveryMode, MsgPriority priority, TimeSpan timeToLive)

+		{

+			Send(this.destination, message, deliveryMode, priority, timeToLive, true);

 		}

 

 		public void Send(IDestination dest, IMessage message, MsgDeliveryMode deliveryMode, MsgPriority priority, TimeSpan timeToLive)

 		{

-			// UNUSED_PARAM(deliveryMode);	// No concept of different delivery modes in ZMQ

-			// UNUSED_PARAM(priority);		// No concept of priority messages in ZMQ

-			// UNUSED_PARAM(timeToLive);	// No concept of time-to-live in ZMQ

+			Send(destination, message, deliveryMode, priority, timeToLive, true);

+		}

+

+		public void Send(IDestination dest, IMessage message, MsgDeliveryMode deliveryMode, MsgPriority priority, TimeSpan timeToLive, bool specifiedTimeToLive)

+		{

+			if(null == dest)

+			{

+				return;

+			}

 

 			if(null != this.ProducerTransformer)

 			{

@@ -88,14 +94,145 @@
 				}

 			}

 

-			// TODO: Support encoding of all message types + all meta data (e.g., headers and properties)

+			// Serialize the message data

+			Destination theDest = (Destination) dest;

+			List<byte> msgDataBuilder = new List<byte>();

+

+			// Always set the message Id.

+			message.NMSMessageId = Guid.NewGuid().ToString();

+			message.NMSTimestamp = DateTime.UtcNow;

+			if(specifiedTimeToLive)

+			{

+				message.NMSTimeToLive = timeToLive;

+			}

 

 			// Prefix the message with the destination name. The client will subscribe to this destination name

 			// in order to receive messages.

-			Destination theDest = (Destination) dest;

+			msgDataBuilder.AddRange(Encoding.UTF8.GetBytes(theDest.Name));

 

-			string msg = theDest.Name + ((ITextMessage) message).Text;

-			theDest.Send(msg);

+			// Encode all meta data (e.g., headers and properties)

+			EncodeField(msgDataBuilder, WireFormat.MFT_MESSAGEID, message.NMSMessageId);

+			EncodeField(msgDataBuilder, WireFormat.MFT_TIMESTAMP, message.NMSTimestamp.ToBinary());

+			if(null != message.NMSType)

+			{

+				EncodeField(msgDataBuilder, WireFormat.MFT_NMSTYPE, message.NMSType);

+			}

+

+			if(null != message.NMSCorrelationID)

+			{

+				EncodeField(msgDataBuilder, WireFormat.MFT_CORRELATIONID, message.NMSCorrelationID);

+			}

+

+			if(null != message.NMSReplyTo)

+			{

+				EncodeField(msgDataBuilder, WireFormat.MFT_REPLYTO, ((Destination) message.NMSReplyTo).Name);

+			}

+

+			EncodeField(msgDataBuilder, WireFormat.MFT_DELIVERYMODE, message.NMSDeliveryMode);

+			EncodeField(msgDataBuilder, WireFormat.MFT_PRIORITY, message.NMSPriority);

+			EncodeField(msgDataBuilder, WireFormat.MFT_TIMETOLIVE, message.NMSTimeToLive.Ticks);

+

+			IPrimitiveMap properties = message.Properties;

+			if(null != properties && properties.Count > 0)

+			{

+				// Encode into a temporary buffer, and then place a single buffer into the msgDataBuilder.

+				List<byte> propertiesBuilder = new List<byte>();

+

+				EncodeFieldData(propertiesBuilder, propertiesBuilder.Count);

+				foreach(string propertyKey in properties.Keys)

+				{

+					EncodeFieldData(propertiesBuilder, propertyKey);

+					EncodeFieldData(propertiesBuilder, properties.GetBytes(propertyKey));

+				}

+

+				EncodeField(msgDataBuilder, WireFormat.MFT_HEADERS, propertiesBuilder.ToArray());

+			}

+

+			if(message is ITextMessage)

+			{

+				EncodeField(msgDataBuilder, WireFormat.MFT_MSGTYPE, WireFormat.MT_TEXTMESSAGE);

+				// Append the message text body to the msg.

+				string msgBody = ((ITextMessage) message).Text;

+

+				if(null != msgBody)

+				{

+					EncodeField(msgDataBuilder, WireFormat.MFT_BODY, msgBody);

+				}

+			}

+			else

+			{

+				// TODO: Add support for more message types

+				EncodeField(msgDataBuilder, WireFormat.MFT_MSGTYPE, WireFormat.MT_MESSAGE);

+			}

+

+			// Put the sentinal field marker.

+			EncodeField(msgDataBuilder, WireFormat.MFT_NONE, 0);

+			theDest.Send(msgDataBuilder.ToArray());

+		}

+

+		private void EncodeField(List<byte> msgDataBuilder, int msgFieldType, string fieldData)

+		{

+			if(null == fieldData)

+			{

+				fieldData = string.Empty;

+			}

+

+			EncodeField(msgDataBuilder, msgFieldType, Encoding.UTF8.GetBytes(fieldData));

+		}

+

+		private void EncodeField(List<byte> msgDataBuilder, int msgFieldType, Enum fieldData)

+		{

+			EncodeField(msgDataBuilder, msgFieldType, Convert.ToInt32(fieldData));

+		}

+

+		private void EncodeField(List<byte> msgDataBuilder, int msgFieldType, int fieldData)

+		{

+			EncodeField(msgDataBuilder, msgFieldType, BitConverter.GetBytes(IPAddress.HostToNetworkOrder(fieldData)));

+		}

+

+		private void EncodeField(List<byte> msgDataBuilder, int msgFieldType, long fieldData)

+		{

+			EncodeField(msgDataBuilder, msgFieldType, BitConverter.GetBytes(IPAddress.HostToNetworkOrder(fieldData)));

+		}

+

+		private void EncodeField(List<byte> msgDataBuilder, int msgFieldType, byte[] fieldData)

+		{

+			// Encode the field type

+			msgDataBuilder.AddRange(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(msgFieldType)));

+			EncodeFieldData(msgDataBuilder, fieldData);

+		}

+

+		private void EncodeFieldData(List<byte> msgDataBuilder, int fieldData)

+		{

+			msgDataBuilder.AddRange(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(fieldData)));

+		}

+

+		private void EncodeFieldData(List<byte> msgDataBuilder, string fieldData)

+		{

+			if(null == fieldData)

+			{

+				fieldData = string.Empty;

+			}

+

+			EncodeFieldData(msgDataBuilder, Encoding.UTF8.GetBytes(fieldData));

+		}

+

+		private void EncodeFieldData(List<byte> msgDataBuilder, byte[] fieldData)

+		{

+			// Encode the field length

+			int fieldLength = 0;

+

+			if(null != fieldData)

+			{

+				fieldLength = fieldData.Length;

+			}

+

+			EncodeFieldData(msgDataBuilder, fieldLength);

+			if(0 != fieldLength)

+			{

+				// Encode the field data

+				msgDataBuilder.AddRange(fieldData);

+			}

 		}

 

 		public void Dispose()

diff --git a/src/main/csharp/Session.cs b/src/main/csharp/Session.cs
index bcde99c..1478a4e 100644
--- a/src/main/csharp/Session.cs
+++ b/src/main/csharp/Session.cs
@@ -16,6 +16,7 @@
  */

 

 using System;

+using System.Collections.Generic;

 using System.Messaging;

 

 namespace Apache.NMS.ZMQ

@@ -28,6 +29,8 @@
         private Connection connection;

         private AcknowledgementMode acknowledgementMode;

         private MessageQueueTransaction messageQueueTransaction;

+		private List<Destination> destinations = new List<Destination>();

+		private object destinationLock = new object();

 

         public Session(Connection connection, AcknowledgementMode acknowledgementMode)

         {

@@ -46,13 +49,49 @@
 

         public void Close()

         {

-            if(MessageQueueTransaction != null)

+			List<Destination> closingDestinations = null;

+

+			lock(destinationLock)

+			{

+				if(destinations.Count > 0)

+				{

+					closingDestinations = new List<Destination>(destinations);

+				}

+

+				destinations.Clear();

+			}

+

+			if(null != closingDestinations)

+			{

+				foreach(Destination dest in closingDestinations)

+				{

+					dest.Dispose();

+				}

+			}

+			

+			if(MessageQueueTransaction != null)

             {

                 MessageQueueTransaction.Dispose();

                 MessageQueueTransaction = null;

             }

         }

 

+		internal void RegisterDestination(Destination dest)

+		{

+			lock(destinationLock)

+			{

+				destinations.Add(dest);

+			}

+		}

+

+		internal void UnregisterDestination(Destination dest)

+		{

+			lock(destinationLock)

+			{

+				destinations.Remove(dest);

+			}

+		}

+

         #region Producer methods

         public IMessageProducer CreateProducer()

         {

diff --git a/src/main/csharp/WireFormat.cs b/src/main/csharp/WireFormat.cs
new file mode 100644
index 0000000..8cc90d1
--- /dev/null
+++ b/src/main/csharp/WireFormat.cs
@@ -0,0 +1,55 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to You under the Apache License, Version 2.0

+ * (the "License"); you may not use this file except in compliance with

+ * the License.  You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+namespace Apache.NMS.ZMQ

+{

+	public class WireFormat

+	{

+		/// <summary>

+		/// Message Field Types

+		/// IMPORTANT: These assigned numbers cannot change. If new field types

+		/// are added, then they must be assigned new numbers. Do not re-use numbers.

+		/// </summary>

+		public const int MFT_NONE = 0;

+		public const int MFT_MESSAGEID = 1;

+		public const int MFT_TIMESTAMP = 2;

+		public const int MFT_NMSTYPE = 3;

+		public const int MFT_CORRELATIONID = 4;

+		public const int MFT_REPLYTO = 5;

+		public const int MFT_DELIVERYMODE = 6;

+		public const int MFT_PRIORITY = 7;

+		public const int MFT_TIMETOLIVE = 8;

+		public const int MFT_HEADERS = 9;

+		public const int MFT_MSGTYPE = 10;

+		public const int MFT_BODY = 11;

+

+		// Message Types

+		/// <summary>

+		/// Message Types

+		/// These are the base message types. This is a sub-field of the MFT_MSGTYPE message field type.

+		/// IMPORTANT: These numbers cannot be changed. It will break wireformat compatibility.

+		/// </summary>

+		public const int MT_UNKNOWN = 0;

+		public const int MT_MESSAGE = 1;

+		public const int MT_TEXTMESSAGE = 2;

+		public const int MT_BYTESMESSAGE = 3;

+		public const int MT_MAPMESSAGE = 4;

+		public const int MT_OBJECTMESSAGE = 5;

+		public const int MT_STREAMMESSAGE = 6;

+

+	}

+}

diff --git a/vs2010-zmq-net-4.0-test.csproj b/vs2010-zmq-net-4.0-test.csproj
index 3a58166..ecd7f06 100644
--- a/vs2010-zmq-net-4.0-test.csproj
+++ b/vs2010-zmq-net-4.0-test.csproj
@@ -57,6 +57,9 @@
     <None Include="Apache.NMS.ZMQ.Test.nunit">

       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>

     </None>

+    <Content Include="nmsprovider-test.config">

+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>

+    </Content>

   </ItemGroup>

   <ItemGroup>

     <ProjectReference Include="vs2010-zmq-net-4.0.csproj">

diff --git a/vs2010-zmq-net-4.0.csproj b/vs2010-zmq-net-4.0.csproj
index f41958b..bee8036 100644
--- a/vs2010-zmq-net-4.0.csproj
+++ b/vs2010-zmq-net-4.0.csproj
@@ -74,6 +74,7 @@
     </Compile>

     <Compile Include="src\main\csharp\TextMessage.cs" />

     <Compile Include="src\main\csharp\Utils.cs" />

+    <Compile Include="src\main\csharp\WireFormat.cs" />

   </ItemGroup>

   <ItemGroup>

     <Content Include="lib\clrzmq\net-4.0\libzmq.dll">