The MSMQ ActiveX Objects

The ActiveX programming interface for MSMQ consists of a series of ten objects. The five objects we're mainly interested in are:

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

MSMQQueueInfo
object directly.

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
,
MSMQ.MSMQQueue
and
MSMQ.MSMQMessage
. You'll soon get fed up typing 'MSMQ'.

The other MSMQ ActiveX objects are:

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

Create
method of the
MSMQQueueInfo
object:

MSMQQueueInfoObject.Create([transactional][,open_to_all])

To make the queue a transactional one, the first (optional) parameter is set to

True
, the default is
False
. Setting the second (optional) parameter to
True
means that the messages in the queue and its journal can be read by any other application. The default is
False
—readable only by the owner of the queue.

The MSMQQueueInfo Object Properties

Before we can create the queue we have to set the

PathName
property of the
MSMQQueueInfo
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
BasePriority
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
PrivLevel
property can be used to control the privacy and encryption setting of the queue (discussed in more detail in Chapter 8). The
Quota
property can be set to control the maximum size in KB of the queue, and add a comment to the queue with the
Label
property. If we don’t set the
Label
property explicitly, the queue name in the
PathName
property is used.

The following is the minimal Visual Basic code to create a queue named

FinanceQueue
on the local machine:

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

New
method to create an instance of an object. Instead, we use the ASP
Server.CreateObject
method, and specify the full registered class name:

<%

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

MSMQQueueInfo
object also provides a
FormatName
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
PathName
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
DIRECT
format:

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

FormatName
property can be used.

Updating, Refreshing and Deleting Queues

Some properties of a queue can be changed after it has been created, by changing them in the relevant

MSMQQueueInfo
object and then calling its
Update
method:

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

MSQMQueueInfo
object using the
Refresh
method:

objQueueInfo.Refresh      'in a different application

To delete a queue, we simply call the

MSMQQueueInfo
object's
Delete
method. None of these methods require any parameters:

objQueueInfo.Delete

Locating And Opening An Existing Queue

To locate an existing queue involves four steps:

Getting A List Of Queues

To return a list of queues we use the

MSMQQuery
object's
LookupQueue
method:

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

REL_EQ
(equal),
REL_NEQ
(not equal),
REL_LT
(less than),
REL_GT
(greater than),
REL_LE
(less than or equal),
REL_GE
(greater than or equal) and
REL_NOP
(ignore this value):

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
,
REL_GE=6
and
REL_NOP=0
.

Searching the Queue List

Once we've got a list of queues in a

MSMQQueueInfos
object, we can search through it for the
MSMQQueueInfo
queue object that we want, or see that none matched the criteria we used in the
LookupQueue
method. The
MSMQQueueInfos
object is not a true VB-style collection. To loop through it we have to use the
Reset
and
Next
methods. When we get to the end of the list, an empty
MSMQQueueInfo
object is returned by the
Next
method:

...

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

MSMQQueueInfo
object that references the queue we want to use, and we can get on and open it.

Opening a Queue

To open a queue once we've created or located it, we use the

Open
method of the
MSMQQueueInfo
object. This returns a
MSMQQueue
object:

Set MSMQQueueObject = MSMQQueueInfoObject.Open(accessmode, sharemode)

The first parameter defines how the application will use the queue. The options are:

MQ_PEEK_ACCESS
(
32
)
Messages can be examined but cannot be removed from the queue.
MQ_SEND_ACCESS
(
2
)
Messages can only be sent to the queue.
MQ_RECEIVE_ACCESS
(
1
)
Messages can be examined (peeked) or retrieved (removed) from the queue.

The second parameter defines who can access the queue. The options are:

MQ_DENY_NONE
(
0
)
must be used if accessmode is
MQ_PEEK_ACCESS
or
MQ_SEND_ACCESS
. It allows any application to access the queue.
MQ_DENY_RECEIVE_SHARE
(
1
)
can only be used when accessmode is
MQ_RECEIVE_ACCESS
. 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

FinanceQueue
queue, assuming we know where it is, we can use:

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

Open
method:

...

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

Server.CreateObject
method:

<%

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

MSMQQueueInfo
object is a reference to the queue itself, while each
MSMQQueue
object is a reference to an individual open instance of that queue. Several applications can have the same queue open simultaneously.

'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
Next
line to the code before the call to the
Create
method. Then, if the queue already exists, the code will continue and open it ready for use. This saves having to use the
LookupQueue
method first:

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

Send
method of the message itself, the
MSMQMessage
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):

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

MSMQQueueInfo
object's
Open
method. It can also be a
MSMQQueue
object's
Handle
property, which is a long integer queue identifier that is available when the queue is opened.

The optional Transaction parameter indicates if the sending of the message is part of a transaction. The default is

MQ_MTS_TRANSACTION
(
1
- part of any existing transaction), and other values are
MQ_XA_TRANSACTION
(
2
- part of an XA-compliant external transaction) and
MQ_NO_TRANSACTION
(
0
- not involved in a transaction). This parameter can also be a reference to an existing transaction which the send action is to join.

