Sending and Receiving ToolTalk Messages

This section describes how to register and manage message patterns, declare actions for your application to perform when it receives messages, composes messages, and sends notices and requests. You accomplish all of these tasks using the VkMsgClient class.

You do not explicitly instantiate VkMsgClient objects. Instead, the VkMsgApp, VkMsgWindow, and VkMsgComponent classes all automatically instantiate their own VkMsgClient objects and provide access functions to those objects.

Sending Notices and Requests

This section describes how to send notices and requests using VkMsgClient. Both notices and requests are types of messages. A sending application sends a notice message to provide information to other applications; the sending application does not expect a reply to a notice. All of the parameters in a notice have an "in" mode, which indicates that receiving applications should read only those parameters.

A sending application sends a request message to ask another application to perform an action and expects a single reply to a request. In a request, some message parameters have an "out" mode, which indicates that the receiving application should fill in these parameters for the reply message. Or "in/out", which indicates that the receiving application should read those parameters and then fill them in with new values for the reply message.

Sending simple notices

If you're sending a notice consisting of only string arguments or only integer arguments, you can use the VkMsgClient::sendStringNotice() or VkMsgClient::sendIntNotice() function, respectively:

void sendStringNotice(char *op ...)
void sendIntNotice(char *op ...)

For both functions, op is the message operator. sendStringNotice() expects a NULL-terminated list of pointers to character strings; sendIntNotice() expects a NULL-terminated list of integers. These functions create a message with the arguments you provide, send the message, and then automatically delete the message (that is, delete the storage space allocated by your application when the functions create the message).


Caution: Because sendIntNotice() expects a NULL-terminated list of arguments, be sure that you don't provide a zero-valued argument or else you'll prematurely terminate your argument list.


Composing and sending messages

To send requests or to send notices that contain a mix of argument types, you must compose the message before sending it. To begin composing a message, call VkMsgClient::composeBegin():

void composeBegin()


Note: You can compose only one message at a time among all VkMsgClient objects.


You can then add arguments to your message one at a time, using VkMsgClient::composeAdd():

void composeAdd(char *val, VkMsgMode mode = VK_MSG_IN)
void composeAdd(int ival, VkMsgMode mode = VK_MSG_IN)
void composeAdd(unsigned char *val, int len,
VkMsgMode mode = VK_MSG_IN)

You can add as an argument: 1) a NULL-terminated character string; 2) an integer value; or 3) a binary string. If you provide a binary string, you must also specify the length of the string as the len argument.

The mode argument is an enumerated value of type VkMsgMode. VK_MSG_IN indicates that the argument is written by the sending application and can be read by the handling application and any observing applications. VK_MSG_OUT indicates that the argument is written by the handling applications and is read by the sending application. VK_MSG_INOUT indicates that the argument can be written by both the sending and handling applications, and can be read by the sending, handling, or any observing applications.

Notice messages

Once you finish composing the message, you send the message. If the message is a notice, use VkMsgClient::sendNotice():

void sendNotice(char *op)

op is the message operator. sendNotice() sends the message you composed, and then automatically deletes the message (that is, deletes the storage space allocated by your application when you compose the message).

Sending a request

To send a request, use VkMsgClient::sendRequest():

VkMsgMessage sendRequest(char *op)

sendRequest() sends the message you composed. op is the message operator. sendRequest() returns an opaque message handle. You can use this handle when calling the various utility functions provided by the ViewKit ObjectPak message facility as described in "Useful functions when handling messages" .

When you send a request using sendRequest(), the function returns immediately. To obtain and handle the reply message to your request, your application must register a message action as described in "Receiving Notices and Handling Requests" .


Note: The ViewKit ObjectPak message facility automatically deletes the request message when your application receives the corresponding reply or failure message.


Sending synchronous requests

In some cases, your application might require a reply to a request before performing any other processing. In these cases, you can use VkMsgClient::sendSyncRequest() to send a synchronous request:

VkMsgMessage sendSyncRequest(char *op)

sendSyncRequest() uses a secondary X event loop to simulate a synchronous reply to a request. sendSyncRequest() blocks until it receives the reply message, which it passes as its return value. If the request fails, sendSyncRequest() returns NULL. Note that because sendSyncRequest() uses a secondary X event loop, you should beware of any problems with re-entrant code in any callbacks that could be invoked.

Once you receive the reply message, you can use the various utility functions provided by the ObjectPak message facility, as described in "Useful functions when handling messages" , to parse the reply. You must also use VkMsgDestroy() to destroy the message when you no longer need the reply:

VkMsgStatus VkMsgDestroy(VkMsgMessage msg)

VkMsgDestroy() is implemented as a normal function rather than a class member function. It is declared in the header file <Vk/VkMsg.h>. The VkMsgStatus return value is the same as the tt_status values used by ToolTalk; consult the ToolTalk Programmer's Guide for information on these values.

Specifying a filename message attribute

The convention for passing a filename argument in a message is to specify the filename as a message attribute rather than a message argument. The VkMsgClient class provides the following functions for sending a message with a filename attribute:

void sendStringFileNotice(char *op, char *file ...)
void sendIntFileNotice(char *op, char *file ...)
void sendFileNotice(char *op, char *file)
void sendFileRequest(char *op, char *file)
void sendSyncFileRequest(char *op, char *file)

In these functions, file is the filename.

Receiving Notices and Handling Requests

This section describes how to receive notices and handle requests using the ObjectPak message facility, as well as message dispatch, writing message action callbacks, creating message patterns, associating message actions with message patterns, and registering and unregistering message patterns.

Overview of message dispatch

For each type of message an application wants to receive, it must register a message pattern with the ToolTalk service. The message pattern describes the operator, arguments, and attributes that a message must have to be delivered to the application. Your application must also register message actions, callback functions that are called when your application receives messages with a particular message pattern.

When the ToolTalk service receives a message, it matches the message against all registered patterns. If the message is a notice, the ToolTalk service delivers a separate message to each application with a matching pattern; if the message is a request, the ToolTalk service selects the "best" pattern match and delivers a single message to the application with the matching pattern.

The ObjectPak message facility then dispatches the message received by the application to each action registered for the matching operator, regardless of any other pattern information you provided when registering the action. Your action is responsible for testing the message arguments and determining whether to process the message or not.

Example

As an example, consider an application that registers an action for a message pattern consisting of a "show" operator and an integer argument. If another process then sends a "show" message with an integer argument, the ToolTalk service sends the message to your application, and your application dispatches the message to the action. On the other hand, if another process sends a "show" message with a character string argument, the ToolTalk service does not send the message to your application because it does not match the registered pattern.

A complication arises if you have multiple actions registered for a particular message operator; for example, if in addition to the action described above, your application registered an action for a message pattern consisting of a "show" operator and a character string argument. In this case, the ToolTalk service sends to your application any "show" message with either an integer or a character string argument. The ObjectPak message facility then dispatches those messages to each action in your application registered for the "show" operator. Each action is then responsible for testing the message arguments and determining whether to process the message or not.

Writing message action callbacks

You implement message actions as callback functions that your application invokes when it receives a message matching a given message pattern. This section describes how to write the message action callback functions. "Creating and registering simple message patterns" describes how to register these callback functions with the ObjectPak message facility.

All message action callback functions must be of type VkMsgClientAction:

typedef Boolean (*VkMsgClientAction)(
void* /* clientData */,
VkMsgFacilityReason /* reason */,
VkMsgMessage /* msg_in */,
char* /* op */,
int /* argc */,
VkMsgArg* /* argv */
)

Note: The VkMsgClientAction function must be a regular function or a static member function; it cannot be a regular class member function.


Callback arguments

The callback function arguments are as follows:

clientData The arbitrary client data you provided as the clientData argument when you registered this callback function using the addAction() function, as described in "Creating and registering simple message patterns" .

reason The reason for calling the callback function, expressed as an enumerated value of type VkMsgFacilityReason. Possible values are:

VK_MSG_FACILITY_NOTICE--Notice

VK_MSG_FACILITY_REQUEST--Request

VK_MSG_FACILITY_REPLY--Reply to a previous request

VK_MSG_FACILITY_FAILED--Request failed (not handled)

VK_MSG_FACILITY_STARTED--Request caused autostart of the handler and was queued

msg_in The incoming message in the form of an opaque message handle. You can use this handle when calling the various utility functions provided by the ViewKit ObjectPak message facility as described in "Useful functions when handling messages" . For example, you might want to compare returned values against those you sent in your request.

op The message operator

argc The number of message arguments

argv The message arguments, passed as an array of VkMsgArg structures.

The format of the VkMsgArg structure, for passing message arguments, is:

typedef struct {
char *type;
VkMsgValue value;
VkMsgMode mode;
} VkMsgArg

Structure elements

The elements of the structure are as follows:

type The argument type. This can take any of three pre-defined constant values:

VK_MSG_INT_MSG_ARG_TYPE--integer value

VK_MSG_STRING_MSG_ARG_TYPE--character string value

VK_MSG_BSTRING_MSG_ARG_TYPE--binary string value

type The argument value, expressed as a VkMsgValue union. The definition of the VkMsgValue union is:

typedef union {

int ival;

char *val;

VkMsgBValue bval;

} VkMsgValue

 

typedef struct {

unsigned char *val;

int len;

} VkMsgBValue

type The argument mode, expressed as an enumerated value of type VkMsgMode. Possible values are:

VK_MSG_IN--the argument is written by the sending application and can be read by the handling application and any observing applications.

VK_MSG_OUT--the argument is written by the handling applications and is read by the sending application.

VK_MSG_INOUT--the argument can be written by both the sending and handling applications, and can be read by the sending, handling, or any observing applications.


Note: Remember that the ViewKit ObjectPak message facility dispatches the messages received by the application to each action registered for the matching operator, regardless of any other pattern information you provided when registering the actions. Your actions are responsible for testing the message arguments and determining whether or not to process the messages they receive.


Callbacks that process notices

Message action callbacks that process notices are relatively straightforward to write; the callback simply examines the message data and performs any actions required by the application. You can use the various utility functions provided by the ObjectPak message facility, as described in "Useful functions when handling messages" , to parse the message. Notice callbacks are not expected to send reply messages.

Callbacks that process requests

Message action callbacks that process requests must first decide whether or not to handle the request. If so, the callback should perform the following steps:

  1. Read any required data from the message's "in" or "in/out" arguments.
  2. Perform any appropriate actions.
  3. Modify any "out" or "in/out" arguments.
  4. Send the reply message using VkMsgReply():
    VkMsgStatus VkMsgReply(VkMsgMessage msg)

VkMsgReply() is implemented as a normal function rather than a class member function. It is declared in the header file <Vk/VkMsg.h>.

After performing any appropriate actions, your message action callback should return a Boolean value to indicate whether or not the ViewKit ObjectPak message facility should propagate the message to other callbacks registered for that action. A Boolean value of FALSE propagates the message; a value of TRUE does not propagate the message.

If a message action callback that processes a request decides to handle a request, it should return TRUE to prevent other message action callbacks from attempting to handle the request as well. If the callback decides not to handle a request, it should return FALSE to allow other callbacks to attempt to handle the message. If all of an application's message actions reject a message, the ToolTalk service tries to dispatch the request to another application.

You should always return FALSE in message action callbacks that process notice messages.

The ViewKit ObjectPak message facility automatically destroys request messages when your application receives the corresponding reply or failure message. If for some reason you need to explicitly destroy a request message, call the VkMsgDestroyRequest() function:

VkMsgStatus VkMsgDestroyRequest(VkMsgMessage msg)

VkMsgDestroyRequest() is implemented as a normal function rather than a class member function. It is declared in the header file <Vk/VkMsg.h>. The VkMsgStatus return value is the same as the tt_status values used by ToolTalk; consult the ToolTalk Programmer's Guide for information on these values.

Useful functions when handling messages

The ObjectPak message facility also provides several utility functions for manipulating messages. These functions are implemented as normal functions rather than class member functions. Most of these utility function are actually redefined ToolTalk functions. The ObjectPak message facility provides this level of indirection to allow messaging services other than ToolTalk to be used. You should never directly call any of the ToolTalk routines in your application. Similarly, all of the ToolTalk constants (TT_*) have been replaced by ObjectPak message facility equivalents (VK_MSG_*).

VkMsgTypeIsInt(), VkMsgTypeIsString(), and VkMsgTypeIsBString() check to see whether a given argument is an integer, a character string, or a binary string respectively:

Boolean VkMsgTypeIsInt(char *atype)
Boolean VkMsgTypeIsString(char *atype)
Boolean VkMsgTypeIsBString(char *atype)

The header file <Vk/VkMsgUtils.h> contains these declarations.

VkMsgSetIVal(), VkMsgSetVal(), and VkMsgSetBVal() change the integer, character string, or binary string value, respectively, of a given message argument:

VkMsgSetIVal(VkMsgMessage msg, int index, int value)
VkMsgSetVal(VkMsgMessage msg, int index, char *value)
VkMsgSetBVal(VkMsgMessage msg, int index,
unsigned char *value, int len)

The header file <Vk/VkMsg.h> contains these declarations.

Parsing a messages arugments

To parse a message's arguments into a VkMsgArg structure (as described in "Writing message action callbacks" ), use the VkMsgParseArguments() function:

void VkMsgParseArguments(VkMsgMessage msg, int *argc_return, VkMsgArg **argv_return)

This function is declared in the header file <Vk/VkMsgUtils.h>. You must free the argv result when done.

Retrieving a file attribute

To retrieve the file attribute associated with a message, use function VkMsgFile():

char *VkMsgFile(VkMsgMessage msg)

This function is declared in the header file <Vk/VkMsg.h>. You are responsible for freeing the returned value when you are finished using it. If there is no file attribute, VkMsgFile() returns NULL.

VkMsgIsErr() checks to see whether a VkMsgStatus value is an error status:

int VkMsgIsErr(VkMsgStatus status)

This function is declared in the header file <Vk/VkMsg.h>. If the VkMsgStatus value is a warning, VkMsgIsErr() returns 0; if it is an error VkMsgIsErr() returns 1.

VkMsgPtrError() determines whether a given opaque handle returned by an ObjectPak message facility function is valid:

VkMsgStatus VkMsgPtrError(void *pointer)

This function is declared in the header file <Vk/VkMsg.h>. You can use this function to test for valid message handles or pattern handles. If the handle is valid, VkMsgPtrError() returns the constant VK_MSG_OK.

VkMsgFail() informs the ToolTalk service that your process cannot handle this request and that the message should not be offered to other processes of the same ptype as yours:

VkMsgStatus VkMsgFail(VkMsgMessage msg)

This function is declared in the header file <Vk/VkMsg.h>. The ToolTalk service sends the message back to the sender with state VK_MSG_FAILED.

VkMsgReject() informs the ToolTalk service that your process cannot handle this message:

VkMsgStatus VkMsgReject(VkMsgMesage msg)

This function is declared in the header file <Vk/VkMsg.h>. The ToolTalk service will try other handlers.

Creating and registering simple message patterns

To use your message actions, you must associate them with message patterns and then register those patterns with the ObjectPak message facility.

Use the VkMsgClient::addAction() function to create a message pattern and associate a message action with that pattern:

VkMsgPattern addAction(char *op, VkMsgClientAction proc,
void *clientData, VkMsgActionType type,
Boolean deleteMessage = TRUE)

Note: Register your application's message actions before executing VkApp::run() or posting any ObjectPak dialog. You must register your message actions before entering any Xt event loop; otherwise your application might receive messages before registering message actions, and your application will not process the message as expected.


addAction() creates and registers a simple message pattern consisting of only a message operator, op. You can create more detailed message patterns using the functions described in "Creating more detailed message patterns" . After your application registers this pattern, it receives all messages sent that contain this operator. addAction() returns an opaque message pattern handle. You use this handle to remove this action with the removeAction() function described later in this section.

The proc argument is the callback function invoked when your application receives a message matching the message pattern. The function must be of type VkMsgClientAction. The ObjectPak message facility passes the clientData argument to the function as client data. See "Writing message action callbacks" for information on writing message action callbacks.

VkMsgActionType
values

The type argument specifies the type of message processing the message action implements. type is an enumerated value of type VkMsgActionType, which can take any of the following values:

VK_MSG_ACTION_OBSERVE Observe messages; use to process notices

VK_MSG_ACTION_HANDLE Handle messages; use to process requests

VK_MSG_ACTION_REPLY Process replies to a request

VK_MSG_ACTION_FAIL Process request failures

VK_MSG_ACTION_START Process notices of message handler starting

The deleteMessage argument determines whether or not your application automatically deletes the message after all actions process it. By default, the ObjectPak message facility automatically destroys the message and your message actions don't need to worry about it. However, if you want to save a copy of the message to deal with it later (for example, to send a reply), you must set deleteMessage to False.

After you've created your message patterns, you must call VkMsgClient::updatePatterns() to register those patterns:

void updatePatterns()

The VkMsgClient::removeAction() function removes an action:

void removeAction(VkMsgPattern pat)

pat is the message pattern returned by addAction(). removeAction() automatically unregisters the associated message pattern from the ObjectPak message facility.

Creating more detailed message patterns

The message pattern created by addAction() consists of only the message operator string. Any message received with that operator matches the particular message pattern and is dispatched to your action.

You can create more detailed message patterns, adding arguments and attributes, to restrict the messages that the ToolTalk service dispatches to your application. To create a more detailed pattern, you first create a basic message pattern using the VkMsgClient::createAction() function:

VkMsgPattern createAction(char *op, VkMsgClientAction proc,
void *clientData, VkMsgActionType type,
Boolean deleteMessage = TRUE)

createAction() accepts the same arguments as addAction(). Like addAction(), it creates a simple message pattern consisting of only a message operator, op, and associates the message action, proc, with that pattern. Unlike addAction(), createAction() does not automatically register the pattern with the ObjectPak message facility. Instead, you can specify additional arguments or attributes to the message pattern using various pattern modifier functions.

All of the pattern modifier functions are implemented as normal functions rather than class member functions. They are actually redefined ToolTalk functions. The header file <Vk/VkMsg.h> contains these declarations. The ObjectPak message facility provides this level of indirection to allow messaging services other than ToolTalk to be used. You should never directly call any of the ToolTalk routines in your application.

Consult the <Vk/VkMsg.h> header file for a list of pattern modifier functions you might find useful. Refer to the ToolTalk Programmer's Guide for information on these functions.

After modifying the message pattern, call VkMsgClient::registerPattern() to register the pattern with the ObjectPak message facility:

VkMsgStatus registerPattern(VkMsgPattern pat)

pat is the opaque message pattern handle returned by createAction(). registerPattern() returns a VkMsgStatus value indicating its status. The VkMsgStatus values are the same as the tt_status values used by ToolTalk; consult the ToolTalk Programmer's Guide for information on these values.

After you've created your message patterns, you must call VkMsgClient::updatePatterns() to register those patterns:

void updatePatterns()

You can unregister a message pattern with the VkMsgClient::unregisterPattern() function:

VkMsgStatus unregisterPattern(VkMsgPattern pat)

pat is the opaque message pattern handle returned by createAction(). unregisterPattern() returns a VkMsgStatus value indicating its status. The VkMsgStatus values are the same as the tt_status values used by ToolTalk; consult the ToolTalk Programmer's Guide for information on these values.

Example

As an example of creating a detailed message pattern, consider the following code sample:

pat = createAction("message_op", callback, this, VK_MSG_ACTION_HANDLE);
VkMsgPatternArg(pat, VK_MSG_IN, VK_MSG_INT_MSG_ARG_TYPE, NULL)
VkMsgPatternArg(pat, VK_MSG_OUT, VK_MSG_ALL_MSG_ARG_TYPE, NULL);
VkMsgPatternIArg(pat, VK_MSG_IN, VK_MSG_INT_MSG_ARG_TYPE, 5);
registerPattern(pat);
updatePatterns();

This example creates a message pattern that matches messages with the operator "message_op," and has three arguments. The first is an "in" integer argument. The second is an "out" argument of any type. The last is an "in" integer argument with value 5.

Detecting and Handling Errors in Handling Requests

Error types

The two kinds of errors that can occur when a request is made and a reply is expected are as follows:

  • Either no one handles the request (and ToolTalk could not autostart an appropriate service)
  • Someone does handle the request and replies, but some error occurs while handling the request.

For instance, a request to "Raise the Mail Tool" would fail if there is no mail tool, since no reply could be received. In this case, if you had registered a VK_MSG_ACTION_FAIL type message action for the "Raise the Mail Tool" operator, your application would call that message action. If you were using VkMsgClient::sendSyncRequest(), it would return NULL.

If your request is successfully sent to a handler, but an error occurs while processing the request, most handlers send a reply and indicate that an error condition occurred. Many handlers return a status code indicating the return status. To obtain a reply status code, call function VkMsgStatusCode():

int VkMsgStatusCode(VkMsgMessage msg)

Furthermore, some handlers provide useful error strings to display in the case of errors. To obtain a reply error string, call function VkMsgStatusString():

char *VkMsgStatusString(VkMsgMessage msg)

The meaning of status codes and the validity of status strings depend on the service handling the request. Both functions are implemented as normal function rather than class member functions. They are actually redefined ToolTalk functions. Header file <Vk/VkMsg.h> contains these declarations.