Message Queuing MSMQ Explained with Examples
Before i get started with MSMQ,I believe most of the developers have question
like
What Is Message Queuing?
Message Queuing is a
middleware component of the Windows operating system. Your application can use Message
Queuing to send messages to another application even if the recipient
application is not running or the computer on which the sender or recipient
application is running is disconnected from the network. Messages are stored and
forwarded by Message Queuing until they reach the destination queue. Later, when
a recipient application runs, it can retrieve the messages from the queue.
Message Queuing decouples sender and recipient applications so they do not need
to run at the same time. Message Queuing provides built-in security, transaction
support, and more
Second Question like How do I get Message Queuing
?
Message Queuing is a built-in component of Windows. Fig Below Shows where you
can find MSMQ Component in your computer
To install Message Queuing on a computer running Windows 2000 or Windows XP
1.Click Start, click
Control Panel, and then click
Add or Remove Programs.
2.Click Add/Remove Windows
Components.
3.In the Windows Components Wizard
dialog box, in the Components box, select the
Message Queuing check box, and then click
Next.
To install Message Queuing on a computer running Windows Server 2003
1.Point to Start, point
to Control Panel, and then click
Add or Remove Programs.
2.In the Add or Remove Programs
dialog box, click Add/Remove Windows Components.
3.In the Windows Components
Wizard dialog box, in the Components box,
click Application Server, and then click
Details.
4.In the Application Server
dialog box, in the Subcomponents of Application Server
box, select the Message Queuing check box, and then
click OK.
5.In the Windows Components
Wizard dialog box, click Next.
1. alright, I installed it. Can I see it in action, immediately?
Yes.
To use Message Queuing to send a message on a computer running Windows XP
1.Click Start,
right-click My Computer, and then click
Manage.
2.In the console tree, expand
Services and Applications, and then expand Message
Queuing.
3.In the console tree, under
Message Queuing, right-click Private Queues,
point to New, and then click
Private Queue.
4.In the New Private Queue
dialog box, in the Queue Name box, enter
MyPrivQ, and then click OK.
5.Using Notepad, copy the contents of
vbsSendMsg.vbs [it is just
vbscript which is joing job of sending message to a queue]into a file with a .vbs extension.
6.Run this .vbs script file from the command line prompt.
The .vbs script file will send a message to the queue that you just created.
7.In the console tree, expand
MyPrivQ, and then click Queue messages.
The message appears in the details pane.
To send a message between computers
1.Create a queue on the remote computer to receive the
message.
2.On the computer from which the message will be sent,
locate the following line in the .vbs script file created in the preceding
procedure:
strFormatName = "direct=os:.\private$\myprivq"
3.Change the preceding line as follows:
strFormatName = "direct=os:ComptuerName\private$\myprivq"
vbsSendMsg.vbs
Option Explicit
Dim objInfo
Dim objQue
Dim objMsg
Dim strFormatName ' Destination
strFormatName = "direct=os:.\private$\myprivq"
Set objInfo = CreateObject("MSMQ.MSMQQueueInfo")
Set objMsg = CreateObject("MSMQ.MSMQMessage")
objMsg.Label = "my message"
objInfo.FormatName = strFormatName
set objQue = objInfo.Open( 2, 0 )
' Send Message
objMsg.Send objQue
' Close Destination
objQue.Close
Set objMsg = Nothing
Set objInfo = Nothing
msgbox "Done..."
where
ComputerName is the name of the remote computer to
receive the message
Now Let us Start up with
Creating MSMQ Queue programatically
The following code snippet shows how to create a new message queue. First you
want to check with MessageQueue.Exists if the queue already exists. If not, then
you can create a new queue with MessageQueue.Create and provide the queue path
plus if the queue is transactional or not. The queue path consists of three
parts - the machine name, if it is a private queue or not plus the name of the
queue. If you address a MSMQ instance on the same machine your code runs on,
then you can use instead of the machine name also a dot. A public queue path
uses "Machine name\Queue name" and a private queue path "Machine
name\Private$\Queue name". The label of the queue is a user friendly name of the
queue. Queues can also participate in transactions. MSMQ distinguishes between
internal and external transactions. Internal transactions only encompass MSMQ.
You start a transaction with a MessageQueueTransaction object, then send your
messages with a MessageQueue object and at the end commit or abort the
transaction. Aborting an internal transaction will roll back all send messages,
meaning no message will be send. Messages can only be read/received after the
internal transaction has been committed. External transaction means the
component which sends the messages to a queue is registered in COM+ and
participates in a COM+ transaction. In this case you use the COM+ transaction
controls which itself use the MSDTC "Distributed Transaction Coordinator". The
MSDTC will commit or abort the transaction. Otherwise internal and external
transactions behave the same way. You can us internal or external transactions
only when the queue has been marked as transactional. If the queue has been
marked as transactional you can send only transactional messages and if it has
been marked as "not transactional" then you can only send non transactional
messages.
// check to make sure the message queue does not exist already
if (!MessageQueue.Exists(QueueName))
{
// create the new message queue and make it transactional
MessageQueue MQ = MessageQueue.Create(QueueName,true);
// set the label name and close the message queue
MQ.Label = LabelName;
MQ.Close();
}
The next code snippet shows how to delete an existing queue. First you check
with MessageQueue.Exists if the queue exists then you delete it with
MessageQueue.Delete. Message queues have also windows security ACLs assigned.
This allows you to control access to queues through standard windows security.
The security for queues can be set through the "Computer Management" interface
by bringing up the properties window of a queue. If you do not have the right to
delete the queue then MessageQueue.Delete raises a MessageQueueException
exception, which for example is handled in the code snippet below.
if (MessageQueue.Exists(QueuePath))
{
try
{
MessageQueue.Delete(QueuePath);
}
catch ( MessageQueueException)
{
MessageQueue.Show( "You do not have the rights to delete the queue.");
}
}
You can also enumerate a list of all message queues present on a MSMQ instance.
To get a list of public queues you call GetPublicQueues,
GetPublicQueuesByCategory, GetPublicQueuesByLabel or GetPublicQueuesByMachine on
the MessageQueue class. To get the list of private queues you call
MessageQueue.GetPrivateQueuesByMachine. The method names are self explanatory.
All of them return an array of MessageQueue objects which you then use to obtain
information about each queue. The code snippet below gets the list of private
queues and then loops through each to get the queue name and label and if the
queue is transactional or not.
MessageQueue[] MQList = MessageQueue.GetPrivateQueuesByMachine(MachineName);
if (MQList.Length > 0)
{
string[,] MQNameList = new string[MQList.Length, 3];
for (int Count = 0; Count < MQList.Length; Count++)
{
MQNameList[Count, 0] = MQList[Count].QueueName;
MQNameList[Count, 1] = MQList[Count].Label;
MQNameList[Count, 2] = MQList[Count].Transactional.ToString();
}
}
A look how to send and receive messages with the .NET framework
You use a Message object to send a message to a queue. First you create a new
MessageQueue object and pass along the queue path you want the message to be
send to. Next you create a Message object and pass along the object which
contains the data you want to send. When the message is send, this object will
get serialized and stored in the message body. The default serialization object
used is the XmlMessageFormatter, which serializes the object to XML. You can
also use a BinaryMessageFormatter, which formats the object into binary format,
or an ActiveXMessageFormatter which serializes in a format which is compatible
with COM message queuing components. This formatter works with most primitive
types and requires for objects the implementation of the IPersistStream
interface. If you want to use a formatter other then the default one then you
need to create an instance of it and assign it to the Formatter property of the
message object. Then you set the properties of the message object as required
and finally call the Send() method on the message queue object you created. At
the end you close the queue by calling Close() on the message queue object. Here
is a sample code snippet:
// create a message queue object
MessageQueue MQueue = new MessageQueue(MessageQueuePath);
// create the message and set the base properties
Message Msg = new Message(TheObject);
Msg.ResponseQueue = new MessageQueue(ResponseMessageQueuePath);
Msg.Priority = MessagePriority.Normal;
Msg.UseJournalQueue = true;
Msg.Label = Label;
// we want a acknowledgement if received or not in the response queue
Msg.AcknowledgeType = AcknowledgeTypes.FullReceive;
Msg.AdministrationQueue = new MessageQueue(ResponseMessageQueuePath);
// send the message
MQueue.Send(Msg);
// close the mesage queue
MQueue.Close();
The message object itself provides you with a lot of control about the message
you are sending. You can set a user friendly label with the Label property. You
can set the priority of the message with the Priority property using a value of
the MessagePriority enumeration. If you want the message to be placed in a
journal when received/read then set the UseJournal property on the message to
true. The AcknowledgeType property provides you control over which
acknowledgement you require using a value of the AcknowledgeTypes enumeration.
For example setting it to FullReceive means you will get an acknowledgement if
it has been received/read but also if the message is undeliverable or expired.
The acknowledgement is sent to the queue specified by the AdministrationQueue
property of the message. Via the TimeToBeReceived property you can specify by
when the message needs to be received/read otherwise it will expire and be
removed by MSMQ. You don't set an absolute date/time but rather a time span
using a TimeSpan object. There are many more properties you can set on the
Message object. Please refer to the MSDN documentation for a complete list.
You can use MessageQueueTransaction to start an internal transaction. You create
an instance of that class and call BeginTransaction(). For every message you
send and want to be part of this transaction you pass along this transaction
object in the Send() method. When done you call Commit() or Abort() on the
transaction object. The messages you send as part of a transaction will only be
available to read by the time you call Commit(). Don't forget to still call
Close() on the message queue object. Using a transaction works only with queues
which are marked as transactional. Here is a code snippet:
// create a message queue transaction and start it
MessageQueueTransaction Transaction = new MessageQueueTransaction();
Transaction.Begin();
// create a message queue
MessageQueue MQueue = new MessageQueue(MessageQueuePath);
// create the message and set the base properties
Message Msg = new Message(TheObject);
// send the message as part of the transaction
MQueue.Send(Msg, Transaction);
// commit the transaction
Transaction.Commit();
// close the mesage queue
MQueue.Close();
Reading a message will automatically remove it from the queue and if the message
property UseJournal has been set to true a copy will be placed in the journal.
You read messages by calling Receive() on the message queue object. This call is
synchronous and blocks till a message is available. An overloaded version of
Receive() allows to specify a time span, the time how long the call will wait to
receive a message. The Receive() method returns a Message object. The Body
property allows you to access the object which was stored in the message when
sent. It is important to use the same formatter type when reading and sending
the message or otherwise an exception will be thrown. For example, if you used a
BinaryMessageFormatter when sending the message then you need to create a
BinaryMessageFormatter object and assign it to the Formatter property on the
message queue object before you call the Receive() method. The
XmlMessageFormatter is not able to recognize which type has been serialized into
XML. Therefore you need to set the TargetType property on the
XmlMessageFormatter to the type you have stored in the message. Through this the
XML formatter knows which type to create and de-serialize out of the message.
For performance improvement Receive() reads by default only a handful of
properties from the message. Accessing a message property which has not been
read throws an InvalidOperationException exception. Before you call Receive()
you set on the message queue object through the MessageReadPropertyFilter
property which message properties you want to be read. Call
MessageReadPropertyFilter.SetAll() if you want to read every property of the
message. Here is a code snippet:
// open the selected message queue
MessageQueue MQueue = new MessageQueue(ListOfMessageQueues.SelectedNode.Name);
MQueue.MessageReadPropertyFilter.SetAll();
// the target type we have stored in the message body
((XmlMessageFormatter)MQueue.Formatter).TargetTypes = new Type[] { typeof(Order) };
try
{
// read the message from the queue, but only wait for 5 sec
System.Messaging.Message Msg = MQueue.Receive(new TimeSpan(0, 0, 5));
// read the order from the message body
Order Order = (Order)Msg.Body;
// close the message queue
MQueue.Close();
}
// no message present to read
catch (MessageQueueException)
{
MessageBox.Show(this, "There is no message in the queue at this time.");
}
The code snippet specifies a time span for how log to wait for a message. If no
message is present within that timeout, then a MessageQueueException exception
is thrown. You can also read messages asynchronously. First you register an
event handler of the type ReceiveCompletedEventHandler with the property
ReceiveCompleted on the message queue object. Then you call on the message queue
object instead of Receive() the method BeginReceive(). BeginReceive() will
return immediately and the event handler you registered will be called when a
message is available for reading. In the event handler you then call
EndReceive() which returns the message. When you call BeginReceive() you get an
IAsyncResult handler back which you need to pass along when calling EndReceive()
so the method knows which asynchronous read to finish. You can call
BeginReceive() multiple times. In this case you are waiting for multiple
messages and for each available message your event handler gets called. Your
event handler stays active, so keeps still waiting for a message, even after
calling Close() on the message queue object. This behavior is due to the fact
that read and write handles to the message queues are cached for performance
reasons. Set the MessageQueue.EnbaleConnectionCache property to false to
disables the cache which will make sure that your event handler no longer
listens for messages after you close your message queue object. Here is a code
snippet for asynchronous reads:
MessageQueue.EnableConnectionCache = false;
MessageQueue MQueue = new MessageQueue(MessageQueuePath);
MQueue.MessageReadPropertyFilter.SetAll();
((XmlMessageFormatter)MQueue.Formatter).TargetTypes = new Type[] { typeof(Order) };
MQueue.ReceiveCompleted += new ReceiveCompletedEventHandler(MessageEventHandler);
IAsyncResult MQResult = MQueue.BeginReceive(new TimeSpan(1, 0, 0), MQueue);
private void MessageEventHandler(object sender, ReceiveCompletedEventArgs e)
{
System.Messaging.Message Msg =
((MessageQueue)e.AsyncResult.AsyncState).
EndReceive(e.AsyncResult);
Order Order = (Order)Msg.Body;
IAsyncResult AsyncResult = ((MessageQueue)e.AsyncResult.AsyncState).BeginReceive(
new TimeSpan(1, 0, 0), ((MessageQueue)e.AsyncResult.AsyncState));
}
When you call BeginReceive() you can also pass along some state information.
This can be any object and in our code snippet above it is the message queue
object. This way the event handler can get hold of the message queue object and
then call EndReceive(). If you want to continue waiting for the next message
then you need to call in the event handler again BeginReceive() on the same
message queue object. BeginReceive() and Receive() always read the first message
in the queue and remove it from the queue. You can use Peek() and BeginPeek()
with EndPeek() to peek for the first message without removing it.
MessageQueue.GetAllMessages() returns you a list of all messages in a queue
without removing them from the queue. Here is a code snippet:
MessageQueue MQueue = new MessageQueue(MessageQueuePath);
MQueue.MessageReadPropertyFilter.Id = true;
MQueue.MessageReadPropertyFilter.Priority = true;
MQueue.MessageReadPropertyFilter.SentTime = true;
MQueue.MessageReadPropertyFilter.MessageType = true;
MQueue.MessageReadPropertyFilter.Label = true;
Message[] Msg = MQueue.GetAllMessages();
string[,] Messages = new string[Msg.Length,5];
for (int Count = 0; Count < Msg.Length; Count++)
{
Messages[Count, 0] = Msg[Count].Id;
Messages[Count, 1] = Msg[Count].Label;
Messages[Count, 2] = Msg[Count].MessageType.ToString();
Messages[Count, 3] = Msg[Count].Priority.ToString();
Messages[Count, 4] = Msg[Count].SentTime.ToString();
}
MQueue.Close();
Another way to obtain the list of messages without removing them from a queue is
the method MessageQueue.GetMessageEnumerator(). It returns a MessageEnumerator
enumerator which you use to move from message to message. If there is no message
available it will wait the specified time span. Here is the code snippet:
MessageQueue MQueue = new MessageQueue(MessageQueuePath);
MQueue.MessageReadPropertyFilter.SetAll();
MessageEnumerator Enumerator = MQueue.GetMessageEnumerator();
while (Enumerator.MoveNext(new TimeSpan(0,0,1)))
{
Message Msg = Enumerator.Current;
}
MQueue.Close();
The MessageQueue object provides you with a lot of control how to read and send
messages. But sometimes it would be useful to get notified when a message has
been sent to a queue. You can achieve that by having a windows service running
in the background and receiving or peeking synchronously or asynchronously
messages. There is another approach you can use, MSMQ triggers.
Summary: