Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Visual CSharp .NET Programming (2002) [eng]

.pdf
Скачиваний:
58
Добавлен:
16.08.2013
Размер:
7.98 Mб
Скачать

Private queues Visible only on a local machine.

System queues Primarily used for administrative purposes, although applications that need access to a journal queue (which saves copies of messages as they are processed), acknowledgment, and auditing facilities can use these queues.

Formerly a separate product called MSMQ, message queuing is now part of Windows XP and Windows 2000 (however, as I'll show you shortly, you may need to install it). It is additionally available to Windows NT users as part of the NT4 Options Pack. MSMQ services are not available for the Windows 98 or Windows Me family of products. In addition, public (as opposed to private) message queuing cannot be run using non-Server versions of NT or 2000.

Note The examples in this chapter use private queues but would essentially be the same if public queues were involved. (You can substitute one of the GetPublicQueues methods for the GetPrivateQueuesByMachine method shown in one of the examples.)

Before we move on to making sure that you have message queuing installed, let's back up for a second and ask, what is message queuing likely to be good for?

First, one could easily see how messaging could be used as an architecture to divide up computationally intensive tasks. This could happen within a single large application or across multiple applications.

Note In some cases, you may need to consider issues around giving multiple programs access to the same resource, such as a file. In this case, you may need to apply specific locking conditions to the resource to prevent conflicts using synchronization objects. For more information, look up 'Synchronization' in online help.

An application might send out messages saying what it is doing and providing interim results that could be used by other applications performing related tasks. One of the tasks might be to find certain text on the Internet; a vast army of 'crawlers' might go out to parse the text behind web pages-for example, using the screen scraping technique explained at the end of Chapter 10, 'Working with Streams and Files.' A crawler might send out messages like 'I found it' or 'Completed assignment without finding requested text.'

Workflow like this facilitates peer-to-peer divisions of load. It also makes conceivable a scenario like the one proposed by David Gerlernter in his seminal book about the future of computing, Mirror Worlds (Oxford University Press, 1992), in which myriad applications are set loose to create their own world within a world in the computer as part of the ultimate object-oriented paradigm.

On a more mundane level, message queues encourage self-reporting. An application could cause each server in a large network to send out messages with traffic and status statistics. An automated administrative program could use these messages to route incoming traffic.

Note Microsoft message queuing is somewhat limited for this kind of activity, because it is limited to servers using Microsoft operating systems. Most large networks are Unixonly or are heterogeneous-that is, they include servers using a variety of operating systems, such as Linux, Unix, and Windows.

Making Sure Message Queuing Is Installed

To begin with, you should verify that messaging is installed on a server that you are connected to, or you should use the Services applet to make sure that the Message Queuing service has been installed and is started (Figure 11.4). (To open the Services applet in Windows XP, select Control Panel from the Start menu, double-click Administrative Tools, and double-click the Services shortcut.)

Figure 11.4: You can check the Services application to make sure that Message Queuing is installed and started.

In the Services applet, if the Message Queuing service is not shown as running, click the Start link to start the service. You can also right-click the Message Queuing service, select Properties from the context menu, and set it for Automatic startup (if the service hasn't already been configured this way).

If you don't see the Message Queuing service in the Services applet, then it probably has not been installed. To remedy this under Windows XP, double-click the Add or Remove Programs link in the Control Panel. In the Add or Remove Programs dialog, click Add/Remove Windows Components. The Windows Components Wizard will open. As shown in Figure 11.5, make sure that Message Queuing is selected, click Next, and follow the instructions to complete the installation.

Figure 11.5: The Windows Component Wizard is used to install the Message Queuing application on a system.

If you open a new Visual C# .NET project in the Visual Studio development environment, you won't see the System.Messaging namespace in the Object Browser, and the classes in that namespace will not be usable. As a preliminary, you must add a reference to System.Messaging

.dll. To do this, select Project ? Add Reference (alternatively, highlight the project in Solution Explorer, right-click, and select Add Reference from the context menu). The Add Reference dialog will open.

Using the .NET tab of the Add Reference dialog, highlight System.Messaging.dll and click Select. The DLL will appear in the Selected Components list at the bottom of the Add Reference dialog (Figure 11.6). Click OK to add the reference. The classes in the System.Messaging namespace will then appear in the Object browser (Figure 11.7).

Figure 11.6: A reference to the selected component (System.Messaging) will be added to the current project.

Figure 11.7: With Message Queuing installed and a reference to the System.Messaging component added, the System.Messaging namespace and the Messaging classes appear in the Object Browser.

Note Code that uses the System.Messaging namespace should include a using System.Messaging directive. In the interests of clarity, I've omitted these directives from the program listings in the remainder of this chapter.

In most cases, by their very nature, applications organized around message queues are, in fact, asynchronous. (If I'm going about my business doing things but waiting to get a message from you that you've completed a task, and providing a result, then I am behaving asynchronously.) But as you'll see in the examples in this chapter, messages can be popped off a queue-or peeked at-either synchronously or asynchronously. In other words, even though the whole process of message queuing is essentially asynchronous by definition, it is possible to add another level of asynchronism on top of this.

Note The examples in this chapter show creating and using message queues in code. You should know, however, that the Message Queue item on the components tab of the Toolbar makes it easy to set the properties of public queues using the Properties window (rather than code), provided you are connected to a server that supports public queues. I'll leave it to your personal taste whether you prefer to set properties using code or in the Properties window.

Working with Message Queues and Messages

In order to work with MessageQueue objects, you need to know how to create or join a queue. Next, you need to know how to push a Message object onto the queue (by sending it) and how to pop one off the front of the queue (by receiving it).

Note In this section, I'll show you how to receive messages synchronously. In the example toward the end of the chapter, I'll show you how to receive-and peek-asynchronously.

You also might want to retrieve a list of all the current queues and-with a queue from this list selected-to retrieve all the messages on one queue.

This is a lot of programmatic ground to cover, so let's get started. The first example shows an application that sends and receives messages to itself. From a coding standpoint, it doesn't make much difference if the message is received by the application that sent it or by another application.

Creating or Joining a Queue

Listing 11.3 shows joining a queue-or creating a queue if it doesn't already exist-using the name supplied by the user in a TextBox.

Listing 11.3: Creating or Joining a Queue

MessageQueue mq;

private void btnCreate_Click(object sender, System.EventArgs e) { if (txtQName.Text != String.Empty) {

string qpath = @".\Private$\" + txtQName.Text; if (!MessageQueue.Exists(qpath))

MessageQueue.Create (qpath); mq = new MessageQueue(qpath);

}

else

MessageBox.Show ("Please enter a Queue name!", "No path", MessageBoxButtons.OK, MessageBoxIcon.Error);

}

The name of the queue is built up by appending the name supplied by the user to the verbatim string @".\Private$\" (which specifies that a private queue on the current machine is being created):

string qpath = @".\Private$\" + txtQName.Text;

Note For an explanation of verbatim strings, see Chapter 9, 'Everything Is String Manipulation.'

It's then an easy matter to create the MessageQueue if it doesn't already exist, as determined by the MessageQueue.Exists property:

if (!MessageQueue.Exists(qpath)) MessageQueue.Create (qpath);

One way or another, a MessageQueue with the requisite name now exists, and we can instantiate it in our class-level variable mq:

mq = new MessageQueue(qpath);

If you run this code and enter a queue name (as shown in Figure 11.8), you can use Server Explorer to verify that it has been created (I'll show you Server Explorer in just a moment).

Figure 11.8: It' s easy to join an existing queue (or create the queue if it doesn't already exist).

Sending a Message

To send a message-that is, to push a Message object onto the queue-is pretty easy. The first step is to apply a formatter to the MessageQueue:

mq.Formatter = new BinaryMessageFormatter();

Message Formatters

Message formatters, which implement the IMessageFormatter interface, can be applied

to Messages or to a MessageQueue as whole. The job of the message formatter is to produce a stream to be written to or read from a message body. Clearly, you want to deformat a message with the same formatter used to add it to the queue.

There are three formatters to choose from, supplied in the System.Messaging namespace:

ActiveXMessageFormatter Used when sending or receiving COM components.

BinaryMessageFormatter Used to serialize the message into a binary representation.

XMLMessageFormatter The default formatter; serializes the message to and from XML.

Although XMLMessageFormatter is a little slower than BinaryMessageFormatter, one can do some pretty cool things with XMLMessageFormatter (for example, it is standard practice to deserialize a message using XMLMessageFormatter into specified elements of an XML schema). For further information, look up "XMLMessageFormatter" in online help.

Next, the MessageQueue's Send method is used to push a message onto the queue:

mq.Send (txtMsg.Text, txtSubject.Text);

In the example shown, an optional subject heading, called the label, has been sent along with the text of the message.

Note Since the first parameter of the Send method is of type object, the example implicitly constructs a Message object, using the text string entered by the user to instantiate it (the string becomes the body of the message). But if you needed to, you could explicitly instantiate a Message object using one of the Message class constructors and then set Message instance properties, such as DigitalSignature, Priority, UseTracing, etc.

Tip It's important to understand that you can send any object as a message, not limited to just text. This includes the possibility of sending objects based on classes you have designed yourself.

Listing 11.4 shows the code for sending the relatively simple message.

Listing 11.4: Sending a Message

MessageQueue mq;

...

private void btnSend_Click(object sender, System.EventArgs e) { if (txtMsg.Text != String.Empty){

if (mq != null){

mq.Formatter = new BinaryMessageFormatter(); mq.Send (txtMsg.Text, txtSubject.Text); txtMsg.Text = String.Empty;

txtSubject.Text = String.Empty;

}

else

MessageBox.Show ("Please create or select a Queue!", "No queue", MessageBoxButtons.OK, MessageBoxIcon.Error);

}

else

MessageBox.Show ("Please enter a Message!", "No text", MessageBoxButtons.OK, MessageBoxIcon.Error);

}

If you run the code, you can enter a message and a subject line (Figure 11.9).

Figure 11.9: When you send a message, it is placed at the end of the queue.

Next, click Send, and open Server Explorer (View ? Server Explorer). Expand the node for the server hosting the message queue. You'll find Private Queues beneath the Message Queues node. Within Private Queues, there will be a node for the newly created queue, and each message on the queue will be listed by subject label (Figure 11.10).

Figure 11.10: If you open Server Explorer, the messages in a queue will be labeled with their subject.

Retrieving a Message

To retrieve the first message on the queue synchronously, use the Receive method of the MessageQueue object with a TimeSpan instance:

System.Messaging.Message msg = mq.Receive (new TimeSpan(0,0,5));

Note Receiving a message asynchronously is covered later in this chapter.

The MessageQueue Receive method stores a reference to the first message on the queue in a new System.Messaging.Message instance. The System.TimeSpan instance represents an interval of time-TimeSpan(0,0,5) means five seconds-that execution will wait for the message retrieval (this is sometimes referred to as a timeout parameter). If a timeout is not supplied, the execution will wait indefinitely for the message.

The code shown in Listing 11.5 starts with a check to make sure that there are some messages on the queue in question, using the Length property of the array of Message instances returned by the GetAllMessages method.

Listing 11.5: Retrieving the Message at the Head of the Queue

MessageQueue mq;

...

private void btnRetrieve_Click(object sender, System.EventArgs e) { if (mq.GetAllMessages().Length > 0) {

mq.Formatter = new BinaryMessageFormatter(); System.Messaging.Message msg = mq.Receive (new TimeSpan(0,0,5)); txtRetrieve.Text = msg.Body.ToString();

}

else

MessageBox.Show ("Nothing on the queue!", "No messages", MessageBoxButtons.OK, MessageBoxIcon.Error);

}

The code shown in Listing 11.5 converts the Body property of the Message instance to string and displays it, as shown in Figure 11.11.

Figure 11.11: The Receive method retrieves the message at the 'head' of the queue.

Note Windows message queuing is designed to automatically handle connection problems and issues of transient connectivity (such as a dial-up connection between two machines). If you send a message to a machine-normally a server-that you are not currently connected with, the message will wait in a queue on the source machine until the connection is established, when it will be sent.

Getting a List of Message Queues

Let's move on to a new application. Perhaps this application would like to receive and reply to messages sent by the first application, but it isn't quite sure of the name of the queue. It's easy to list all the MessageQueues on a machine, as shown in Listing 11.6.

Listing 11.6: Retrieving All the Private Queues on a Machine

MessageQueue [] msq;

private void btnGet_Click(object sender, System.EventArgs e) { lstQ.Items.Clear();

msq = MessageQueue.GetPrivateQueuesByMachine("."); foreach (MessageQueue m in msq) {

lstQ.Items.Add(m.FormatName);

}

}

The code in Listing 11.6 uses the GetPrivateQueuesByMachine method to read the existing MessageQueues into an array declared at the class level, msq. The argument passed to the method, ".", is shorthand for the current machine. Once the MessageQueues are retrieved into the array, each instance's FormatName property is displayed in a ListBox, as shown in Figure 11.12.

Figure 11.12: You can easily retrieve all the queues on a machine.

Getting All Messages in a Queue

It's not a whole lot harder to display all the messages on a queue selected by the user; most of the programmatic work involves determining which MessageQueue the user selected. As a preliminary before doing this, I decided to set up a property at the class (or form) level to store the index in the array of the selected MessageQueue, so that this could easily be used to reply to a message in the queue. Here's the property:

private int m_MsgIndex = 0; public int MsgIndex {

get {

return m_MsgIndex;

}

set {

m_MsgIndex = value;

}

}

Note I could have used a variable declared with a class-level scope to accomplish the same purpose (as with msq, the variable holding the array of MessageQueues), but I decided to use a property for the sake of variety. Properties are wonderful agents of encapsulation, and it is good to get in the habit of using them.

I compared the FormatName property of each MessageQueue in the msq array with the contents of the ListBox.SelectedItem property cast to string, to determine which MessageQueue was selected:

if (msq[i].FormatName == (string) lstQ.SelectedItem)

When a match is made, we have the MessageQueue that was selected by the user and can save the index in the property created for that purpose:

this.MsgIndex = i;

An array of System.Message.Message objects named messages is declared, and populated using the GetAllMessages method of the selected MessageQueue:

System.Messaging.Message [] messages = msq[i].GetAllMessages();

The messages on this queue are now eliminated using the Purge method, since, unlike the MessageQueue.Receive method, MessageQueue.GetAllMessages does not delete the messages obtained.

Note You will have to decide in the context of a particular application whether it is

appropriate to purge a message queue or not.

With the messages formerly on the queue safely stored in the messages array, it is now time to display the message subject line-using the Label property-and its body text-using the Body property and the ToString method:

for (int j = 0; j < messages.Length; j++) { lstMessages.Items.Add

(messages[j].Label + ": " +messages[j].Body.ToString());

}

When the code is run, the subject and body of each message, separated by a colon, is displayed (Figure 11.13).

Figure 11.13: You can use an array of messages to retrieve the subject and text of all messages in a queue.

The complete code for retrieving all the messages in a queue is shown in Listing 11.7.

Listing 11.7: Getting All the Messages in a Queue

MessageQueue [] msq;

...

private void btnGetMessages_Click(object sender, System.EventArgs e) { lstMessages.Items.Clear();

for (int i = 0; i < msq.Length; i++) {

if (msq[i].FormatName == (string) lstQ.SelectedItem) { this.MsgIndex = i;

msq[i].Formatter = new BinaryMessageFormatter(); System.Messaging.Message [] messages = msq[i].GetAllMessages(); msq[i].Purge();

for (int j = 0; j < messages.Length; j++) { lstMessages.Items.Add

(messages[j].Label + ": " +messages[j].Body.ToString());

}

}

}

}

private int m_MsgIndex = 0; public int MsgIndex {

get {