The Application Kit Table of Contents     The Application Kit Index

Messaging

The Application Kit provides a message-passing system lets your application send messages to and receives messages from other applications (including the Be-defined servers and apps), and from other threads in your own application.

The primary messaging classes are:

The other messaging classes are:

The rest of this chapter looks at...

describes how the classes fit together in the messaging system with an emphasis on what you can do in your application to take part.


Features of the Fundamental Classes

Looked at collectively, the four fundamental messaging classes comprise a huge chunk of API. Fortunately, the essential part of this API is pretty small; that's what we're going to look at here.


The BMessage Class

In the BMessage class, there's one essential data member, and two essential functions:

   /* The args are:  name, index, value (returned by reference) */
   bool returnValue;
   aMessage.FindBool("IsEnabled", 2, &returnValue);

In summary, a BMessage contains (1) a command constant and (2) a set of data fields. Every BMessage that's used in the messaging system must have a command constant, but not every object needs to have data fields. (Other parts of the BeOS use BMessages for their data only. The BClipboard object, for example, ignores a BMessage's command constant.)

 
When discussing system-generated BMessage objects, we refer to the object by its command constant. For example, "a B_MOUSE_DOWN" means "a BMessage that has B_MOUSE_DOWN as its command constant".


Notice that a BMessage doesn't know how to send itself. However, as we'll see later, it does know how to reply to its sender once it's in the hands of the recipient.


The BLooper Class

BLooper's role is to receive messages and figure out what to do with them. There are four parts to this job, embodied in these functions:

   /* This form of the BMessage constructor sets the command constant. */
   be_app->PostMessage(new BMessage(YOUR_CONSTANT_HERE))

In the Be kits, the BApplication and BWindow classes inherit from BLooper.


The BHandler Class

BHandler objects are called upon to handle the messages that a BLooper receives. A BHandler depends on two essential function:

    void MyHandler::MessageReceived(BMessage *message)
   {
      /* Examine the command constant. */
      switch ( message->what ) {
   
      case YOUR_CONSTANT_HERE:
         /* Call a function that handles this sort of message. */
         HandlerFunctionA();
         break;
   
      case ANOTHER_CONSTANT_HERE:
         /* ditto */
         HandlerFunctionB();
         break;
      
      default:
         /* Messages that your class doesn't recognize must be passed
          * on to the base class implementation. */
         baseClass::MessageReceived(message);
         break;
      }
   }

BLooper inherits from BHandler, and automatically adds itself to its own handler chain. This means that a BLooper can handle the messages that it receives—this is the default behaviour for most messages. We'll examine this issue in depth later in this chapter.

The other classes that inherit from BHandler are BView and BShelf (both in the Interface Kit).


The BMessenger Class

A BMessenger's most important feature is that it can send a message to a remote application. To do this takes two steps, which point out the class' essential features:

BMessengers can also be used to target local looper/handler pairs.


From Looper to Handler

A BLooper pops a message from its message queue and, within its DispatchMessage() function, dispatches the message by invoking a BHandler function. But (1) which BHandler and (2) which function?


Finding a Handler

First, let's answer the "which BHandler" question. To determine which BHandler should handle an in-coming message, a BLooper follows these steps:

1.    Does the BMessage target a specific BHandler? Both the BLooper::PostMessage() and BMessenger::SendMessage() functions provide additional arguments that let you specify a target handler that you want to have handle the message (you can also set the target in the BMessenger constructor). If a BHandler is specified, the BMessage will show up in that object's MessageReceived() function (or it will invoke a message-mapped hook function, as explained below).

2.    Does the BLooper designate a preferred handler? Through its SetPreferredHandler() function, a BLooper can designate one of the objects in its handler chain as its preferred handler, and passes all messages to that object.

3.    The BLooper handles the BMessage itself. If there's no target handler or preferred handler designation, the BLooper handles the message itself—in other words, the message is passed to the BLooper's own MessageReceived() function (or message-mapped hook).

We should mention here that both the BApplication and the BWindow class fine-tune this decision process a bit. However, the meddling that they do only applies to system messages (described below). The messages that you define yourself (i.e. the command constants that your application defines) will always follow the message dispatching path described here.

 
If you look at the DispatchMessage() protocol, you'll notice that it has a BMessage and a BHandler as arguments. In other words, the "which handler" decision described here is actually made before DispatchMessage() is called. In general, this is an implementation detail that you shouldn't worry about. If you want to think that DispatchMessage() makes the decision—and we've done nothing to disabuse you of this notion—go ahead and think it.



Finding a Function

As described above, a BLooper passes a BMessage to a BHandler by invoking the latter's MessageReceived() function. This is true of all messages that you create yourself, but it isn't true of certain messages that the system defines and sends. These system-generated messages (or system messages)—particularly those that report user events such as B_MOUSE_DOWN or B_APP_ACTIVATED—invoke specific hook functions.

For example, when the user presses a key, a B_KEY_DOWN message is sent to the active BWindow object.. From within its DispatchMessage() function, BWindow invokes the MouseDown() function of the BView that currently holds keyboard focus. (When a BView is made the focus of keyboard events, its window promotes it to preferred handler.)

