MSMQ, Your Reliable Asynchronous Message Processing

Introduction

Today's enterpriseapplications are multi-tiered and interop with many other systems in the enterprise.Users demand easy to use, responsive and sophisticated applications or they willavoid using it. Reliability and responsiveness do not come easy. Reliable exchangeand processing of data (messages) in itself is a time consuming and complex undertaking.Microsoft Message Queue server, short MSMQ, provides exactly that ? guaranteed andreliable message delivery. It provides an easy way to send messages between differentapplications or to process messages asynchronously.

Many back-end processes canbe long-running. You want to separate these processes from the user-interaction.You want to be able to store the message and process it asynchronously while theuser can go on and do other things. Then, when the message has been processed youwant to provide a means of notifying the user about its result. Asynchronous processingis key for being responsive to the user and can also become complex. Think aboutthe following example in a typical eCommerce application. The user purchases somegoods from your site and uses a credit card for its payment. In real life that meanscalling out to a credit merchant to verify the credit card and securing the funds,shipping of the goods, updating your inventory status and making sure to re-stockyour warehouse. Some of that you want to do in real time and some of that you wantto do asynchronously. But all these interactions need to happen in a reliable fashion.You can't charge the customer and then for whatever technical reasons not ship thegoods. You also want to be assured that your inventory status gets updated and soldgoods are replaced. Any technical brake-down in that process means a negative experiencefor customers and high unnecessary costs. Reliable and guaranteed message deliveryand processing avoids all this. And any messaging solution like "Microsoft MessageQueue" server can make it easy to provide this guaranteed, reliable and asynchronousmessage processing.

Overview of Microsoft Message Queue server

MSMQ comes as partof the Windows OS (MSMQ 1.0 with Windows NT 4, Windows 95 and Windows 98; MSMQ 2.0with Windows 2000; MSMQ 3.0 with Windows 2003 and Windows XP). This article concentrateson MSMQ 3.0 running on Windows 2003. You can find a number of whitepapers and informationon the Microsoft site (clickhere). You can install MSMQ through the "Add/RemoveWindows Components" (under the component "Application Server" and "Message Queuing").

Queues are used to store and route messages. Applications send messages to queuesand receive/read messages from queues. There are two different types of queues ?private and public queues. Private queues do not provide any routing. Applicationscan send messages to private queues and other applications or other layers of thesame application can then read these messages from the same queue of the same MSMQinstance. Private queues are accessed through "Machine name\Private$\Queue name",for example "MyMachine\Private$\Orders". If MSMQ is installed on the same machineas your application then you can replace the machine name with a dot, for example".\Private$\Orders". Private queues do not provide any routing but can still beaccessed remotely. So you can have a workgroup where one machine has MSMQ installedand all other machines in that workgroup can access this MSMQ instance. Privatequeues are your choice if all clients/machines accessing the queue are in closeproximity and have a fast and reliable connection between them. A good example wouldbe an application which splits up the order placement, order processing and orderprocessed notification into three separate layers but all layers are in close proximity.All three layers can use the same MSMQ instance with one or multiple private queuesto exchange messages reliable and easy.

Public queues on the other hand providemessage routing. Public queues are your choice if your applications are distributedor have to deal with unreliable communication mediums. A good example would be tohave order placement and order fulfillment in different geographical locations oreven be performed by different companies. Take an eCommerce portal which resellsproducts from other merchants. You want to take the orders with your applicationand then reliably route these orders to each merchant for the fulfillment. Thismeans you might have to send these orders over the internet to merchants residingall over the country ? a distributed environment with an unreliable communicationmedium in between. You would have a MSMQ server in close proximity to the servershosting your eCommerce application and have a public queue for each merchant. TheeCommerce application would place orders in the appropriate public queues and thenrely on MSMQ to guarantee the delivery. Merchants would rely on the same means tosend notifications back to the eCommerce site that orders have been processed. Publicqueues are accessed through "Machine name\Queue name", for example "MyServer\Orders".You can also replace the machine name with a dot if your application resides onthe same machine as MSMQ.

