C# MSMQ - Send and Receive XML

Here's an article which can you give you better idea on "How to send and receive XML files using MSMQ", this application was developed using C# and let us see more in detail how it works and where it can be applied effectively in an real-time environment!

What this Application does?

The main objective of this application is to,

  • Display list of Private,Public,Transactional Deadletter and Non-Transactional Deadletter queues in the form of Treeview
  • Display all the messages from a particular private/public/deadletter queues in a Listview Control.
  • Ability to purge messages from a particular queue.
  • It can also save the Message contents to a particular XML file. Assuming the Message Body contains XML data.
  • It can send the selected single message to another queue, basically it copies a message from one queue to another.
  • Also, it can post/send a XML file content to a particular queue.


MSMQ XML Application:

The below screenshot will give you a basic picture on how this Application will work,

This Application was built using the following C# Windows Components,

  • Label Control   - to display the Current Machine Name
  • Tree View Control  - displays the list of private, public and deadletter queues in an hierarchical format.
  • List View Control - displays the Label and Message Id of all the Messages in a particular queue
  • Remove Selected Messages - Button which when clicked, removes the selected messages from a particular queue.
  • Check ALL  - Makes the checkbox selected for all messages.
  • UnCheck ALL  - Unchecks the selected messages.
  • Save Selected Message as XML - Select a single message in Listview and enter a valid xml file with path and click this button, message will get stored as XML file.
  • Post Selected Message to Queue - Select a particular message in Listview and enter a valid queue path and click this button, message will get posted to the entered queue. This is just a copy operation.
  • Post XML to selected Queue - select a particular in Treeview control and enter a valid xml file with path and click this button, XML data will get posted to the selected Queue with Message Label as "Test".

Now let us see the coding part of the individual components in detail,

Form_load code:

 private void Form1_Load(object sender, System.EventArgs e)
 {
 //display Current Machine Name
 txtMachine.Text  = "Queues From Machine: " + System.Net.Dns.GetHostName();
 strMachine= System.Net.Dns.GetHostName();
 //clear the treeview
 treeQueues.Nodes.Clear();
 //Add the machine name as Root Note
 nodeRoot = treeQueues.Nodes.Add(strMachine );
 //populate private queues
 populatePrivateQueueDetails();
 //populate public queues
 populatePublicQueueDetails();
 //populate non-transactional deadletter queues
 populateDLQDetails();
 //populate transactional deadletter queues
 populateTDLQDetails();  
 }

In the Form_load event, we display the Current Machine Name, Clear the TreeView nodes, and invoke individual methods which populates Private, Public & Deadletter queues.

populatePrivateQueueDeails Code:

private void populatePrivateQueueDetails()
{
//declare array of Messages
System.Messaging.MessageQueue[] arQueues;

try
{
 nodePrivate = nodeRoot.Nodes.Add("Private");
 //List the private queues on the machine
 arQueues = MessageQueue.GetPrivateQueuesByMachine(strMachine );
 foreach ( MessageQueue oQueue in arQueues )
 {
  //add the private queue name with path under Private Node
  nodePrivate.Nodes.Add(oQueue.MachineName + @"\" + oQueue.QueueName );
 }
 }catch(Exception e)
 {
  MessageBox.Show(e.Message);
 }
}

This method picks all the Private Queues(Queue Path) located in the current machine using GetPrivateQueuesByMachine Method and subsequently adds the same as a child node to a parent node named "Private".

populatePublicQueueDetails Code:

 private void populatePublicQueueDetails()
 {
  //declare array of Messages
  System.Messaging.MessageQueue[] arQueues;
  try
  {
   nodePublic = nodeRoot.Nodes.Add( "Public" );
   
   //List the public queues on the machine
   arQueues = MessageQueue.GetPublicQueuesByMachine(strMachine);

   foreach ( MessageQueue  oQueue in arQueues )
   {
    //add the public queues with path under Public Node
    nodePublic.Nodes.Add(oQueue.Path);
   }
  }
  catch(Exception e)
  {
   MessageBox.Show(e.Message);
  }
}

This method picks all the Public Queues(Queue Path) located in the current machine using GetPublicQueuesByMachine Method and subsequently adds the same as a child node to a parent node named "Public".

populateDLQDetails Code:

 private void populateDLQDetails()
 {
  nodeDLQ = nodeRoot.Nodes.Add(strMachine  + "\\Deadletter$");
  //Access the non-transactional deadletter queue(system queue)
  MessageQueue DLQ = new MessageQueue(strMachine  + "\\Deadletter$" );
 }

The above method creates a non-transactonal deadletter queue node(which is nothing but a System Queue).

populateTDLQDetails Code:

 private void populateTDLQDetails()
 {
  //MachineName\Deadletter$
  //MachineName\XactDeadletter$
  //Access the Transactional Deadletter queue
  nodeDLQ = nodeRoot.Nodes.Add(strMachine  + "\\XactDeadletter$");
  MessageQueue TDLQ = new MessageQueue(strMachine + "\\XactDeadletter$");
 }

The above method creates a transactonal deadletter queue node(which is also a System Queue).

Note: We cannot send messages to a System Queue, a message gets posted to a dead-letter queue whenever a source application cannot deliver a message to destination queue in a particular time period or due to some other failure.

Now we are done with populating the Treeview with private, public and dead-letter queues. Whenever user selects a particular Queue in Treeview, we need to display ALL the messages in a Listview control(with just 2 columns - Message Label and Message Id). Let us see the code behind part of this process,

TreeView code:

 private void treeQueues_AfterSelect(object sender, System.Windows.Forms.TreeViewEventArgs e)
 {
  if ( (e.Action == TreeViewAction.ByMouse) || (e.Action == TreeViewAction.ByKeyboard ))
  {
   //Clear the existing Messages from ListView
   lViewMessages.Items.Clear();
   //populate the Messages based on the Queue selected in Treeview
   populateMessages(e.Node.Text );
  }
 }

In the above Treeview select event, populateMessages Method is invoked for each and every queue click.

populateMessages Code:


 private void populateMessages(string strQueue)
 {
 
  //this method populates all the messages into Listview
  //based on the queue selected
  try
   {
   //open the selected queue
   MessageQueue popQ = new MessageQueue(strQueue);
   //read all the properties
   popQ.MessageReadPropertyFilter.SetAll();

   // get a message enumerator which can be used to go through all the messages
   MessageEnumerator Enumerator = popQ.GetMessageEnumerator();

   //clear listview contents and column headers
   lViewMessages.Items.Clear();
   lViewMessages.Columns.Clear();

   //create column headers for listview
   System.Windows.Forms.ColumnHeader ch1 = new ColumnHeader() ;
   ch1.Text = "Message Label";
   ch1.Width =lViewMessages.Width/2 ;
   lViewMessages.Columns.Add(ch1);

   //make the listview as checkbox type
   ch1.ListView.CheckBoxes=true;
    
   //add another column header for listview 
   System.Windows.Forms.ColumnHeader ch2 = new ColumnHeader() ;
   ch2.Text = "Message Id";
   ch2.Width =lViewMessages.Width/2 ;    
   lViewMessages.Columns.Add(ch2);    

   // loop through all the messages
   while (Enumerator.MoveNext(new TimeSpan( 0, 0, 1)))
   {
    // get a reference to the current message
    System.Messaging.Message Msg = Enumerator.Current;
    System.Windows.Forms.ListViewItem lvi = lViewMessages.Items.Add(Msg.Label );
    lvi.SubItems.Add (Msg.Id);
   }

   // close the message queue
   popQ.Close();
   }
   catch(Exception e)
   {
    MessageBox.Show(e.Message);
   }
  }

GetMessageEnumerator Method helps us to enumerate all the Messages for the selected Queue. We are also trying to create headers for Listview and make it a Checkbox enabled Listview. In the above code, we enumerate the Messages and add the Message Label as the Listview Item and Message Id as Listivew SubItem.

Listview Selected Item Code:

Using the below code, we try to identify the selected item in an Listview.

private void lViewMessages_SelectedIndexChanged(object sender, System.EventArgs e)
 {
  foreach ( System.Windows.Forms.ListViewItem lsel in lViewMessages.SelectedItems )
  {
   //find out the selected message index and message id value
   iselIndex = lsel.Index;
   strSelId = lsel.SubItems[1].Text  ;
  }
   
 }

Remove Selected Messages Code:

The button click event code takes care of popping/removing selected messages from a particular queue. Since, Message Id is unique for each and every message, the same is used for finding out the selected message. Please note that the below code will work only for removing messages one by one.

 private void cmdPurge_Click(object sender, System.EventArgs e)
 {
  MessageQueue popQ = new MessageQueue(treeQueues.SelectedNode.Text  );

  popQ.MessageReadPropertyFilter.SetAll();

  // get a message enumerator we can use to go through all the messages
  MessageEnumerator Enumerator = popQ.GetMessageEnumerator();
  
  // loop through all the messages
  while (Enumerator.MoveNext(new TimeSpan(0, 0, 1)))
  {
   // get a reference to the current message
   System.Messaging.Message Msg = Enumerator.Current;

   foreach ( System.Windows.Forms.ListViewItem lsel in lViewMessages.CheckedItems  )
   {
   strSelId = lsel.SubItems[1].Text  ;
   iselIndex = lsel.Index;

   if (Msg.Id.ToString().Equals(strSelId))
   {
    popQ.ReceiveById ( Msg.Id.ToString());  
    lViewMessages.Items.RemoveAt(iselIndex);
   }    
   }
  }

  // close the message queue
  popQ.Close(); 
  }

Check ALL Code:

Mark the checkbox selected for ALL messages.

Mark the checkbox selected for ALL messages.

 private void cmdSelectAll_Click(object sender, System.EventArgs e)
 {
  foreach ( System.Windows.Forms.ListViewItem lsel in lViewMessages.Items  )
  {
   //check all the items in the listview
   lsel.Checked =true;
  }
 }

UnCheck ALL Code:

UnCheck ALL the items of the checkbox.
 
 private void cmdUnCheck_Click(object sender, System.EventArgs e)
  {
   foreach ( System.Windows.Forms.ListViewItem lsel in lViewMessages.Items  )
   {
    //uncheck all the items in the listview
    lsel.Checked =false;
   }  
  }

Save Selected Message as XML:

 private void cmdSaveAs_Click(object sender, System.EventArgs e)
 {

  foreach ( System.Windows.Forms.ListViewItem lsel in lViewMessages.CheckedItems )
  {
   //find out the selected message index and message id value
   iselIndex = lsel.Index;
   strSelId = lsel.SubItems[1].Text  ;
  }
   
  //pass the selected message id to retrievemessageDetails
  if (iselIndex >= 0 ) retrieveMessageDetails(strSelId );  
   
  MessageBox.Show("Messave stored into xml file");

 }

The above event finds out the selected message first and call the retrieveMessageDetails Method.

retrieveMessageDetails Code:


 
private void retrieveMessageDetails(string strSel)
 {
  //open the Queue
  MessageQueue popQ = new MessageQueue(treeQueues.SelectedNode.Text);
   
  //set the Queue Formatter to ActiveXMessageFormatter
  //as we are trying to retrieve an XML file from the queue
  popQ.Formatter = new  ActiveXMessageFormatter();
   
  //set this property to read all properties
  popQ.MessageReadPropertyFilter.SetAll();

  // get a message enumerator we can use to go through all the messages
  MessageEnumerator Enumerator = popQ.GetMessageEnumerator();
  
  // loop through all the messages
  while (Enumerator.MoveNext(new TimeSpan(0, 0, 1)))
  {
   // get a reference to the current message
   System.Messaging.Message Msg = Enumerator.Current;
   
   if (Msg.Id.Equals(strSel))
   {
   try
   {
    //set the Message Formatter to ActiveMessageFormatter type
    Msg.Formatter = new ActiveXMessageFormatter ();           
    object myObject;
    //type cast the message Body to Object Type
    myObject = (object) Msg.Body;

    System.Xml.XmlDocument xmlDoc = new System.Xml.XmlDocument();
    //load the xml string using XmlDOCument
    xmlDoc.LoadXml (myObject.ToString());
    //save the xml file to a specified folder
    xmlDoc.Save(txtSaveAs.Text  ); 

   }
   catch(Exception e )
   {
    MessageBox.Show(e.StackTrace.ToString());
   }
  }    
 }

In the above method, we enumerate the Messages and find out the selected message based on Message Id. Message body should be serialized as an XML content, and  hence ActiveMessageFormatter type is applied. XMLDocument object is used to load the Message Body as XML and stores the XML content to an XML file name.

Post Selected Message to Queue Code:

This method just copy/post selected message from one queue to another queue(entered in textbox).


 private void cmdPostMsg_Click(object sender, System.EventArgs e)
 {

  try
  {

  MessageQueue msgQ = new MessageQueue( txtQueue.Text );
  MessageQueue popQ = new MessageQueue(treeQueues.SelectedNode.Text  );

  popQ.MessageReadPropertyFilter.SetAll();

  // get a message enumerator we can use to go through all the messages
  MessageEnumerator Enumerator = popQ.GetMessageEnumerator();
  
  // loop through all the messages
  while (Enumerator.MoveNext(new TimeSpan(0, 0, 1)))
  {
   // get a reference to the current message
   System.Messaging.Message Msg = Enumerator.Current;
   //Msg.Formatter = new System.Messaging.XmlMessageFormatter(
   // new string[] {"System.String"});


   foreach ( System.Windows.Forms.ListViewItem lsel in lViewMessages.CheckedItems  )
   {
    strSelId = lsel.SubItems[1].Text  ;
    iselIndex = lsel.Index;

    if (Msg.Id.ToString().Equals(strSelId))
    {
     msgQ.Send(Msg);  
    }    
   }
  }

  // close the message queue
  popQ.Close();  
 
  MessageBox.Show("Post Successful!");

  }
  catch(Exception e1)
  {
   MessageBox.Show(e1.Message);
  }  
 }

Post XML File to Selected Queue Code:

In this event, we load the entered XML file into an XMLDOcument object and will post the XML content to a Queue selected using Treeview control.

 private void cmdPostXML_Click(object sender, System.EventArgs e)
 {
  //open the destination queue
  MessageQueue msgQ = new MessageQueue( treeQueues.SelectedNode.Text );  
  System.Messaging.Message msg = new System.Messaging.Message();
  System.Xml.XmlDocument xmlDoc = new System.Xml.XmlDocument();
  //load the xml string using XmlDOCument
  xmlDoc.Load(txtPostXML.Text ); 
  msg.Label = "Test";
  //frame the message contents
  msg.Body =xmlDoc.InnerXml.ToString();
  //post the xml message to queue
  msgQ.Send(msg);
  MessageBox.Show("Post Successful!");
 }

Now we are completely done and ready to use this application. We can do some custom changes to the above code and can very well use it as generalized Tool for any MQ based application.

Where this Application can be used?

It can be used in real-time application where you have a database which is in a remote location and a scheduled service/job which picks up data from this database, frame an XML file and sends it to an MQ, your listener service will pick up the XML data from MQ and take it forward for further processing. Now that you have your XML data in your hand, you can very well consume/customize it based on your requirement.

Please download the complete working version of this code from the below link,

http://www.eggheadcafe.com/fileupload/-190104164_MSMQApp.zip


References

http://www.codeproject.com/csharp/mgpMyQueue.asp

Interacting MSMQ with C#
http://asptoday.com/Content.aspx?id=774

MSMQ, your reliable message processing
http://www.c-sharpcorner.com/Code/2004/July/MSMQ.asp

Biography

I am a software developer and have worked on Microsoft technologies for about eight years now. I have always been fascinated by Microsoft technologies and with the advent of .NET , this fascination has reached new heights. I take a lot of interest in reading technical articles and equally enjoy writing them.

By sundar k   Popularity  (22016 Views)