So the question of "which function" is fairly simple: If the BMessage is a system message that's mapped to a hook function, the hook function is invoked. If it's not mapped to a hook function, the BHandler's MessageReceived() function is invoked.

A full list of system messages and the hook functions that they're mapped to is given in the System Messages Appendix. Note that not all system messages are mapped to hook functions; some of them do show up in MessageReceived().

Inheritance and the Handler Chain

Let's look at MessageReceived() again. It was asserted, in a code snippet shown earlier, that a typical MessageReceived() implementation should include an invocation of the base class' version of the function:

   void MyHandler::MessageReceived(BMessage *message)
   {
      switch ( message->what ) {
   
      /* Command constants that you handle go here. */   
   
      default:
         /* Messages that your class doesn't recognize must be passed
          * on to the base class implementation. */
         baseClass::MessageReceived(message);
         break;
      }
   }

This isn't just a good idea—it's an essential part of the messaging system. Forwarding the message to the base class does two things: It lets messages (1) pass up the class hierarchy, and (2) pass along the handler chain (in that order).

Passing up the class hierarchy is mostly straight-forward—it's no different for the MessageReceived() function than it is for any other function. But what happens at the top of the hierarchy—at the BHandler class itself—adds a small wrinkle. BHandler's implementation of MessageReceived() looks for the next handler in the BLooper's handler chain and invokes that object's MessageReceived() function.


Sending a Message

There are two functions that send messages to distinct recipients:


The PostMessage() Function

You can post a message if the recipient BLooper is in your application:

   myLooper->PostMessage(new BMessage(DO_SOMETHING), targetHandler);

As shown here, you can specify the handler that you want to have handle a posted message. The only requirement is that the BHandler must belong to the BLooper.

If the handler argument is NULL, the message is handled by the looper's preferred handler

   myLooper->PostMessage(new BMessage(DO_SOMETHING), NULL);

By using the default handler, you let the looper decide who should handle the message.

 
The creator of the BMessage retains ownership and is responsible for deleting it when it's no longer needed.



The SendMessage() Function

If you want to send a message to another application, you have to use BMessenger's SendMessage() function. First, you construct a BMessenger object that identifies the remote app by signature...

   BMessenger messenger("application/x-some-app");

...and then you invoke SendMessage():

   messenger.SendMessage(new BMessage(DO_SOMETHING));

 
The creator of the BMessage retains ownership and is responsible for deleting it when it's no longer needed.



Handling a Reply

Every BMessage that you send identifies the application from which it was sent. The recipient of the message can reply to the message whether you (the sender) expect a reply or not. By default, reply messages are handled by your BApplication object. If you want reply messages to be handled by some other BHandler, you specify the object as a final argument to the PostMesssage() or SendMessage() call:

   myLooper->PostMessage(new BMessage(DO_SOMETHING), targetHandler, replyHandler);
   /* and */
   myMessenger.SendMessage(&message, replyHandler);

The reply is sent asynchronously with regard to the PostMessage()/SendMessage() function.

SendMessage() (only) lets you ask for a reply message that's handed back synchronously in the SendMessage() call itself:

   BMessage reply;
   myMessenger.SendMessage(&message, &reply);

SendMessage() doesn't return until a reply is received. A default message is created and returned if the recipient doesn't respond quickly enough.

Receiving a Message

BMessage's SendReply() function has the same syntax as SendMessage(), so it's possible to ask for a synchronous reply to a message that is itself a reply,

   BMessage message(READY);
   BMessage reply;
   theMessage->SendReply(&message, &reply);
   if ( reply->what != B_NO_REPLY ) {
       . . .
   }

or to designate a BHandler for an asynchronous reply to the reply:

   theMessage->SendReply(&message, someHandler);

In this way, two applications can maintain an ongoing exchange of messages.


Handler Associations

To be notified of an arriving message, a BHandler must "belong" to the BLooper; it must have been added to the BLooper's list of eligible handlers. The list can contain any number of objects, but at any given time a BHandler can belong to only one BLooper.

Handlers that belong to the same BLooper can be chained in a linked list. If an object can't respond to a message, the system passes the message to its next handler.

BLooper's AddHandler() function sets up the looper-handler association; BHandler's SetNextHandler() sets the handler-handler link.


Message Filters

The BMessageFilter class lets create filtering functions that examine and re-route (or reject) incoming messages before they're processed by a BLooper. Message filters can also be applied to individual BHandler objects.


Message Protocols

Both the source and the destination of a message must agree upon its format—the command constant and the names and types of data fields. They must also agree on details of the exchange—when the message can be sent, whether it requires a response, what the format of the reply should be, what it means if an expected data item is omitted, and so on.

None of this is a problem for messages that are used only within an application; the application developer can keep track of the details. However, protocols must be published for messages that communicate between applications. You're urged to publish the specifications for all messages your application is willing to accept from outside sources and for all those that it can package for delivery to other applications.


The Application Kit Table of Contents     The Application Kit Index


The Be Book,
...in lovely HTML...
for BeOS Release 5.

Copyright © 2000 Be, Inc. All rights reserved..