Referencing Queues
To send a message to a queue, the sending application must first provide the queue location. MSMQ provides several different methods for identifying queues; the most commonly used queue references are the path name and the direct format name. You commonly use path names to reference public queues, and local private queues. Because they query AD for queue information, you can't use path names to reference private queues located on remote machines. Instead, developers commonly use direct format names to access remote private queues. Direct format names provide a means of interacting with queues directly, avoiding the need for MSMQ to query AD. This fact makes using direct format names the preferred method of accessing queues of all types, because not only does the queuing infrastructure become more robust, but message throughput increases considerably.
Both pathnames and direct format names use similar formats. The first portion of the reference specifies the location of the queue by either specifying the machine name or the network address of the server hosting the queue. The second portion of the reference is the queue name, which can include an optional attribute to specify that the queue is private. Table 1 shows several examples of queue references.
Table 1. Sample pathnames and direct format names for both public and private queues.
| Path Names | |
| .\DevXTestQueue | References a public queue named DevXTestQueue on the local machine. |
| server01\DevXTestQueue | References a public queue named DevXTestQueue on a machine named server01 (local queues can be referenced with a machine name as well). |
| .\private$\DevXTestQueue | References a private queue named DevXTestQueue on the local machine. |
| server01\private$\DevXTestQueue | References a private queue named DevXTestQueue on a machine named server01. |
| Direct Format Names | |
| Formatname:DIRECT=OS:.\DevXTestQueue | References a public queue named DevXTestQueue on the local machine. |
| Formatname:DIRECT=OS:server01\DevXTestQueue | References a public queue named DevXTestQueue on a machine named server01. |
| Formatname:DIRECT=TCP:127.0.0.1\private$\DevXTestQueue | References a private queue named DevXTestQueue on the local machine. |
Developer-friendly Application Features
MSMQ provides several common features that you'll use in most projects. These features have been part of MSMQ from the beginning; without them, MSMQ developers would probably need to build these features into a "wrapper" or management application to provide the same functionality.
The CorrelationID Property
You use the CorrelationId property of a message to correlate two or more messages with one another. The typical usage scenario involves a client sending a request to a server for processing, and the server sending a message to the client when processing is complete. If this process is managed asynchronously, you will need a way to correlate responses with their respective requestsenter the CorrelationId property. In the scenario above, the client would retrieve the MSMQ-assigned ID property of the message being sent, and store it in an application-specific state table. On the server side, as the application creates response messages, it can set the CorrelationId of the response message to the value of the ID property of the request message. When the client application receives response messages from the server, it compares the CorrelationId property of the response message to the value of the message ID it had stored previously. The primary value of the CorrelationId is in correlating requests and responses.
In the code sample below, the client stores the original ID of the request message. The server code uses the request message's ID property to set the CorrelationId property of the response message. Then, when the client receives the response message, it uses the original request message ID to match the response message CorrelationId being returned from the server process.
For example, here's a client implementation that sends a request.
Private Sub SendRequestMessage(ByVal InternalState As String)
Dim RequestQueue As System.Messaging.MessageQueue
Dim RequestMessage As System.Messaging.Message
Dim MessageId As String
' open send queue
RequestQueue = GetRequestQueue()
' create message and set properties
RequestMessage = New Message
RequestMessage.Body = GetRequest(InternalState)
MessageId = RequestMessage.Id()
' send message
RequestQueue.Send(RequestMessage)
' store message state information
StoreReqeust(InternalState, MessageId)
End Sub
This server implementation sets the response message's CorrelationId when processing the request. Private Sub SendResponse(ByVal RequestMessage As Message, _
ByVal Response As String)
Dim ResponseMessage As New System.Messaging.Message(Response)
Dim ResponseQueue As System.Messaging.MessageQueue
ResponseQueue = GetResponseQueue()
' set the response message correlationId
ResponseMessage.CorrelationId = RequestMessage.Id
ResponseMessage.Body = Response
' send the response message
ResponseQueue.Send(ResponseMessage)
End Sub
Finally, here's how the client implementation would handle the response. Private Sub NewMessageArrived(ByVal ResponseMessage As Message)
Dim InternalState As String
InternalState = LookupRequest(ResponseMessage.CorrelationId)
ProcessResponse(InternalState, ResponseMessage)
End Sub
The ResponseQueue Property
MSMQ provides an easy mechanism to correlate requests with responses, butassuming this is a fault-tolerant solution running on multiple machineshow does the server know where to send response messages? You could make the response location a property of the message payload, incorporating it into the design of the message you are sending; however, proper separation of the payload from the delivery mechanism prohibits this form of mingling. To make this easier, MSMQ provides a ResponseQueue property on the Message object that you can use to provide the response location. Building on the scenario shown in the preceding code fragments, the client application would set the ResponseQueue property of the request message to specify the queue to which response messages should be sent. When the server is ready to send the response message, it opens the queue specified in the ResponseQueue property of the request message, and sends the response message to it.
When reading through the last code sample demonstrating the use of the CorrelationId property, did you wonder how the GetResponseQueue() function might be implemented? The following code sample shows how to implement the same code using the ResponseQueue property. It's an evolution of the previous code sample, demonstrating the client setting the ResponseQueue property of the request message, and the server using that ResponseQueue property as the destination queue for the response message. Note that this doesn't affect client processing of the response message.
Here's the client implementation for sending a request.
Private Sub SendRequestMessage(ByVal InternalState As String)
Dim RequestQueue As System.Messaging.MessageQueue
Dim ResponseQueue As System.Messaging.MessageQueue
Dim RequestMessage As System.Messaging.Message
Dim MessageId As String
' open request and response queues
RequestQueue = GetRequestQueue()
ResponseQueue = New MessageQueue(".\DevXResponseQueue")
' create message and set properties
RequestMessage = New Message
RequestMessage.Body = GetRequest(InternalState)
RequestMessage.ResponseQueue = ResponseQueue
MessageId = RequestMessage.Id()
' send message
RequestQueue.Send(RequestMessage)
' store message state information
StoreReqeust(InternalState, MessageId)
End Sub
And here's the server implementation for processing the request. Private Sub SendResponse(ByVal RequestMessage As Message, _
ByVal Response As String)
Dim ResponseMessage As New System.Messaging.Message(Response)
Dim ResponseQueue As System.Messaging.MessageQueue
' the response queue is retrieved from the request message
ResponseQueue = RequestMessage.ResponseQueue
' set message properties
ResponseMessage.CorrelationId = RequestMessage.Id
ResponseMessage.Body = Response
' send the response message
ResponseQueue.Send(ResponseMessage)
End Sub
Finally, here's the client implementation for handling the response. Private Sub NewMessageArrived(ByVal ResponseMessage As Message)
Dim InternalState As String
InternalState = LookupRequest(ResponseMessage.CorrelationId)
ProcessResponse(InternalState, ResponseMessage)
End Sub
The TimeToBeReceived and TimeToReachQueue Properties ' Client implementation for sending the request.
Private Sub SendRequestMessage(ByVal InternalState As String)
Dim RequestQueue As System.Messaging.MessageQueue
Dim ResponseQueue As System.Messaging.MessageQueue
Dim RequestMessage As System.Messaging.Message
Dim MessageId As String
' open the request and response queues
RequestQueue = GetRequestQueue()
ResponseQueue = New MessageQueue(".\DevXResponseQueue")
' create the message and set message properties
RequestMessage = New Message
RequestMessage.Body = GetRequest(InternalState)
RequestMessage.ResponseQueue = ResponseQueue
RequestMessage.TimeToBeReceived = New TimeSpan(0, 1, 0)
MessageId = RequestMessage.Id()
' send the message
RequestQueue.Send(RequestMessage)
' store the message state details
StoreReqeust(InternalState, MessageId)
End Sub
' Server Implementation -- Processing the Reqeust
Private Sub SendResponse(ByVal RequestMessage As Message, _
ByVal Response As String)
Dim ResponseMessage As New System.Messaging.Message(Response)
Dim ResponseQueue As System.Messaging.MessageQueue
' the response queue is retrieved from the request message
ResponseQueue = RequestMessage.ResponseQueue
' set message properties
ResponseMessage.CorrelationId = RequestMessage.Id
ResponseMessage.Body = Response
' send the response message
ResponseQueue.Send(ResponseMessage)
End Sub
' Client Implementation -- Handling the Response
Private Sub NewMessageArrived(ByVal ResponseMessage As Message)
Dim InternalState As String
InternalState = LookupRequest(ResponseMessage.CorrelationId)
ProcessResponse(InternalState, ResponseMessage)
End Sub
MSMQ Administration in .NET
MSMQ provides surprising simple management of MSMQ queues. You can can create, delete, and purge queues with little effort. Further, you can set many queue properties programmatically, including permissions assignment.
Creating, Deleting, and Purging Queues
To create a queue, simply call the shared CreateQueue method, passing the pathname identifying the queue. Similarly, you can delete and purge (remove all messages) queues . See the code samples below.
' create a private queue on the local machine named DevXTestQueue
MessageQueue.Create(".\private$\DevXTestQueue")
' delete the private queue on the local machine
' named DevXTestQueue
MessageQueue.Delete(".\private$\DevXTestQueue")
' purge the private queue on the local machine
' named DevXTestQueue
MessageQueue.Purge(".\private$\DevXTestQueue")
Testing for the Presence of a Queue ' Using Exists to Test for the Presence of a Queue
Private Function CreateQueue(ByVal PathName As String) _
As MessageQueue
If MessageQueue.Exists(PathName) = True Then
Return New MessageQueue(PathName)
Else
Return MessageQueue.Create(PathName)
End If
End Function
Setting Permissions on Queues ' Using SetPermissions to Grant and Revoke Permissions on a Queue
myQueue = New MessageQueue(".\private$\DevXTestQueue)
myQueue.SetPermissions("michael.jones@passporthealth.com", _
MessageQueueAccessRights.FullControl, AccessControlEntryType.Set)
myQueue = New MessageQueue(".\private$\DevXTestQueue)
myQueue.SetPermissions("michael.jones@passporthealth.com", _
MessageQueueAccessRights.FullControl, _
AccessControlEntryType.Revoke)
Enumerating Queues on a Machine ' Using GetPublicQueuesByMachine and GetPrivateQueuesByMachine to
' Enumerate Queues
myQueues = MessageQueue.GetPublicQueuesByMachine(".")
Debug.WriteLine("---- " & myQueues.GetLength(0).ToString & _
" Queues ----")
For Each myQueue As MessageQueue In myQueues
Debug.WriteLine(myQueue.QueueName)
Next
myQueues = MessageQueue. GetPrivateQueuesByMachine("SERVER1")
Debug.WriteLine("---- " & myQueues.GetLength(0).ToString & _
" Queues ----")
For Each myQueue As MessageQueue In myQueues
Debug.WriteLine(myQueue.QueueName)
Next
Messaging with MSMQ
The System.Messaging namespace provides a concise way to send a simple message to a queue. Simply open the queue, and send a simple string-based message. This option does not support modifying the default attributes of the message before sending it. The code below demonstrates this functionality. The Try/Catch block was added for completeness.
Private Sub SendMessage()
Dim myQueue As MessageQueue
Try
myQueue = New MessageQueue(".\private$\DevXTestQueue")
myQueue.Send("This is a sample message body")
Catch ex As Exception
MessageBox.Show("Exception was thrown: " & _
ex.Source & ": " & ex.Message)
End Try
End Sub
To send a more complex message, for example when you need to modify the default attributes of a message before sending it, such as setting the CorrelationId, the ResponseQueue property, or making other modifications, you must first create a message object, set the properties, and then send the message, for example: Private Sub SendMessage(Byval MessageBody as String)
Dim RequestQueue As System.Messaging.MessageQueue
Dim ResponseQueue As System.Messaging.MessageQueue
Dim RequestMessage As System.Messaging.Message
' open the request and receive queues
RequestQueue = GetRequestQueue()
ResponseQueue = GetResponseQueue()
' create the message and set properties
RequestMessage = New Message
RequestMessage.Body = MessageBody
RequestMessage.ResponseQueue = ResponseQueue
RequestMessage.TimeToBeReceived = New TimeSpan(0, 1, 0)
MessageId = RequestMessage.Id()
' send the message
RequestQueue.Send(RequestMessage)
End Sub
Receiving a Single Message Private Sub ReceiveMessage()
Dim myQueue As MessageQueue
Dim myMsg As System.Messaging.Message
Try
' create a queue reference
myQueue = New MessageQueue(".\private$\DevXTestQueue")
' set the formatter for the received message
myQueue.Formatter = New XmlMessageFormatter( _
New Type() {GetType(System.String)})
' retrieve the message from the queue, waiting for no
' longer than X seconds
myMsg = myQueue.Receive(New TimeSpan(0, 0, 5))
' display the message
If Not IsNothing(myMsg) Then
MessageBox.Show("Message Received: " & CStr(myMsg.Body))
End If
' handle any exceptions that were raised
Catch exQueue As MessageQueueException
MessageBox.Show("The receive timeout expired " & _
"before a message was received.")
Catch ex As Exception
MessageBox.Show("Generic Exception was thrown: " & _
ex.Source & ": " & ex.Message)
End Try
End Sub
Receiving Messages Asynchronously Private Sub StartListening()
Dim myReceiveQueue As MessageQueue
Try
' open the receive queue
myReceiveQueue = New _
MessageQueue(".\private$\DevXTestQueue")
' set the receive queue formatter
myReceiveQueue.Formatter = New XmlMessageFormatter( _
New Type() {GetType(System.String)})
' add a handler for the ReceiveCompleted event and
' call BeginReceive
AddHandler myReceiveQueue.ReceiveCompleted, _
AddressOf MSMQ_ReceiveCompleted
myReceiveQueue.BeginReceive()
Catch ex As Exception
MessageBox.Show("Generic Exception was thrown: " _
& ex.Source & ": " & ex.Message)
End Try
End Sub
Public Sub MSMQ_ReceiveCompleted( _
ByVal p_source As Object, _
ByVal p_objAsyncResult As ReceiveCompletedEventArgs)
Dim myMessage As Message
Dim myMessageQueue As MessageQueue
' cast the source parm to a MessageQueue object
myMessageQueue = CType(p_source, MessageQueue)
' calling EndReceive will return the message that was received
myMessage = myReceiveQueue.EndReceive( _
p_objAsyncResult.AsyncResult)
' do something useful with the message
MessageBox.Show(CStr(myMessage.Body))
End Sub
MSMQ provides an easy-to-use facility for exchanging messages between applications. It also provides a robust queuing framework, with many advanced capabilities that I did not have space to cover. In a later article, I will discuss more advanced features of MSMQ, and some ways to make it more robust.
| DevX is a division of Jupitermedia Corporation © Copyright 2007 Jupitermedia Corporation. All Rights Reserved. Legal Notices |