The ActiveX programming interface for MSMQ consists of a series of ten objects. The five objects we're mainly interested in are:
MSMQQuery
is the main 'containing' object for MSMQ, and is used to query the enterprise to obtain sets of queues.MSMQQueueInfos
is a set, or list, of queues that have previously been created. Although it looks like a VB collection, it's handled in a different way.MSMQQueueInfo
is a single queue object within a set of queues, and is used to work with the queue's properties and methods.MSMQQueue
objects represent the individual open instances of a queue, because a queue can be opened by more than one application at a time. It is also used to work with the queue's properties and methods.MSMQMessage
is the message object used to create and send messages, and refer to received messages.
You can think of the relationship as being like that shown in the diagram here. However, you don't have to create instances of all the objects each time—if you just want to create a new queue you can instantiate an
object directly.MSMQQueueInfo
Note that these are the class names within the DLLs that implement the objects. If you are creating object instances using Active Server Pages you need to prefix each with the registered name of the DLL. The objects become
, MSMQ.MSMQQuery
, MSMQ.MSMQQueueInfos
, MSMQ.MSMQQueueInfo
and MSMQ.MSMQQueue
. You'll soon get fed up typing 'MSMQ'.MSMQ.MSMQMessage
The other MSMQ ActiveX objects are:
MSMQApplication
is used to obtain the ID of a computer running an MSMQ-based application, given its name.MSMQEvent
is used to provide event handling, so that the arrival of messages or message errors can be detected when the system is running asynchronously. This saves our receiving code from having to keep looking for messages in a queue, and provides a more efficient application. You'll see this technique used in the next chapter.MSMQTransactionDispenser
represents the MSMQ built-in transaction dispenser, and can be used to obtain a reference to a transaction running under it.MSMQCoordinatedTransactionDispenser
represents the external MS DTC transaction dispenser, and can be used to obtain a reference to a transaction running under it.MSMQTransaction
represents the transaction returned by one of the two previous objects. It provides methods to commit or abort a transaction.A full list of all the properties, methods and events of the MSMQ objects is in Apeendix D, at the end of this book. We'll be covering the ones you use most often here.
Generally, there are four kinds of tasks that we'll regularly want to carry out with MSMQ:
We'll look at each of these topics in turn now.
Creating, Refreshing And Deleting Queues
To create a new queue, we simply use the
method of the Create
object: MSMQQueueInfo
MSMQQueueInfoObject.Create([transactional][,open_to_all])
To make the queue a transactional one, the first (optional) parameter is set to
, the default is True
. Setting the second (optional) parameter to False
means that the messages in the queue and its journal can be read by any other application. The default is True
—readable only by the owner of the queue. False
The MSMQQueueInfo Object Properties
Before we can create the queue we have to set the
property of the PathName
object—which defines where the queue will be located, and its name. Queue names must be unique on each machine, but there can be different queues with the same name on different machines. We can also set the MSMQQueueInfo
property to set the priority of all messages for the queue, unless explicitly over-ridden (see the section on sending messages for more details). The BasePriority
property can be used to control the privacy and encryption setting of the queue (discussed in more detail in Chapter 8). The PrivLevel
property can be set to control the maximum size in KB of the queue, and add a comment to the queue with the Quota
property. If we don’t set the Label
property explicitly, the queue name in the Label
property is used.PathName
The following is the minimal Visual Basic code to create a queue named
on the local machine:FinanceQueue
Dim objQueueInfo As New MSMQQueueInfo
objQueueInfo.PathName = ".\FinanceQueue"
objQueueInfo.Create
To create a queue on a remote machine, we just specify the machine name:
Dim objQueueInfo As New MSMQQueueInfo
objQueueInfo.PathName = "portia\FinanceQueue"
objQueueInfo.Create
In Active Server Pages, we can’t use the
method to create an instance of an object. Instead, we use the ASP New
method, and specify the full registered class name:Server.CreateObject
<%
Set objQueueInfo = Server.CreateObject("MSMQ.MSMQQueueInfo")
objQueueInfo.PathName = ".\FinanceQueue"
objQueueInfo.Create
%>
Also note that Active Server Pages (like VBScript and JavaScript) does not recognize the MSMQ named constants that you'll meet in subsequent examples. Instead, you have to use the actual constant values in your code.
Creating Private Queues
Queues that we create using a combination of the machine name and the queue name are public queues, and are registered in the MSMQ enterprise information store so that other applications can find and use them. We can also create private queues, on the local machine only, which are not registered with the enterprise and so are not available to other applications. This code creates a private queue named
:FinancePrivate
Dim objQueueInfo As New MSMQQueueInfo
objQueueInfo.PathName = "PRIVATE$\FinancePrivate"
objQueueInfo.Create
Using the FormatName Property
The
object also provides a MSMQQueueInfo
property, which MSMQ actually uses to create the queue. Normally, we don’t specify a value for this property unless we want to define the queue in a special way, and the queue is created by MSMQ using machine defaults combined with the FormatName
we provide. One example of a special situation is when we use an Independent Client to provide disconnected messaging. To be able to open a queue while not connected to the appropriate MSMQ Site Controller we can use the PathName
format:DIRECT
objQueueInfo.FormatName = "DIRECT=Protocol:MachineAddress\QueueName"
This also allows us to create a queue by specifying an Internet URL or IP address, for example:
objQueueInfo.FormatName = "DIRECT=TCP:194.73.51.228\FinanceQueue"
See the MSMQ Documentation for a full discussion of the other ways that the
property can be used.FormatName
Updating, Refreshing and Deleting Queues
Some properties of a queue can be changed after it has been created, by changing them in the relevant
object and then calling its MSMQQueueInfo
method: Update
objQueueInfo.Label = "New label text"
objQueueInfo.Update
Other applications using the queue can then get the current settings of the queue's properties into their
object using the MSQMQueueInfo
method:Refresh
objQueueInfo.Refresh 'in a different application
To delete a queue, we simply call the
object's MSMQQueueInfo
method. None of these methods require any parameters:Delete
objQueueInfo.Delete
Locating And Opening An Existing Queue
To locate an existing queue involves four steps:
MSMQQuery
object to reference MSMQ itself. MSMQQuery
object to return a MSMQQueueInfos
object.MSMQQueueInfos
list of queues to find the one we want.MSMQQueueInfo
object.Getting A List Of Queues
To return a list of queues we use the
object's MSMQQuery
method:LookupQueue
Set MSMQQueueInfosObject = MSMQQueryObject.LookupQueue
([QueueGuid][,ServiceGuid][,Label]
[,CreateTime][,ModifiedTime]
[,RelServiceGuid][,RelLabel]
[,RelCreateTime][,RelModifiedTime])
By specifying one or more of the parameters, we can force the list to only contain queues we're interested in. The five parameters that define aspects of the queues are:
QueueGuid - the string identifier of the queue.
ServiceGuid - a string indicating the type of service provided by the queue.
Label - the label of the queue as a string
CreateTime - the date and time that the queue was created.
ModifiedTime - the date and time that the queue properties were last set or modified.
Dim objQuery As New MSMQQuery 'a reference to MSMQ itself
Dim objQueueSet As MSMQQueueInfos 'to hold the list of queues
'look for all queues named FinanceQueue
Set objQueueSet = objQuery.LookupQueue(Label:="FinanceQueue")
...
It's also possible to specify a relationship between the value provided in one of the five parameters above and the actual values of that property in queues that will match. This is done by setting the appropriate Relxxx relationship parameters to
(equal), REL_EQ
(not equal), REL_NEQ
(less than), REL_LT
(greater than), REL_GT
(less than or equal), REL_LE
(greater than or equal) and REL_GE
(ignore this value):REL_NOP
Dim objQuery As New MSMQQuery
Dim objQueueSet As MSMQQueueInfos
'look for all queues created on or after 10/5/98
Set objQueueSet = objQuery.LookupQueue(CreateTime:="10/5/98", _
RelCreateTime:=REL_GE)
...
In Visual Basic it's usual to use named arguments (or named parameters) with this method, as shown above. However, VBScript and JScript, as used in Active Sever Pages, do not support named arguments. Instead we supply empty parameters up to and including the parameter we require, for example:
<%
Set objQuery = Server.CreateObject("MSMQ.MSMQQuery") 'in ASP
Dim objQueueSet
Dim objQueueInfo
'look for all queues named FinanceQueue
Set objQueueSet = objQuery.LookupQueue(, , "FinanceQueue")
...
%>
In ASP, the values for the relationship parameters are
, REL_EQ=1
, REL_NEQ=2 REL_LT=3
, REL_GT=4
, REL_LE=5
and REL_GE=6
.REL_NOP=0
Searching the Queue List
Once we've got a list of queues in a
object, we can search through it for the MSMQQueueInfos
queue object that we want, or see that none matched the criteria we used in the MSMQQueueInfo
method. The LookupQueue
object is not a true VB-style collection. To loop through it we have to use the MSMQQueueInfos
and Reset
methods. When we get to the end of the list, an empty Next
object is returned by the MSMQQueueInfo
method:Next
...
Dim objQueueInfo As MSMQQueueInfo 'to hold an individual queue
objQueueSet.Reset
Set objQueueInfo = objQueueSet.Next
While Not objQueueInfo Is Nothing
MsgBox "Found queue:" & objQueueInfo.PathName
Set objQueueInfo = objQueueSet.Next 'assign queue to QueueInfo object
Wend
The result is that we now have a
object that references the queue we want to use, and we can get on and open it.MSMQQueueInfo
Opening a Queue
To open a queue once we've created or located it, we use the
method of the Open
object. This returns a MSMQQueueInfo
object:MSMQQueue
Set MSMQQueueObject = MSMQQueueInfoObject.Open(accessmode, sharemode)
The first parameter defines how the application will use the queue. The options are:
( ) |
Messages can be examined but cannot be removed from the queue. |
( ) |
Messages can only be sent to the queue. |
( ) |
Messages can be examined (peeked) or retrieved (removed) from the queue. |
The second parameter defines who can access the queue. The options are:
( ) |
must be used if accessmode is or . It allows any application to access the queue. |
( ) |
can only be used when accessmode is . It allows only this application to retrieve messages from the queue. If another application has already opened the queue in this mode an error occurs. |
To open the
queue, assuming we know where it is, we can use:FinanceQueue
Dim objQueueInfo As New MSMQQueueInfo
Dim objQueue As MSMQQueue
'set the name of the queue to open
objQueueInfo.PathName = "portia\FinanceQueue"
Set objQueue = objQueueInfo.Open(MQ_SEND_ACCESS, MQ_DENY_NONE)
If objQueue.IsOpen Then
'OK to send a message
End If
Likewise, to open a queue for receiving messages, we change the parameters for the
method:Open
...
objQueueInfo.PathName = "portia\FinanceQueue"
Set objQueue = objQueueInfo.Open(MQ_RECEIVE_ACCESS, MQ_DENY_NONE)
If objQueue.IsOpen Then
'OK to receive a message
End If
In Active Server Pages we have to use the actual constant values in our code, and the
method:Server.CreateObject
<%
Set objQueueInfo = Server.CreateObject("MSMQ.MSMQQueueInfo")
Dim objQueue
'set the name of the queue to open
objQueueInfo.PathName = "portia\FinanceQueue"
Set objQueue = objQueueInfo.Open(2, 0) 'send and deny_none
If objQueue.IsOpen Then
'OK to send a message
End If
%>
Remember, the
object is a reference to the queue itself, while each MSMQQueueInfo
object is a reference to an individual open instance of that queue. Several applications can have the same queue open simultaneously.MSMQQueue
'Quick And Dirty' Queue Creation and Opening
If a queue with the same machine name and path as one we are creating already exists, an error occurs. If we only want to use a queue that may already exist, or if not needs to be created, an easy way is to add an On
Error
Resume
line to the code before the call to the Next
method. Then, if the queue already exists, the code will continue and open it ready for use. This saves having to use the Create
method first: LookupQueue
Dim objQueueInfo As New MSMQQueueInfo
Dim objQueue As MSMQQueue
objQueueInfo.PathName = ".\FinanceQueue"
On Error Resume Next
objQueueInfo.Create
Set objQueue = objQueueInfo.Open(MQ_SEND_ACCESS, MQ_DENY_NONE)
...
Sending A Message
To send a message we use the
method of the message itself, the Send
object, rather than a method of a particular queue. This allows us to send the message to any queue we like, as long as it is of the appropriate type (i.e. transactional or encrypted if the message requires this type of queue):MSMQMessage
MSMQMessageObject.Send(DestinationQueue,[Transaction])
The required DestinationQueue parameter is a reference to the queue object to which the message is to be sent, as returned from a call to the
object's MSMQQueueInfo
method. It can also be a Open
object's MSMQQueue
property, which is a long integer queue identifier that is available when the queue is opened.Handle
The optional Transaction parameter indicates if the sending of the message is part of a transaction. The default is
(MQ_MTS_TRANSACTION
- part of any existing transaction), and other values are 1
(MQ_XA_TRANSACTION
- part of an XA-compliant external transaction) and 2
(MQ_NO_TRANSACTION
- not involved in a transaction). This parameter can also be a reference to an existing transaction which the send action is to join.0
The MSMQMessage Object Properties
The
object has almost thirty properties. Some of these are the same as the properties we met for the MSMQMessage
object, and allow us to explicitly change the values for this message from the defaults specified by the queue. Some of the more useful ones are listed and described below:MSMQQueueInfo
The
and Label
properties are used to define the content of the message. The Body
is a string of up to 250 characters. It appears in MQ Explorer, and can be used to identify messages. The Label
property holds the real content of the message. It can be a Body
variable, an String
of Array
variables, any numeric, Byte
or Currency
variable, or a persistent ActiveX object. Persistent ActiveX objects are files that can be serialized, and which support the Date
interface. Examples are Microsoft Word and Excel document files.IPersist
The
property controls how the message is handled and delivered by MSMQ. The two possible values are:Delivery
( ) |
Default; indicates that the message is held in memory on each machine it passes through, and on the destination machine until it can be delivered (it may be cached to disk temporarily if the machine runs short of memory). This method is faster than delivery, but less robust as messages can be lost in a machine failure or shut down. |
( ) |
Indicates that, on every machine along the message route, the message is stored locally in a backup file on disk until it has been safely delivered to the next machine or to the destination queue. This guarantees delivery even in the case of a machine crash |
.
The
and MaxTimeToReachQueue
properties are used to specify timeouts (or expiry periods) in seconds for the message to either to reach the destination queue, or to be retrieved from that queue. The default for both is MaxTimeToReceive
(INFINITE
) if not specified.-1
The
property can be set to values from Priority
(lowest priority) to 0
(highest priority) to specify the urgency of the message. The default if not specified is 7
. Messages with the highest priority are transmitted from one machine to the next, and to the destination queue, first. 3
Because messages are usually retrieved from the queue in the order that they arrive, this can affect the way an application works. If you are using mixed priority messages you need to make sure that the receiving application can identify each message if this is appropriate, so that it acts on them in the correct order and in the correct way.
The
property specifies what action MSMQ should take with the message if it can’t be delivered. The usual values are:Ack
( ) |
the default, indicates that no acknowledgment should be returned. |
( ) |
Indicates that a positive or negative acknowledgment will be returned depending on whether the message reaches the destination queue. |
( ) |
Similar to the above, but only sends back negative acknowledgements. If all goes well no acknowledgement is returned. |
( ) |
Indicates that a positive or negative acknowledgment will be returned depending on whether the message is retrieved from the destination queue before it times out (or expires). |
( ) are |
Similar to the above, but only sends back negative acknowledgements. If all goes well no acknowledgement is returned. |
The
property is set to a reference to a queue where the delivery acknowledgements are to be sent. AdminQueueInfo
The
property is a reference to a ResponseQueueInfo
queue object where the receiving application should send responses. This allows the response queue to be specified dynamically by the sender, and change as and when required.MSMQQueueInfo
The
property can be set to Journal
(MQMSG_JOURNAL
) for a message, which indicates that a copy should be sent to the Journal queue that is attached to this queue. Setting the 2
property to Journal
(MQMSG_DEADLETTER
) indicates that undeliverable messages should be returned to the local machine's Dead Letter queue or Xact Dead Letter queue (for transactional messages).1
Using most of these properties, the following example shows how a message can be created and sent to the queue
we opened earlier:objQueue
...
If objQueue.IsOpen Then
'OK to send a message
Dim objMessage As New MSMQMessage
objMessage.Label = "Finance Test Message"
objMessage.Body = "This is a test message for the Finance application"
objMessage.Delivery = MQMSG_DELIVERY_RECOVERABLE
objMessage.MaxTimeToReachQueue = 30
objMessage.MaxTimeToReceive = INFINITE
objMessage.Priority = 5
objMessage.Ack = MQMSG_ACKNOWLEDGMENT_NONE
objMessage.Journal = MQMSG_DEADLETTER
'create a queue for response messages and get a reference to it
Dim objReturnInfo As MSMQQueueInfo
objReturnInfo.PathName = ".\ReturnQueue"
objReturnInfo.Create
objMessage.ResponseQueueInfo = objReturnInfo
objMessage.Send objQueue
End If
Closing the Queue
Once we're finished with a queue after sending messages to it, we can close it by calling the
object's MSMQQueue
method:Close
objQueue.Close
Receiving A Message
To receive the top message from a queue, we use the
method of the Receive
object that references the open queue from which we want the message to come. The message is removed from the queue, which must have been opened with the access mode MSMQQueue
(MQ_RECEIVE_ACCESS
):1
Set MSMQMessageObject = MSMQQueueObject.Receive
([Transaction][,UpdateDestinationInfo]
[,IgnoreBody][,Timeout])
The optional parameters define how the message will be received. The Transaction parameter indicates if the receive is part of a transaction. The default is
(MQ_MTS_TRANSACTION
- part of any existing transaction), and other values are 1
(MQ_XA_TRANSACTION
- part of an XA-compliant external transaction) and 2
(MQ_NO_TRANSACTION
- not involved in a transaction). This parameter can also be a reference to an existing transaction which the receive action is to join.0
The UpdateDestinationInfo parameter defines whether the other (remote)
objects will be updated to indicate that a message has been removed from the queue. This can slow down the system considerably, and the default is MSMQQueueInfo
.False
The IgnoreBody parameter can be set to
if we don’t want to retrieve the body of the message from the queue. The default is True
.False
Finally, the Timeout parameter is used to define the number of milliseconds until the receive action times out. The default is
(INFINITE
), and if this is not changed the -1
method will stop application execution if the queue is empty. It's usual to use named arguments (in Visual Basic) with the Receive
method, and you should always specify a value for this argument.Receive
...
If objQueue.IsOpen Then
'OK to receive a message
Set objMessage = objQueue.Receive(ReceiveTimeout:=1000)
MsgBox objMessage.Label
End If
Note that the code above will wait for the
period (one second) for a message to arrive if there isn't already one in the queue. The application will be stalled at this point until either a message arrives and is retrieved, or the timeout period passes. If you fail to set the timeout, the default value of RecieveTimeout
(infinite) means that the application will stall indefinitely until a message arrives. -1
Asynchronous Message Retrieval
As you may have noticed from the previous discussion of the
method, there is a minor problem. How do we know if a message has arrived? If we try to receive a message from an empty queue, we will stall the application. One of the core concepts of MSMQ is that it guarantees delivery of messages over slow and unreliable networks, or from disconnected clients. To save our receiving application from having to sit in a loop calling the Receive
method continually, we use the events that MSMQ exposes through its Receive
object. Our code can react to an event that is raised when a message arrives, rather than keep looking for a message. This technique is called asynchronous message retrieval.MSMQEvent
To keep the discussion of the MSMQ methods simple here, we're ignoring asynchronous message receipt for the time being. You'll see how it is implemented in the next chapter, as it's a core part of the way our main Wrox Car Co sample application works.
Peeking At Messages
Providing a queue has been opened with an access mode of
or MQ_PEEK_ACCESS
, we can examine messages without removing them from the queue. MQ_RECEIVE_ACCESS
Set MSMQMessageObject = MSMQQueueObject.Peek
([Timeout][,UpdateDestinationInfo][,IgnoreBody])
The parameter meanings are the same as with the
method shown above. As well as peeking at the topmost message in the queue, we can search through the queue using the Receive
and PeekCurrent
methods. The PeekNext
and/or Reset
method must be called first to initialize the position to the top of the queue, and each call to PeekCurrent
moves an implied cursor down the queue. If the queue has been opened for PeekNext
while peeking, we can call the MQ_RECEIVE_ACCESS
method to retrieve the current message from the queue.ReceiveCurrent
Closing the Queue
Once we're finished with a queue after reading from it, we can close it by calling the
object's MSMQQueue
method:Close
objQueue.Close