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.
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.
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.
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.
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.
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.