MSMQ can be deployed in two modes ? workgroup mode ordomain mode. The workgroup mode provides only private queues. So there is no messagerouting available in the workgroup mode. The domain mode provides private queuesand public queues. But the domain mode requires that the machine you install MSMQon is part of a domain and that the domain uses Active Directory. When you installMSMQ in the workgroup mode you only require the components "Common" and "Triggers".When installing in the domain mode you need the components "Common", "Active DirectoryIntegration", "Routing Support" and "Triggers". When installing in domain mode youcan set up sites and site links which determine which queues replicated and howmessages are routed. This goes beyond this article.

Private queues are a bit fasterthen public queues. Think about your requirements to determine which type of queueyou need to use. The type of queue determines which MSMQ mode you need. If all clientsaccessing MSMQ are in close proximity and have a fast and reliable medium to accessit, then use private queues and install MSMQ in workgroup mode. If you have a distributedenvironment or an unreliable communication medium you need message routing whichrequires public queues and you install MSMQ in domain mode. MSMQ installs two windowsservices ? "Message Queuing" and "Message Queuing Triggers".

There is also an administrativeinterface which you find under "Computer Management". Under the item "Services andApplications" you find an entry called "Message Queuing". You can view all privateand public queues. You can create new queues, edit the properties of existing queuesand also delete existing queues. Under each queue you find three items:

  • Queue messages ? Shows you all the messages currently sitting in the queue. You can viewthe properties of each message but you can not change it. You can not create a newmessage or delete an individual message. You can remove all messages by right clickingon "Queue Messages" and selecting "All Tasks | Purge".
  • Journal Messages ? Whenyou create a queue you can also enable a "Journal". This means when a message isreceived/read from that queue a copy will be placed into the journal of that queue.So you have a copy of every message read and processed from that queue. Regularlypurge messages from the Journal which are no longer needed otherwis

    e it wil
    l growfor ever.

  • Triggers ? Allows you to register a trigger to be called when a messageis placed into this message queue. When setting up the trigger you can specify whethera COM component is called or an external executable (more on this later on).

Besideprivate and public queues you find also an entry called "System Queues". These aresystem generated queues which you can not modify or remove. Underneath you findthree queues:

  • Journal messages ? When you have enabled the "Journal" of a queuethen a copy of the message is placed in the journal of that queue when read/received.If the journal of the queue is not enabled then a copy is placed in the journalunder system queues. This gives you control to have a journal per queue or one systemjournal for all queues.
  • Dead-letter messages ? Places a copy of every messagewhich can not be delivered or expires before it is delivered or expires before itis received/read.
  • Transactional dead-letter messages ? Same as the dead-lettermessages queue but for all transactional messages. See more later on about transactionalmessages.

You can also programmatically create and delete queues as well as setthe properties of existing queues.

A look at MSMQ and the .NET framework

The .NETframework provides a number of classes to work with MSMQ, located in the System.Messagingnamespace. The two most important classes are MessageQueue and Message. The MessageQueueobject is used to open or delete existing queues and create new ones. It is alsoused to send and receive messages. The Message object itself is used to encapsulatethe data you want to send or receive into a message.

The following code snippetshows how to create a new message queue. First you want to check with MessageQueue.Existsif the queue already exists. If not, then you can create a new queue with MessageQueue.Createand provide the queue path plus if the queue is transactional or not. The queuepath consists of three parts ? the machine name, if it is a private queue or notplus the name of the queue. If you address a MSMQ instance on the same machine yourcode runs on, then you can use instead of the machine name also a dot. A publicqueue path uses "Machine name\Queue name" and a private queue path "Machine name\Private$\Queuename". The label of the queue is a user friendly name of the queue. Queues can alsoparticipate in transactions. MSMQ distinguishes between internal and external transactions.Internal transactions only encompass MSMQ. You start a transaction with a MessageQueueTransactionobject, then send your messages with a MessageQueue object and at the end commitor abort the transaction. Aborting an internal transaction will roll back all sendmessages, meaning no message will be send. Messages can only be read/received afterthe internal transaction has been committed. External transaction means the componentwhich sends the messages to a queue is registered in COM+ and participates in aCOM+ transaction. In this case you use the COM+ transaction controls which itselfuse the MSDTC "Distributed Transaction Coordinator". The MSDTC will commit or abortthe transaction. Otherwise internal and external transactions behave the same way.You can us internal or external transactions only when the queue has been markedas transactional. If the queue has been marked as transactional you can send onlytransactional messages and if it has been marked as "not transactional" then youcan 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 withMessageQueue.Exists if the queue exists then you delete it with MessageQueue.Delete.Message queues have also windows security ACLs assigned. This allows you to controlaccess to queues through standard windows security. The security for queues canbe set through the "Computer Management" interface by bringing up the propertieswindow of a queue. If you do not have the right to delete the queue then MessageQueue.Deleteraises a MessageQueueException exception, which for example is handled in the code snippet below.

 

