Working with Queues and Messages

It is surprising how easy it is to support message queues in your application, given the power and flexibility that MSMQ provides. By making the implementation of message queuing so easy, Microsoft hopes that more and more developers will take advantage of it. There are two primary operations that you can do with a message queue. You can open or close the queue and you can send or receive a message. There are other methods that allow you to search for a queue, create or delete queues, and manage the properties of existing queues.

Since this is a book about ASP, and ASP works best with ActiveX objects, all of the code snippets that we will be showing will be using the MSMQ ActiveX objects, as opposed to the native COM interfaces.

There are a series of ActiveX objects that encapsulate the functionality of MSMQ. These can be created in ASP pages, using the Server.CreateObject method. They can also be created in VB Active Server Components running inside the MTS environment by using the ObjectContext.CreateInstance method.

Step 1: Get a Queue

The first thing that you will need to do is get your hands on a queue to send your messages to. There are two ways to get a queue. If the queue exists, then you can locate it. If it doesn't exist then you can create it. The MSMQQueueInfo object allows you to manipulate properties and methods that describe a queue. To create a new queue, you will use this object's Create method. Prior to calling create, you will need to set some information about the queue. This information at least has to include the location of the queue.

The location of a queue is called its path name. This path name indicates three things about the queue:

The format of the queue name is machinename\queuename for a public queue or, .\PRIVATE$\queuename for a private queue. In the syntax for creating a private queue, the '.' is used to indicate the local machine. Private queues can only be created on the local machine, so a machine name is not needed. To set the pathname you would use this code:

Dim qinfo as New MSMQQueueInfo
qinfo.PathName = "myserver\TestQueue"

Once the queue has been named, other properties can be set. The pathname parameter is the only required parameter. The other parameters are documented in the MSMQ documentation. To create the queue, all you now need to do is call its create method:

qinfo.Create

We now have a reference to a valid queue that can be used in step 2: opening the queue.

If you know that the queue you are interested in already exists, but do not know the exact name if it, you can lookup a reference to the queue. Using the LookupQueue method of the MSMQQuery object does this. This is the only method of this object, and will return a MSMQQueueInfos object, which is a collection of queues based on criteria that you set. For the criteria, you can choose from one or more of the following properties of the queue:

Parameter—Optional Value
QueueGuid (String) Identifier of queue.
ServiceTypeGuid (String) Type of service provided by the queue.
Label (String) Label of queue.
CreateTime (Variant Date) Time when queue was created.
ModifyTime (Variant Date) Time when queue properties were last set (both when the queue was created and the last time Update was called).

Since these parameters only allow you to set a single value for the parameter, to make the query more effective, you can also set a Boolean operation for each parameter. In other words, when you enter a parameter value, say for Label, the method will only return those queues that exactly match the value you entered. By setting the relationship parameter, you can specify that the value you are entering is some boolean relation to the search value. These possible boolean operators are:

Boolean Operator Value
REL_EQ Equals
REL_NEQ Not Equal to
REL_LT Less Than
REL_GT Greater Than
REL_LE Less Than or Equal To
REL_GE Greater Than or Equal To
REL_NOP Ignore the Parameter

The relationship parameters are:

Relationship Parameter Criteria Parameter
RelServiceType ServiceTypeGuid
RelLabel Label
RelCreateTime CreateTime
RelModifyTime ModifyTime

To call the LookupQueue method using Visual Basic, you would do something like the following:

Dim query As New MSMQQuery
Dim qinfos As MSMQQueueInfos
Dim qinfo As MSMQQueueInfo
Set qinfos = query.LookupQueue(Label:="Bill")

This will return a collection of queues, all of which have a Label value of 'Bill.' While this return value is a collection, it is not navigable using the same methods as other collections. To navigate, there is a Reset method to move in front of the beginning of the collection, and a Next method to get the next entry in the collection. To navigate through each entry in the collection, you can use this:

qinfos.Reset  ' This ensures we are at the beginning of the collection
Set qinfo = qinfos.Next
Do While Not qinfo Is Nothing
'   Do something with this queue
   Set qinfo = qinfos.Next  
Loop

This gives you two methods to retrieve a queue. The next step is to open the queue.

