Introduction
This article covers MSMQ functionality exposed through the System.Messaging
namespace (Installing MSMQ,
creating a Message Queue, Messages, and Pushing, Popping Messages from MSMQ), and the BackgroundWorker
functionality (how to initiate a thread and pass parameters
to the BackgroundWorker
, keeping track of progress and cancel the background process while it's running).
Why MSMQ (Microsoft Message Queue server)?
Message queuing is used in scenarios where we need a failsafe mechanism while two processes communicate with each other. Microsoft has provided MSMQ for implementing
message queues. MSMQ is essentially a messaging protocol that allows applications running on disparate servers to communicate in a failsafe manner.
A queue is a temporary storage where one process can store messages and the other process can retrieve those. This ensures that messages are not lost
even if the systems are not connected for some time period.
Prerequisite: Setting up MSMQ
Ensure that you have your Windows CD. Simply open the Control Panel, "Add/Remove Windows components", and ensure the Message Queuing
Services checkbox is checked. Message queuing must be installed on both the sending (client) and receiving machine (server).
After this step is completed, you will be able to create a private message queue. A private queue is for "workgroup installations", meaning the computer is not
integrated in the Active Directory. There are some limitations that exist in private queues - they are not published whereas public queues are.
Creating a Queue
Open up the Microsoft Management Console, Add/Remove the "Computer Management" snap-in, browse to "Services and Applications", and expand and select
the "Message Queuing" node. Right click on "Private Queues" and create a new queue:
There are different types of queues as mentioned in the table below:
Queue Type | Description |
Public queue | Registered in directory services, can be located by any Message Queuing application. |
Private queue | Registered on local machine, typically cannot be located by other applications. |
System Queue | These are system level queues. |
Under each queue, you will find three items:
- Queue messages: Shows you all messages currently sitting in the queue. You can view the properties of each message but you cannot change it. You cannot create a
new message or delete an individual message. You can remove all messages by right clicking on "Queue Messages" and selecting "All Tasks | Purge".
- Journal Messages: When you create a queue, you can also enable a "Journal". This means when a message is received/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.
- Triggers: Allows you to register a trigger to be called when a message is placed into this message queue. When setting up the trigger, you can specify whether a COM component
is called or an external executable.
Usage of MSMQ
For this example, I had worked with only one application which pushes the data to the MSMQ and pops the data from
MSMQ. For this I had used two BackgroundWorker
threads. One thread is used to create and push the message for MSMQ and the other thread to pop and display
the data from the Queue.
There are a few simple steps to get started with the MSMQ. Add the System.Messaging
reference to the Solution.
Then create the MessageQueue
(provides access to a queue on a message queuing server),
Message
(provides access to the properties needed to define a message queuing message)
objects.
static System.Messaging.MessageQueue objMessageQueue;
static System.Messaging.Message objMqMessage = new System.Messaging.Message();
The BackgroundWorker
component gives you a way to run a time-consuming task on a separate thread. Actually it works the same way as the asynchronous
delegates, but in the asynchronous delegate approach, you have to consider some issues about working with your UI elements,
because they are running on another thread. In BackgroundWorker
, marshalling issues are abstracted away with an event-based model.
For BackgroundWorker
threads, one thread (bgwRandomData
) is used from the toolbox controls:
And the other is created at Runtime (bgwPopMSMQ
) (within the code):
private BackgroundWorker bgwPopMSMQ;
public Form1()
{
InitializeComponent();
this.bgwPopMSMQ = new System.ComponentModel.BackgroundWorker();
bgwPopMSMQ.WorkerSupportsCancellation = true;
this.bgwPopMSMQ.DoWork += new DoWorkEventHandler(bgwPopMSMQ_DoWork);
this.bgwPopMSMQ.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(bgwPopMSMQ_RunWorkerCompleted);
}
Using the BackgroundWorker
For the BackgroundWorker
thread that dropped from the Toolbox (Thread 1) is renamed as
bgwRandomData
, and the WorkerReportsProgress
is
changed to “True” to provide the report progress updates, WorkerSupportsCancellation
is changed to “True” for cancelling
the asynchronous operation.
Now the next step is to create these methods:
DoWork
: This is the main method which is responsible to handle the large operation.ProgressChanged
: This method reports the change in the progress of the operation performed by the background
worker DoWork
method.RunWorkerCompleted
: This method checks RunWorkerCompletedEventArgs
and performs action accordingly.
The BackgroundWorker
thread “bgwRandomData
” generates
a random data string (with 10 numbers) and pushes the message (with
label “MYDATA”) to the myqueuetest
Queue and displays the same data for the textbox,
Graph
.
private void bgwRandomData_DoWork(object sender, DoWorkEventArgs e)
{
Random rnd = new Random();
while (true)
{
string strMessage = string.Empty;
for (int i = 0; i < 10; i++)
{
strMessage = strMessage + (rnd.Next(0, 10)).ToString() + ",";
}
PushIntoQueue(strMessage);
bgwRandomData.ReportProgress(0, (object)strMessage);
System.Threading.Thread.Sleep(1000);
if (bgwRandomData.CancellationPending)
{
e.Cancel = true;
break;
}
}
}
private void bgwRandomData_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
int a = e.ProgressPercentage;
string strMessage = e.UserState.ToString();
DrawTheGraph(ref pictureBox1, strMessage);
textBox1.Text = strMessage;
}
When the bgwRandomData
thread is stopped, the Runworkercompleted
method is invoked:
private void bgwRandomData_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("BackGroundWorker Thread of Pushing is stopped");
}
This is the code to push the message for the MSMQ:
void PushIntoQueue(string strMessage)
{
if (MessageQueue.Exists(@".\Private$\MyQueueTest"))
objMessageQueue = new System.Messaging.MessageQueue(@".\Private$\MyQueueTest");
else
objMessageQueue = MessageQueue.Create(@".\Private$\MyQueueTest");
objMqMessage.Body = strMessage;
objMqMessage.Label = "MYDATA";
if (objMqMessage != null)
{
objMessageQueue.Send(objMqMessage);
}
}
In this, first check for the Queue existence, if Queue does not exist, then create the Queue, else the existing Queue is assigned for objMessageQueue
(Queue object).
Next, assign the string to the body of the message, and update the label as “MYDATA”, then send for the MSMQ.
The BackgroundWorker
thread bgwPopMSMQ
pops up the data from the Queue and displays for the Textbox, Graph. For this thread, the
ProgressChanged
event is not handled (intentionally). Because of this, when trying to update the textbox with text directly, the cross thread operation is observed. To
eliminate the cross thread operation, the code related to the textbox is modified as:
textBox2.Invoke((MethodInvoker)delegate()
{
textBox2.Text = m_strmsg;
});
From a BackgroundWorker
thread, if any UI element needs to be updated,
a better way is to use the ProgressChanged
event. The advantage
of the BackgroundWorker
thread compared to asynchronous delegates is
the marshalling issues are abstracted away with an event-based model.
void bgwPopMSMQ_DoWork(object sender, DoWorkEventArgs e)
{
MessageQueue objMessageQueue = null;
System.Messaging.Message objMessage = new System.Messaging.Message();
string m_strmsg = string.Empty;
if (MessageQueue.Exists(@".\Private$\MyQueueTest"))
{
objMessageQueue = new System.Messaging.MessageQueue(@".\Private$\MyQueueTest");
objMessageQueue.SetPermissions("Everyone", MessageQueueAccessRights.FullControl);
}
else
{
objMessageQueue = MessageQueue.Create(@".\Private$\MyQueueTest");
objMessageQueue.SetPermissions("Everyone", MessageQueueAccessRights.FullControl);
}
objMessageQueue.Purge();
while (true)
{
try
{
{
byte[] bt = new byte[10];
objMessage = objMessageQueue.Receive();
if (objMessage.Label.ToUpper() == "MYDATA")
{
objMessage.Formatter = new XmlMessageFormatter(new String[] { "System.String,mscorlib" });
m_strmsg = objMessage.Body.ToString();
DrawTheGraph(ref pictureBox2, m_strmsg);
textBox2.Invoke((MethodInvoker)delegate()
{
textBox2.Text = m_strmsg;
});
}
}
if (bgwPopMSMQ.CancellationPending)
{
e.Cancel = true;
break;
}
}
catch (Exception Ex)
{
}
}
}
When the two BackgroundWorker
s are running, the first thread pushes the data for the MSMQ and
at the same time, the data
is displayed for the textbox1, Graph1, and the second thread pops the data from MSMQ and displays for the textbox2, Graph2.