// check to make sure the message queue does exist
if (MessageQueue.Exists(QueuePath))
{
      try
      {
            MessageQueue.Delete(QueuePath);
      }
      catch (MessageQueueException)
      {
            MessageBox.Show("Youdo not have the rights to delete the queue.");
      }
}

You can also enumerate a listof all message queues present on a MSMQ instance. To get a list of public queuesyou call GetPublicQueues, GetPublicQueuesByCategory, GetPublicQueuesByLabel or GetPublicQueuesByMachineon the MessageQueue class. To get th

e list of private queues you call MessageQueue.GetPrivateQueuesByMachine.The method names are self explanatory. All of them return an array of MessageQueueobjects which you then use to obtain information about each queue. The code snippetbelow gets the list of private queues and then loops through each to get the queuename and label and if the queue is transactional or not.

 

// get the list of message queues
MessageQueue[] MQList = MessageQueue.GetPrivateQueuesByMachine(MachineName);
<o:p>
</o:p>
// check to make sure we found some private queues on that machine
if (MQList.Length>0)
{
   // allocate a string array which holds for each queue the name, path, etc.
   string[,] MQNameList = new string[MQList.Length, 3];
   
   // loop through all message queues and get the name, path, etc.
   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 senda message to a queue. First you create a new MessageQueue object and pass alongthe queue path you want the message to be send to. Next you create a Message objectand pass along the object which contains the data you want to send. When the messageis send, this object will get serialized and stored in the message body. The defaultserialization object used is the XmlMessageFormatter, which serializes the objectto XML. You can also use a BinaryMessageFormatter, which formats the object intobinary format, or an ActiveXMessageFormatter which serializes in a format whichis compatible with COM message queuing components. This formatter works with mostprimitive types and requires for objects the implementation of the IPersistStreaminterface. If you want to use a formatter other then the default one then you needto create an instance of it and assign it to the Formatter property of the messageobject. Then you set the properties of the message object as required and finallycall the Send() method on the message queue object you created. At the end you closethe queue by calling Close() on the message queue object. Here is a sample codesnippet:

// 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 messageobject 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 priorityof 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 theUseJournal property on the message to true. The AcknowledgeType property providesyou control over which acknowledgement you require using a value of the AcknowledgeTypesenumeration. For example setting it to FullReceive means you will get an acknowledgementif 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 propertyof the message. Via the TimeToBeReceived property you can specify by when the messageneeds to be received/read otherwise it will expire and be removed by MSMQ. You don'tset an absolute date/time but rather a time span using a TimeSpan object. Thereare many more properties you can set on the Message object. Please refer to theMSDN documentation for a complete list.

You can use MessageQueueTransaction to startan 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 alongthis 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 onlybe available to r

ead 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 aremarked 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 messageproperty UseJournal has been set to true a copy will be placed in the journal. Youread messages by calling Receive() on the message queue object. This call is synchronousand blocks till a message is available. An overloaded version of Receive() allowsto 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 accessthe object which was stored in the message when sent. It is important to use thesame formatter type when reading and sending the message or otherwise an exceptionwill be thrown. For example, if you used a BinaryMessageFormatter when sending themessage then you need to create a BinaryMessageFormatter object and assign it tothe Formatter property on the message queue object before you call the Receive()method. The XmlMessageFormatter is not able to recognize which type has been serializedinto XML. Therefore you need to set the TargetType property on the XmlMessageFormatterto the type you have stored in the message. Through this the XML formatter knowswhich type to create and de-serialize out of the message. For performance improvementReceive() reads by default only a handful of properties from the message. Accessinga message property which has not been read throws an InvalidOperationException exception.Before you call Receive() you set on the message queue object through the MessageReadPropertyFilterproperty 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 5sec
      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.");
}

