Now that you understand the architecture of Message Queuing, you can look into the programming. In the next sections, you see how to create and control queues, and how to send and receive messages.
You also build a small course order application that consists of a sending and a receiving part.
Creating a Message Queue
You’ve already seen how to create message queues with the Computer Management utility. Message queues can be created programmatically with the Create () method of the Message Queue class.
With the Create() method, the path of the new queue must be passed. The path consists of the host name where the queue is located and the name of the queue. In the example, the queue MyNewPublicQueue is created on the local host. To create a private queue, the path name must include Private; for example, \PrivateMyNewPrivateQueue. .
After the Create () method is invoked, properties of the queue can be changed. For example, using the Label property, the label of the queue is set to Demo Queue. The sample program writes the path of the queue and the format name to the console. The format name is automatically created with a UUID that can be used to access the queue without the name of the server
Administrative privileges are required to create a queue. Usually, you cannot expect the user of your application to have administrative privileges. That’s why queues usually are created with installation programs. Later in this chapter, you see how message queues can be created with the MessageQueuelnstaller class.
Finding a Queue
The path name and the format name can be used to identify queues. To find queues, you must differentiate between public and private queues. Public queues are published in the Active Directory. For these queues, it is not necessary to know the system where they are located. Private queues can be found only if the name of the system where the queue is located is known.
You can find public queues in the Active Directory domain by searching for the queue’s label, category, or format name. You can also get all queues on a machine. The class MessageQueue has static methods to search for queues: GetPublicQueuesByLabel(), GetPublicQueuesByCategory(), and GetpublicQueuesByMachine() The method GetPublicQueues() returns an array of all public queues in the domain:
The method GetPublicQeues() is overloaded. One version allows passing an instance of the MessageQueueCriteria class. With this class, you can search for queues created or modified before or after a certain time, and you can also look for a category, label, or machine name.
Private queues can be searched with the static method GetPrivateQueuesByMachine(). This method returns all private queues from a specific system.
Opening Known Queues
If the name of the queue is known, it is not necessary to search for it. Queues can be opened by using the path or format name. They both can be set in the constructor of the MessageQueue class.
The path specifies the machine name and the queue name to open the queue. This code example opens the queue MyPublicQueue on the local host. To be sure that the queue exists, you use the static method MessageQueue.Exists():
Depending on the queue type, different identifiers are required when queues are opened. The following table shows:,the syntax of the queue name for specific types.
When you use the path name to open public queues, it is necessary to pass the machine name. If the machine name is not known, the format name can be used instead. The path name for private queues can be used only on the local system. The format name must be used to access private queues remotely.
Instead of the path name, you can use the format name to open a queue. The format name is used for searching the queue in the Active Directory to get the host where the queue is located. In a disconnected environment where the queue cannot be reached at the time the message is sent, it is necessary to use the format name:
The format name has some different uses. It can be used to open private queues and to specify a protocol that should be used:
- To access a private queue, the string that has to be passed to the constructor is Format Name : PRIVATE;MachineGUID\QueueNumber. The queue number for private queues is generated when the queue is created. You can see the queue numbers in the <liindows>\System32\msmq\ storage\lqs directory.
- With Format Name: DIRECT=Protocol :Machine Address \QueueName, you can specify the. protocol that should be used to send the message. The HTTP protocol is supported since Message Queuing 3.0.
- FormatName: DlRECT=OS: Machine Name \Queue Name is another ‘way to specify a queue using he format name. This way you don’t have to specify the protocol but still can use the machine name with the format name.
Sending a Message
You can use the Send method of the MessageQueue class to send a message to the queue. The object passed as an argument of the Send() method is serialized to the associated queue. The Send() method is overloaded so that a label and a MessageQueueTransaction object can be passed. Transactional behavior of Message Queuing is discussed later.
The code example first checks if the queue exists. If it doesn’t exist, a queue is created. Then the queue is opened and the message Sample Message is sent to the queue using the Send() method.
The path name specifies a dot Gust like a period) for the server name, which is the local system. Path names !o private queues work only locally.
By opening the message and selecting the Body tab (see Figure 45-8) of the dialog, you can see that the message was formatted using XML.How the message is formatted is the function of the formatter that’s associated with the message queue.
The format in which messages are transferred to the queue depends on the formatter. The Messagequeue class has a Formatter property through which a formatter can be assigned. The default formatter, Xml Message Formatter, will format the message in XML syntax as shown in the previous example.
A message formatter implements the interface IMessageFormatter. Three message formatters are available with the namespace System Messaging:
- The xml Message Formatter is the default formatter. It serializes objects using XML.See Chapter 28, “Manipulating XML,” for more on XML formatting.
- With the BinaryMessageF.ormatter, messages are serialized in a binary format. These messages are shorter than the messages formatted using XML..
- The ActiveXMessageFormatter is a binary formatter, so that messages can be read or written with COM objects. Using this formatter, it is possible to write a message to the queue with a
NET class and to read the message from the queue with a COM object or vice versa.
The sample message shown in Figure 45-8 with XMLis formatted with the BinaryMessageFormatter in Figure 45-9.
Sending Complex Messages
Instead of passing strings, it is possible to pass objects to the Send() method of the MessageQueue class type of the class must fulfill some specific requirements, but they depend on the formatter.
For the binary formatter, the class must be serializable with the [Serializable] attribute. With the NET runtime serialization, all fields are serialized (this includes private fields). Custom serialization can
be defined by implementing the interface ISerializable. You can read more about the .NET runtime serialization in Chapter 25, “Manipulating Files and the Registry,”
XML serialization takes place with the XML formatter. With XML serialization, all public fields and properties are serialized. The XML serialization can be influenced by using attributes from the System .Xml. Serialization namespace. You can read more about XML serialization in Chapter 28, “Manipulating XML.” .
To read messages, again, the MessageQueue class can be used. With the Reciveve() method, a single message is read and removed from the queue. If messages are sent with different priorities, the message with the highest priority is read. Reading messages with the same priority may mean that the first message sent is not the first message read because the order of messages across the network is not guaranteed. For a guaranteed order, you should use transactional message queues.
In the following example, a message is read from the private queue MyPrivateQueue. Previously, a simple string was passed to the message. When you read a message using the XmlMessageForrnatter, you have to pass the types of the objects that are read to the constructor of the f~tter. In the example, the type System. String is passed to the argument array of the XmlMessageForrnatter constructor. This constructor allows either a String array that contains the types as strings to be passed or a Type array.
The message is read with the Receive() method, and then the message body is written to the console:
The Receive() message behaves synchronously and waits until a message is in the queue if there is none.
Instead of reading message by message with the Receive () method, an enumerator can be used to walk through all messages. The MessageQueue class implements the interface IEnumerable and thus can be used with a foreach statement. Here, the messages are not removed from the queue, but you get just a peek at the messages to get their content:
Instead of using the IEnumerable interface, the class MessageEnumerator can be used. MessageEnumerator implements the interface IEnumerator, but has some more features. With the IEnumerable interface, the messages are not removed from the queue. The method RemoveCurrent() of the MessageEnumerator removes the message from the current cursor position of the enumerator.
In the example, the MessageQueue method GetMessageEnumerator() is used to access the MessageEnumercttor. The MoveNext() method takes a peek messageby message withthe MessageEnumerator. The MoveNext () method is overloaded to allow a time span as an argument. This
is one of the big advantages when using this enumerator. Here, the thread can wait until a message arrives in the queue, but only for the specified time span. The Current property, which is defined by the IEnumerator interface, returns a reference to a message:
The Receive method of the MessageQueue class waits until a message from the queue can be read. To avoid blocking the thread, a timeout can be specified in an overloaded version of the Receive method. To read the message from the queue after the timeout, Receive() must be invoked again. Instead of polling for messages, the asynchronous method BeginReceive() can be called. Before starting the asynchronous read with BeginReceive(), the event ReceiveCompleted should be set. The ReceiveCompleted event requires a ReceiveCompletedEventHandler delegate that references the method that is invoked when a message arrives in the queue and can be read. In the example, the method MessageArrived is passed to the Received CompletedEvem:Handler delegate:
The handler method MessageArrived requires two parameters. The first parameter is the origin of the event, the MessageQueue. The second parameter is of type ReceiveCompletedEventArgs that contains the message and the asynchronous result. In the example, the method EndReceive() from the queue is invoked to get the result of the asynchronous method, the message:
If the message should not be removed from the queue, the BeginPeek() and EndPeek() methods can be used with asynchronous I/O.