The MSMQMessage Object Properties

The

MSMQMessage
object has almost thirty properties. Some of these are the same as the properties we met for the
MSMQQueueInfo
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:

The

Label
and
Body
properties are used to define the content of the message. The
Label
is a string of up to 250 characters. It appears in MQ Explorer, and can be used to identify messages. The
Body
property holds the real content of the message. It can be a
String
variable, an
Array
of
Byte
variables, any numeric,
Currency
or
Date
variable, or a persistent ActiveX object. Persistent ActiveX objects are files that can be serialized, and which support the
IPersist
interface. Examples are Microsoft Word and Excel document files.

The

Delivery
property controls how the message is handled and delivered by MSMQ. The two possible values are:

MQMSG_DELIVERY_EXPRESS
(
0
)
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
recoverable
delivery, but less robust as messages can be lost in a machine failure or shut down.
MQMSG_DELIVERY_RECOVERABLE
(
1
)
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

MaxTimeToReachQueue
and
MaxTimeToReceive
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
INFINITE
(
-1
) if not specified.

The

Priority
property can be set to values from
0
(lowest priority) to
7
(highest priority) to specify the urgency of the message. The default if not specified is
3
. Messages with the highest priority are transmitted from one machine to the next, and to the destination queue, first.

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

Ack
property specifies what action MSMQ should take with the message if it can’t be delivered. The usual values are:

MQMSG_ACKNOWLEDGMENT_NONE
(
0
)
the default, indicates that no acknowledgment should be returned.
MQMSG_ACKNOWLEDGMENT_FULL_REACH_QUEUE
(
5
)
Indicates that a positive or negative acknowledgment will be returned depending on whether the message reaches the destination queue.
MQMSG_ACKNOWLEDGMENT_NACK_REACH_QUEUE
(
4
)
Similar to the above, but only sends back negative acknowledgements. If all goes well no acknowledgement is returned.
MQMSG_ACKNOWLEDGMENT_FULL_RECEIVE
(
14
)
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).
MQMSG_ACKNOWLEDGMENT_NACK_RECEIVE
(
12
) are
Similar to the above, but only sends back negative acknowledgements. If all goes well no acknowledgement is returned.

The

AdminQueueInfo
property is set to a reference to a queue where the delivery acknowledgements are to be sent.

The

ResponseQueueInfo
property is a reference to a
MSMQQueueInfo
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.

The

Journal
property can be set to
MQMSG_JOURNAL
(
2
) for a message, which indicates that a copy should be sent to the Journal queue that is attached to this queue. Setting the
Journal
property to
MQMSG_DEADLETTER
(
1
) indicates that undeliverable messages should be returned to the local machine's Dead Letter queue or Xact Dead Letter queue (for transactional messages).

Using most of these properties, the following example shows how a message can be created and sent to the queue

objQueue
we opened earlier:

...

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

MSMQQueue
object's
Close
method:

objQueue.Close

Receiving A Message

To receive the top message from a queue, we use the

Receive
method of the
MSMQQueue
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
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
(
1
- part of any existing transaction), and other values are
MQ_XA_TRANSACTION
(
2
- part of an XA-compliant external transaction) and
MQ_NO_TRANSACTION
(
0
- not involved in a transaction). This parameter can also be a reference to an existing transaction which the receive action is to join.

The UpdateDestinationInfo parameter defines whether the other (remote)

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

The IgnoreBody parameter can be set to

True
if we don’t want to retrieve the body of the message from the queue. The default is
False
.

Finally, the Timeout parameter is used to define the number of milliseconds until the receive action times out. The default is

INFINITE
(
-1
), and if this is not changed the
Receive
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.

...

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

RecieveTimeout
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
-1
(infinite) means that the application will stall indefinitely until a message arrives.

Asynchronous Message Retrieval

As you may have noticed from the previous discussion of the

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

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

MQ_PEEK_ACCESS
or
MQ_RECEIVE_ACCESS
, we can examine messages without removing them from the queue.

Set MSMQMessageObject = MSMQQueueObject.Peek

([Timeout][,UpdateDestinationInfo][,IgnoreBody])

The parameter meanings are the same as with the

Receive
method shown above. As well as peeking at the topmost message in the queue, we can search through the queue using the
PeekCurrent
and
PeekNext
methods. The
Reset
and/or
PeekCurrent
method must be called first to initialize the position to the top of the queue, and each call to
PeekNext
moves an implied cursor down the queue. If the queue has been opened for
MQ_RECEIVE_ACCESS
while peeking, we can call the
ReceiveCurrent
method to retrieve the current message from the queue.

Closing the Queue

Once we're finished with a queue after reading from it, we can close it by calling the

MSMQQueue
object's
Close
method:

objQueue.Close

© 1998 by Wrox Press. All rights reserved.