Thecode snippet specifies a time span for how log to wait for a message. If no messageis present within that timeout, then a MessageQueueException exception is thrown.You can also read messages asynchronously. First you register an event handler ofthe type ReceiveCompletedEventHandler with the property ReceiveCompleted on themessage queue object. Then youcall on the message queue object instead of Receive() the method BeginReceive().BeginReceive() will return immediately and the event handler you registered willbe called when a message is available for reading. In the event handler you thencall EndReceive() which returns the message. When you call BeginReceive() you getan 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 eachavailable message your event handler gets called. Your event handler stays active,so keeps still waiting for a message, even after calling Close() on the messagequeue object. This behavior is due to the fact that read and write handles to themessage queues are cached for performance reasons. Set the MessageQueue.EnbaleConnectionCacheproperty to false to disables the cache which will make sure that your event handlerno longer listens for messages after you close your message queue object. Here isa code snippet for asynchronous reads:

 

// it is important to set this to false,otherwise the message receiver event
// handler keeps still active even after closingthe message queue object
MessageQueue.EnableConnectionCache = false;

// open the selected message queue
MessageQueue MQueue = new MessageQueue(MessageQueuePath);
MQueue.MessageReadPropertyFilter.SetAll();

// the target types we have stored in the message body
((XmlMessageFormatter)MQueue.Formatter).TargetTypes = new Type[] { typeof(Order) };

// set the event handler to be called when the message has been received
MQueue.ReceiveCompleted += new ReceiveCompletedEventHandler(MessageEventHandler);

// start the receive message process; this call returns immediately
IAsyncResultMQResult = MQueue.BeginReceive(new TimeSpan(1, 0, 0), MQueue);


///
<summary>
/// event handler called when the message is ready to be read
/// </summary>
private void MessageEventHandler(object sender, ReceiveCompletedEventArgs e)
{
     // get the message from the queue
     System.Messaging.Message Msg = ((MessageQueue)e.AsyncResult.AsyncState).EndReceive(e.AsyncResult);

     // process the order
     Order Order = (Order)Msg.Body;

     //start looking for the next message
     IAsyncResult AsyncResult = ((MessageQueue)e.AsyncResult.AsyncState).BeginReceive(
            new TimeSpan(1, 0, 0), ((MessageQueue)e.AsyncResult.AsyncState));
}

When you callBeginReceive() you can also pass along some state information. This can be any objectand in our code snippet above it is the message queue object. This way the eventhandler can get hold of the message queue object and then call EndReceive(). Ifyou want to continue waiting for the next message then you need to call in the eventhandler again BeginReceive() on the same message queue object. BeginReceive() andReceive() 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 messagewithout removing it. MessageQueue.GetAllMessages() returns you a list of all messagesin a queue without removing them from the queue. Here is a code snippet:

 

// open the message queue
MessageQueue MQueue = new MessageQueue(MessageQueuePath);
<o:p>
</o:p>
// set the properties we want to retrieve
MQueue.MessageReadPropertyFilter.Id = true;
MQueue.MessageReadPropertyFilter.Priority= true;
MQueue.MessageReadPropertyFilter.SentTime = true;
MQueue.MessageReadPropertyFilter.MessageType= true;
MQueue.MessageReadPropertyFilter.Label = true;

// get all the messages;does not remove the messages!
Message[] Msg = MQueue.GetAllMessages();

// create a string array to store the message information
string[,] Messages = new string[Msg.Length,5];

// loop through each message and get the info we need
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();
}

// close the message queue
MQueue.Close();

Another way to obtain the list of messageswithout removing them from a queue is the method MessageQueue.GetMessageEnumerator().It returns a MessageEnumerator enumerator which you use to move from message tomessage. If there is no message available it will wait the specified time span.Here is the code snippet:

 

// open the message queue
MessageQueue MQueue = new MessageQueue(MessageQueuePath);
<o:p> 
</o:p>
// we want to retrieve all properties for the message
MQueue.MessageReadPropertyFilter.SetAll();

// get a message enumerator we can use to go through all the messages
MessageEnumeratorEnumerator = MQueue.GetMessageEnumerator();

// loop through all the messages
while (Enumerator.MoveNext(new TimeSpan(0,0,1)))
{
      // get a reference to the current message
      Message Msg = Enumerator.Current;
}

