Accessing Message Queues
To perform a task asynchronously is to execute it without waiting for a result. This type of processing allows you to start a time-consuming process and continue working without having to wait for tha
November 14, 2001
To perform a task asynchronously is to execute it without waiting for a result. This type of processing allows you to start a time-consuming process and continue working without having to wait for that process to finish executing.
Asynchronous processing is ideal to use in many situationsespecially on the Web, where a user's request could take a long time to process, but you want to provide a response back to the user immediately. By handling the user's request asynchronously, your system can respond regardless of how long that request may actually take to execute.
There are many ways to add asynchronous processing to your Microsoft .NET application. For example, you could take advantage of the workflow features of Microsoft Exchange 2000 or Microsoft BizTalk Server. You could use COM+ Queued Components through COM interop, or you could work directly with Message Queuing (MSMQ). An upcoming Architectural Topics article will compare these alternatives in depth.
MSMQ, which is used by both BizTalk and COM+ Queued Components, is a part of the Microsoft Windows NT 4.0 Option Pack and the server versions of Microsoft Windows 2000. On Windows NT 4.0 Workstation, Windows 2000 Professional, and Windows XP Professional, you can use MSMQ for accessing local private queues, as is done in this article. Through MSMQ, your application can place data into a queue, where it will be maintained until an application (either a different application, or the same one that placed the data there) retrieves it. By placing a message representing a task (for example, an order to be processed) into a queue rather than actually processing the task synchronously, your main system is only delayed by the length of time required to post to the queue. Any number of different systems can post messages to a queue, and any number can be used to retrieve and process those same messages, providing scalability to your application.
In this article, we will show how you can use the System.Messaging classes provided by the .NET Framework to work with MSMQ and add asynchronous workflow to your application.
As mentioned earlier, MSMQ is available for both Windows NT 4.0, Windows 2000, and Windows XP Professional, but for our examples we will assume that you have a Windows 2000 server running with MSMQ installed. The examples in this article assume this server is the same machine that is running your .NET code. However, in a real system your code may be connecting to message queues located on a separate server.
Working with Queues
You have to be able to specify a message queue before you can use it. To do this, you need a way to describe a queue uniquely and consistently in your applicationsand .NET provides three different ways to access a specific queue.
Specify a queue by its path
The first method, is through the queue's path
(private$, liquidsoapprivate$Orders),
which specifies the actual server name (or "." for the local server) and the full path to the queue.
Specify a queue by format name
The second option is the Format Name, which is a string that describes the queue using some connection details and the queue's path (DIRECT=OS:liquidsoapprivate$Orders) or using a special globally unique identifier (GUID) that uniquely identifies the message queue.
Specify a queue by label
Finally, the third method of specifying a queue is by using the queue's label ("My Orders"), which is a value that you can assign through code or through the MSMQ administration interface.
Using either the label or the path methods causes a bit of overhead, because MSMQ must resolve those descriptions into the Format Name that it uses internally to describe individual queues. Using a Format Name directly avoids the name resolution, making it a more efficient method, and is actually the only way you can refer to a queue if you want your client application to be able to function when a queue is offline.
Note Offline use of message queues is not covered in this article. See the MSMQ section of the Platform SDK for comprehensive coverage of message queuing.
After specifying the queue by name, you have to create an instance of the System.Messaging.MessageQueue object that will represent the queue. The code to follow, which assumes you have included "Imports System.Messaging" at the top of your code, creates a new instance of the MessageQueue object, using the queue's path to identify it:
Private Const QUEUE_NAME As String = ".private$Orders"
Dim msgQ As New MessageQueue(QUEUE_NAME)
Note See Example 1 in the complete BDAdotNetAsync1.vb sample code (see top of article for download).
Note the use of "." in the queue's path, which specifies the local machine, instead of using the actual server name. As long as you have a private queue named "Orders" defined in MSMQ on your machine, this code should successfully obtain an object reference to that queue. For a more robust application, you could check if the queue exists and automatically create it if it does not, although performing this type of check is relatively time-consuming.
Creating Queues Programmatically
Although the MSMQ interface is available for creating, deleting, and exploring message queues, you can also work with queues through the System.Messaging namespace. Several static methods (meaning you can call them without creating an actual instance of the class) have been provided on the MessageQueue class that give you the ability to check if a queue exists (Exists), create a queue (Create), and delete a queue (Delete). Using these methods, your application can check for the existence of a queue, and create it automatically if it does not exist. The function, GetQ, listed next provides the exact functionality just described:
Private Function GetQ(ByVal queueName As String) As MessageQueue
Dim msgQ As MessageQueue
'Create the queue if it does not already exist
If Not MessageQueue.Exists(queueName) Then
Try
'Create the message queue and the MessageQueue object
msgQ = MessageQueue.Create(queueName)
Catch CreateException As Exception
'Error could occur creating queue if the code does
'not have sufficient permissions to create queues.
Throw New Exception("Error Creating Queue",
CreateException)
End Try
Else
Try
msgQ = New MessageQueue(queueName)
Catch GetException As Exception
Throw New Exception("Error Getting Queue", GetException)
End Try
End If
Return msgQ
End Function
Note See GetQ in the complete BDAdotNetAsync1.vb sample code (see top of article for download).
This type of code can simplify deployment because it removes the need to create any queues ahead of time, but the relative expense of calling Exists means that you will achieve better performance if the queue is created by the install, and your program can just assume it exists. It is also worth noting that your receiving code would also have to check for the existence of the desired queue, since it may not exist until after at least one message has been placed into it.
Note Creating and deleting queues requires certain security permissions, and by default can only be done by the creator/owner of the queue or by an administrator. If you do not have sufficient permissions an exception will be raised with an "Access Denied" message.
Deleting Queues
The remaining static method that you will find useful is Delete, which takes a path to a queue, just like its two companions, Create and Exists, and in this case uses that path to find a queue and remove it from the server. This is, of course, a rather drastic action because it will destroy any messages currently stored inside that queue that could result in the loss of important data. To ensure a queue is successfully deleted, place the call to Delete within an error-handling structure similar to the one shown here:
Try
' Delete the specified queue
MessageQueue.Delete(queuePath)
Catch e As Exception
' Throw exception to caller
Throw New Exception("Deletion Failed", e)
End Try
Note See Example 2 and DeleteQ in the complete BDAdotNetAsync1.vb sample code (see top of article for download).Sending Messages
The System.Messaging namespace provides both a simple and a complex method for sending messages. The difference between the two methods is not in the messages themselves, but in the degree of control each method provides over the message format and delivery. Using either method, you can send any type of object you wish; the examples in this article will send Strings, DataSets, and even a class that is declared within the example code.
Simple Method
To send a message to a queue using the simple method requires only a few steps: First, obtain a reference to the appropriate message queue (using the queue's path, format name, or label), and then use the MessageQueue object's Send method, supplying the object you wish to send and (if desired) a label for your message. That's it. All the options that control how a message is sent will use their default values, so you don't have to do anything else. The code shown next leverages the GetQ procedure (described earlier in this article) and sends a simple string message to a local private queue:
Private Const QUEUE_NAME As String = ".private$Orders"
Dim msqQ As MessageQueue
Dim msgText As String
' Prepare a text messagemsgText = String.Format("Sample Message Sent At {0}", DateTime.Now())Try
' Get a MessageQueue object for the queue
msqQ = GetQ(QUEUE_NAME)
' Send the message to the queue
msqQ.Send(msgText)
Catch e As Exception
' Handle the error
End Try
Note See Example 3 in the complete BDAdotNetAsync1.vb sample code (see top of article for download).
To specify a label for your message, a second parameter can also be included in your call to the Send method:
msgQ.Send(sMessage, "Sample Message")
Using the MSMQ explorer in Windows NT or the Computer Management tools in Windows 2000 and XP, you can view the queues that exist on your machine and inspect the individual messages in each queue. If you assigned a label to your message, that label will appear in the messages view; otherwise, the label area will appear blank.
Figure 1. MSMQ Window from Windows 2000 Computer Management
To view the contents of an individual message, right-click the message, click Properties, and then click the Body tab. In the case of a simple send, with all the options set to their default, whatever object you sent will have been serialized into XML, similar to the sample shown next (and in Figure 2):
Sample Message Sent At 5/25/2001 3:51:48 PM
Figure 2. Message body serialized into XML
Note XML is the default format for sending messages, but it is possible to choose a different format. Changing the formatting used to serialize a message is covered in the Complex Method section.Any object can be sent using this method simply by passing that object in to the Send method. Whatever you provide as an argument will be serialized into XML (see the preceding note) and placed into the body of the message. In addition to basic data types, such as String, Integer, and Double, two other types of objects are commonly sent through MSMQ: DataSets and application-specific object classes created by the user. An example of sending both of these objects is provided next, showing how easy it is to send any type of information using the System.Messaging classes.
To send a DataSet, all you really need to do differently is have an instance of a DataSet created and pass it in to the MessageQueue object's Send method. Once again, just as with the String sent earlier, the DataSet will be serialized into XML and placed into the body of the message:
About the Author
You May Also Like