Step 2: Open a Queue

The MSMQQueueInfo object that was returned when you created a queue or found one using LookupQueue is merely information about a queue. To actually work with a queue, that is use it to send a receive messages, you have to open it. When you open a queue, you call a method on the MSMQQueueInfo object and are returned a MSMQQueue object. It is this object that you will subsequently use in working with the queue. To open a queue, you first need to set two parameters. The first parameter will determine what you are doing with the queue and the second will determine what others can do with it while you have it open.

When you have an open queue in your application, there are three operations that you can perform on it. You can:

Reading and writing messages are self-explanatory. Peeking at a message in the queue means to look at the contents of a message in the queue without removing it from the queue.

You also need to decide how other people can interact with the queue when you have it open. If you have opened the queue for sending or peeking, then the only valid choice is to allow others to fully interact with the queue. If you have opened the queue for receiving, then you can either allow others full access to the queue, or you can prevent others from receiving messages from the queue as well. Even if you choose this option, other applications can still send messages to the queue and peek at messages in the queue. These choices are determined by setting two values when opening the queue: the queue access mode, which governs how you will be able to access the queue, and the queue sharing mode, which governs how others can access the queue.

The set of possible values for the queue access and queue sharing modes are:

Queue Access Mode Queue Sharing Mode
MQ_PEEK_ACCESS MQ_DENY_NONE
MQ_SEND_ACCESS MQ_DENY_RECEIVE_SHARE
MQ_RECEIVE_ACCESS

Once you have determined the parameter settings for your queue, you can issue the open call. The format for this call is:

Open(accessmode, sharemode)

To open a queue for sending messages to it, you would:

qInfo.Open(MQ_SEND_ACCESS, MQ_DENY_NONE)

Now that you have a queue open for writing, the next step that you need to follow is to create a message that can be sent to that queue.

Step 3: Create a Message

To send a message, you will need to create an instance of the MSMQMessage object. This object encapsulates all of the data and methods that you need to create and send a message. There are a number of properties that you can set when sending a message. Some of these parameters are considered core properties, such as the body of the message. Others allow you to modify certain characteristics about how the message is delivered, or other characteristics. This table shows the characteristic properties of a MSMQMessage object:

Property Description
Ack MSMQ can acknowledge when certain events happen to this message after it is sent. Acknowledgements can be positive or negative. This parameter is used to set what actions this message will acknowledge. Possible values are:
MQMSG_ACKNOWLEDGMENT_FULL_REACH_QUEUE
Positive if the message reaches its destination queue, negative if it fails to arrive within the timeout.
MQMSG_ACKNOWLEDGMENT_FULL_RECEIVE
Positive if the message is retrieved from its queue before it expires. Negative if it expires.
MQMSG_ACKNOWLEDGMENT_NACK_REACH_QUEUE
Negative if the message fails to arrive at the queue. No positive acknowledgement sent.
MQMSG_ACKNOWLEDGMENT_NACK_RECEIVE
Negative if the message is not retrieved from its queue before it expires. No positive acknowledgement is sent.
MQMSG_ACKNOWLEDGMENT_NONE
This is the default value – no acknowledgements sent.
AdminQueueInfo If the message is going to send acknowledgements, this is the queue that it will be sent to.
Body The contents of the message
CorrelationId If you are responding to a message, you can put the ID of the incoming message in this field, so that the other application can correlate the two messages.
Delivery MSMQ_DELIVERY_EXPRESS—for Express Delivery
MSMQ_DELIVERY_RECOVERABLE—for Recoverable Delivery
Label A description of the message
MaxTimeToReachQueue Timeout value in seconds after which the message will be deleted if it does not reach its destination queue
MaxTimeToReceive Timeout value in seconds after which the message will be deleted if it is not retrieved from its destination queue.
Priority Sets how quickly a message will be routed and where it is placed in the destination queue. Higher values are routed faster and placed in front of lower priority messages. Messages with the same priority are in the queue in chronological order.
ResponseQueueInfo If the sending application wants a response to this message, in can include the queue it wants the response sent to in this parameter.

The primary parameter for the MSMQMessage object is the Body parameter. This holds the actual contents of the message. This value can be a string, or any type of binary information that both the sending and receiving application know how to handle.

Lets take a look at an example that will create a message, set its properties, and set its content. This will be everything that needs to be done to get the message ready to send.

Dim msgSent As New MSMQMessage
msgSent.Label = "My Message"
msgSent.Body = "Test message with acknowledgment."
msgSent.Ack = QMSG_ACKNOWLEDGMENT_FULL_RECEIVE
msgSent.MaxTimeToReceive = 60  
Set msgSent.AdminQueueInfo = qinfoAdmin

This will create a new MSMQMessage object and set its Label and Body. This message will send an acknowledgment when it is retrieved from its destination queue. This acknowledgment will be send to the qinfoAdmin queue. If the message is not retrieved within 60 seconds, it will be deleted from the destination queue, and a negative acknowledgment will be sent.

The last step in the process of creating and sending the message is to send it to its destination queue.

Step 4: Send a Message

To send a message, you will need both a queue that is open for sending and an MSMQMessage object that has been properly configured. The Send method is actually a method of the message object, not the queue. The destination queue is passed as a parameter to the Send method. To send this message to the queue that we have already opened, you would type:

msgSent.Send qInfo

The message is now on its way to its destination queue. Once it arrives there, it will wait for 60 seconds. If no application retrieves it within that time, it will delete itself from the queue. If an application retrieves the message before the 60 seconds are up, then a positive acknowledgement message will be sent to the queue defined by qinfoAdmin. If the message times out and deletes itself, then a negative acknowledgement will be sent to the qinfoAdmin queue.

Step 5: Retrieving a Message

Now that there is a message sitting in this queue, we need to be able to retrieve it and use its contents in another application. There are two methods that can be used to retrieve messages from a queue. A queue can be read synchronously, wherein all program execution will be blocked until a message is available in the queue, or a timeout value is reached. A queue can also be read asynchronously, where a queue will fire events as messages arrive, and these events can be handled by the application. In the web-based application world, the majority of message retrieval will be done using synchronous reads. We are using the synchronous message since there really is no concept of a "running application" that can receive the message arrival events that an asynchronous read would generate. In a web-based application, the system will check for a waiting message when the user tries to load a specific page in the application. While a synchronous message retrieval on a web application sounds like it may adversely impact system performance, we can first check to see if there is a message waiting before we actually do the read.

In the previous examples, we sent the message to the queue with the label of 'Bill.' We can now use exactly the same steps as were used in steps 1 through 3 above, except now we will open the queue for read.

Dim query As New MSMQQuery
Dim qinfos As MSMQQueueInfos
Dim qinfo As MSMQQueueInfo
Set qinfos = query.LookupQueue(Label:="Bill")
qinfos.Reset  
Set qinfo = qinfos.Next  
qInfo.Open(MQ_RECEIVE_ACCESS, MQ_DENY_NONE)

In the Open method, we specified that this queue will be used to retrieve messages. This mode will also allow us to peek at messages that may be waiting in the queue. To peek at a message in the queue, you would:

Set msgDest = qInfo.Peek(ReceiveTimeout:=100)

This will check to see if there are any messages in the queue. If after 100 milliseconds, or 1/10th of a second, there are no messages, then this method will return. To check to see if a message was found, you will check to see if msgDest properly references an object. If it is set to Nothing, then no message was waiting in the queue.

If a message is found in the queue, then it can be retrieved. There could be more than one message waiting in the queue. If there is, then the message that will be retrieved will be the first one in the queue. This is the message with the highest priority, or if more than one message has the same priority, then the one that has been in the queue the longest will be retrieved. To retrieve this message, you would:

Set msgDest = qDest.Receive(ReceiveTimeout:=10)

This will put the contents of the message that was in the queue into the msgDest object. You can then get the information that this message contained by examining the contents of its body. Once you have read this message from the queue, you can close the queue. This will free up the resources used by the queue object. The MSMQMessage object that you retrieved will still be valid, even after you close the queue. To close the queue, you would:

qInfo.Close()

Now that we have looked at the steps to work with messages and queues, let's step back a bit from the specific implementation and look at how MSMQ and other key pieces of Microsoft technology can work together.

© 1998 by Wrox Press. All rights reserved.