// close the message queue
MQueue.Close();

TheMessageQueue 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 toa queue. You can achieve that by having a windows service running in the backgroundand receiving or peeking synchronously or asynchronously messages. There is anotherapproach you can use, MSMQ triggers.

A closer look at MSMQ Triggers

To be able touse triggers you need to install the "Triggers" component of MSMQ and make surethat the "Message Queuing Triggers" windows service is running. You can create triggersthrough the "Computer Management", under the item "Services and Applications", "MessageQueuing" and "Triggers". First you need to create a rule under the item "Rules".A rule specifies a condition and the action to take when that condition is met.If you don't specify a condition, then the action applies to all messages in a queue.Otherwise the action only applies to messages which meet the condition. You cancreate complex conditions like the message label needs to contain this string, needsto have this priority, etc. The action can be to invoke this COM object or run thisexecutable.

So right click on the item "Rules" and select "New | Rule" from thepopup menu. Give the rule a name and description and click next. On the next screenyou define the condition. Select from the drop down box the condition, for example"Message Label contains" and enter in the "filter/value" box the value of that condition,for example the string the label needs to contain. Click on the add button to addthe condition. You can add as many conditions as needed or you remove individualconditions with the "Remove" button. When the condition is built, or if you don'twant to apply a condition, click on the Next button. The next screen defines theaction to take when the condition is met. Select if you want to invoke a COM componentor an executable. For the COM component you need to specify the ProgID and the methodto be executed, for example the ProgID "MQProcessor.MessageQueueProcessor" and themethod name "ProcessMessage". For an external executable to run you specify thefull path and name of the EXE. With the Parameters button you add the informationwhich gets passed on to the COM object or executable. Select the available parameterfrom the drop down box and click add. You can add multiple parameters, remove parametersand sort them with the up and down button. The list of parameters and their typesmust match your method signature if you use a COM object. For an executable

thisis the list of command line arguments passed along. When done click ok and ok againto save the rule. You can create as many rules as required and edit existingrules by double clicking on a rule or remove rules by right clicking on a rule andselecting "Delete" from the popup menu.

A rule does not specify to which queue itapplies. That is done by creating a trigger. Under each queue you find an item calledTrigger which allows you to specify a trigger for that queue. Under the item "Triggers"(under "Message Queuing") you see all triggers created on this MSMQ instance. Eitherway you create a new trigger by right clicking on the item "Triggers" and selecting"New | Trigger" from the popup menu. Give the trigger a name and select the processingtype which can be either "Peeking", "Retrieval" or "Transactional retrieval". Ifyou want this trigger to be active then check the "Enabled" checkbox". The "Serialized"checkbox results in a serialized processing, meaning messages are processed oneby one in the order they arrive. Otherwise if multiple messages arrive at the sametime multiple instances of your COM object or executable could be invoked. On thenext screen, after clicking Next, you can select the rules which are associatedwith this trigger. The list on the left side shows the available rules. Throughthe attach button you can assign rules to this trigger (shown on the right side).Through the Detach button you can remove rules from this trigger. Through the Upand Down button you can order the rules. The rules are executed one by one in theorder specified. Click Finish to save the trigger. You can create as many triggersas required and edit existing triggers by double clicking on a trigger or removetriggers by right clicking on a trigger and selecting "Delete" from the popup menu.

If you want to use a .NET type instead of a COM object, make sure that you assignto your type the Guid attribute and the ComVisible attribute. The message body passedalong (when selected in the parameter list) is an array of bytes. In order to beable to read the object you have stored in the message body you need to create aMessage object, create a MemoryStream using this array of bytes and then assignthe memory stream to the BodyStream property of the message object. You need toset again the formatter to use through the Formatter property of the message objectand if you use the XmlMessageFormatter you need to set again the type. Here is acode snippet:

 

[ComVisible(true)]
[Guid("CBA5E590-86F8-4e65-8D8D-F03E2DAD6461")]
public class MessageQueueProcessor
{
   [ComVisible(true)]
   public void ProcessMessage(object MsgBody,string MQPath,string RespFormatName)
   {
      try
      {
         // get the response message queue path from the response format name
         string MQResponsePath = RespFormatName.Split(':')[1];

        
// open the respone message queue
         MessageQueue MQueue = new MessageQueue(MQResponsePath);

         // create a message out of the message body we got passed along
         // so we can readthe message body in the format originally sent
         Message Msg = new Message();
         Msg.BodyStream= new System.IO.MemoryStream((Byte[])MessageBody);
         Msg.Formatter = new XmlMessageFormatter(new Type[] { typeof(Order) });

         // gets the Order from the message body passed along
         Order TheOrder = (Order)Msg.Body;

         // send the order unprocessed to the responsemessage queue
         // specified by the received message (so by the message sender)
         MQueue.Send(TheOrder);

         // close the response message queue again
         MQueue.Close();<

br />       }

      // catch any exceptionto keep the MSMQ trigger service ok
      catch(Exception)
      {}
   }
}

It is important to handleall exceptions. Any unhandled exception will result in the "Message Queuing Triggers" windows service to terminate. If your method signature does not match the parameterlist specified in the rule/trigger then you will see an entry in the applicationevent log. The source is set to "MSMQ Triggers" and the description will say thatthe action defined by the rule has not been invoked because the COM object is notregistered. The message is a bit misleading. After you created your .NET type youneed to register it as COM object using the "regasm" utility. So the type can befound and loaded you also need to install the .NET type in the GAC using the "gacutil"utility. As you develop and troubleshoot your .NET type it is best to first stopthe "Message Queuing Triggers" service, then unregister the type as COM object andthen to remove it from the GAC. Then register the type again as COM object, addit again to the GAC and finally start the "Message Queuing Triggers" service again.Without that sequence I encountered sometimes unexpected behaviors which cost me muchtime trying to debug my code without really having any issues. Create a batch filewhich you just run each time when you are ready to test and troubleshoot your .NETtype. Here is an example:

 

net stop "Message Queuing Triggers"
regasm MessageQueueProcessor.dll/unregister
gacutil /u MessageQueueProcessor
<o:p>
</o:p>
regasm MessageQueueProcessor.dll
gacutil/i MessageQueueProcessor.dll
net start "Message Queuing Triggers"<o:p></o:p>

If you need todebug your component, then attach the .NET debugger (through the menu "Debug | Attachto Process") to the process "mqtgsvc.exe". But only do it after you ran your componentonce so it recognizes the process also as .NET process. Otherwise it will not recognizeyour .NET component and you can't debug it.

Summary

Microsoft Message Queue serverprovides a powerful messaging framework. It takes care of the complexities of guaranteedmessage delivery and routing. It is a great enabler to separate the parts of yourapplication which interact with the user and back-end processing. It can also bea great enabler for asynchronous processing of long running tasks or processes.MSMQ triggers are a very convenient way to get notified about new messages and toprocess them. The .NET framework has a comprehensive set of types to interact withMSMQ. It makes it very easy to send and receive messages, to create new and maintainexisting queues and allows to add effortlessly transactional support.

The attachedsample application (created in Visual Studio 2005, using MSMQ 3.0) shows how to send transactional and non-transaction messages.It also demonstrates how to synchronously or asynchronously receive messages andhow to process these messages. The interface of the example is very simplistic butit provides a framework for an administrative interface. You can create new queues,view the list of existing queues, view the properties of existing queues and viewall the messages of queues. It also provides a trigger component which you can useto process incoming messages. See the "readme.htm" file for more details. If youhave comments to this article, please contact me @ klaus_salchner@hotmail.com. Iwant to hear if you learned something new. Contact me if you have questions aboutthis topic or article.

Download Source

 

About the author

Klaus Salchner has worked for 14 years inthe industry, nine years in Europe and another five years in North America. As aSenior Enterprise Architect with solid experience in enterprise software development,Klaus spends considerable time on performance, scalability, availability, maintainability,globalization/localization and security. The projects he has been involved in areused by more than a million users in 50 countries on three continents.

Klaus callsVancouver, British Columbia his home at the moment. His next big goal is doing theNew York marathon in 2005. Klaus is interested in guest speaking opportunities oras an author for .NET magazines or Web sites. He can be contacted at klaus_salchner@hotmail.com or http://www.enterprise-minds.com.

Twitter Digg Delicious Stumbleupon Technorati Facebook Email
Uncategorized

No comments yet... Be the first to leave a reply!