Click here to Skip to main content
Click here to Skip to main content

Using MSMQ , Backgroundworker Threads in C#

By , 29 Mar 2012
Rate this:
Please Sign up or sign in to vote.

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;
//Not drag droped from the toolbox, created in runtime.

public Form1()
{
    InitializeComponent();

    this.bgwPopMSMQ = new System.ComponentModel.BackgroundWorker();
    //To Stop the thread the thread should
    //be provided with  WorkerSupportsCancellation as true;
    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);//Pushing the number to MyTestQueue
        bgwRandomData.ReportProgress(0, (object)strMessage);
        //provides the message for the Testbox, Graph

        System.Threading.Thread.Sleep(1000);//waiting for 1 Sec
        if (bgwRandomData.CancellationPending)//checking for the cancellation
        {
            e.Cancel = true;
            break;
        }
    }
}

private void bgwRandomData_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    int a = e.ProgressPercentage;//for this application dont consider the progress percentage
    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)
{
    //Checking whetere the MyTestQueue exists, if not create it.
    if (MessageQueue.Exists(@".\Private$\MyQueueTest"))
        objMessageQueue = new System.Messaging.MessageQueue(@".\Private$\MyQueueTest");
    else
        objMessageQueue = MessageQueue.Create(@".\Private$\MyQueueTest");

    objMqMessage.Body = strMessage;// Assigning the value to message
    objMqMessage.Label = "MYDATA";// Assigning the Label to message
    if (objMqMessage != null)
    {
        objMessageQueue.Send(objMqMessage);//Storeing the value in //Queue
    }
}

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:

//For cross thread operations
textBox2.Invoke((MethodInvoker)delegate()
{
     textBox2.Text = m_strmsg;
});
//end For cross thread operations

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 Queue exists 
    if (MessageQueue.Exists(@".\Private$\MyQueueTest"))
    {
        objMessageQueue = new System.Messaging.MessageQueue(@".\Private$\MyQueueTest");
        objMessageQueue.SetPermissions("Everyone", MessageQueueAccessRights.FullControl);
    }
    else// else create the Queue
    {
        objMessageQueue = MessageQueue.Create(@".\Private$\MyQueueTest");
         objMessageQueue.SetPermissions("Everyone", MessageQueueAccessRights.FullControl);
    }

    // Purge all the Old data
    objMessageQueue.Purge();

    while (true)
    {
        try
        {
            {
                byte[] bt = new byte[10];
                objMessage = objMessageQueue.Receive();//Receive the first message available in the Queue
                if (objMessage.Label.ToUpper() == "MYDATA")//checks for the lable with name "MYDATA"
                {
                    //sets the formatter used to deserialize an object from the message body
                    objMessage.Formatter = new XmlMessageFormatter(new String[] { "System.String,mscorlib" });
                    m_strmsg = objMessage.Body.ToString();
                    DrawTheGraph(ref pictureBox2, m_strmsg);
                    //For cross thread operations
                    textBox2.Invoke((MethodInvoker)delegate()
                    {
                        textBox2.Text = m_strmsg;
                    });
                    //end For cross thread operations
                    }

            }
            if (bgwPopMSMQ.CancellationPending)//checking for the cancellation
            {
                e.Cancel = true;
                break;
            }
        }
        catch (Exception Ex)
        {
        }
    }
}

When the two BackgroundWorkers 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. 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Vinay Swa

United States United States
No Biography provided

Comments and Discussions

 
Suggestionsome suggestion Pinmemberjasper168885-Sep-12 22:18 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140415.2 | Last Updated 30 Mar 2012
Article Copyright 2012 by Vinay Swa
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid