Legacy Documentclose button

Important: The information in this document is obsolete and should not be used for new development.

Previous Book Contents Book Index Next

Inside Macintosh: Macintosh Toolbox Essentials /
Chapter 2 - Event Manager / Using the Event Manager


Handling High-Level Events

High-level events provide a means of communication between applications. Apple events are high-level events that follow the Apple Event Interprocess Messaging Protocol (AEIMP). In most cases, you should use Apple events rather than define your own high-level events if you wish to communicate with other applications. If you plan to use Apple events, see Inside Macintosh: Interapplication Communication for specific information on Apple events, and refer to this section for specific details about how the Event Manager reports high-level events.

To receive high-level events, you must set the appropriate flags in your application's 'SIZE' resource. You must set the isHighLevelEventAware flag if your application
is to receive any high-level events. You must set the localAndRemoteHLEvents
flag for your application to receive high-level events sent from another computer on
the network. In addition, to receive high-level events from another computer, your application must be shared and Program Linking must be enabled. The user shares your application by selecting your application in the Finder and choosing Sharing from the File menu and enables Program Linking from the Sharing Setup control panel.

If you set the isHighLevelEventAware flag in your application's 'SIZE' resource, your application receives the Finder information in the form of Apple events. The Finder information is the information your application can use to determine which files to open or print. Your application must respond to the required Apple events (Open Application, Open Documents, Print Documents, and Quit Application) that are sent by the Finder if your application sends or receives high-level events.

The what field in the event record of a high-level event contains the kHighLevelEvent constant.

To determine the type of high-level event received, your application needs to examine the message and where fields of the event record. For high-level events, these two fields of the event record have special meanings.

The message field and the where field of the event record together define the specific type of high-level event received. Your application should interpret these fields as having the data type OSType, not LongInt or Point.

The message field contains the event class of the high-level event. For example, Apple events sent by the Edition Manager have the event class 'sect'. You can define your own group of events that are specific to your application. If you have registered your application signature with Apple Computer, Inc., then you can use your signature to define the class of events that belong to your application. Note, however, that Apple reserves the use of all event classes whose names contain only lowercase letters and nonalphabetic characters.

For high-level events, the where field in the event record contains a second message specifier, called the event ID. The event ID defines the particular type of event (or message) within the class of events defined by the event class. For example, the Section Read event sent by the Edition Manager has event class 'sect' and event ID 'read'. The Open Documents event sent by the Finder has event class 'aevt' and event ID 'odoc'. You can define your own set of event IDs corresponding to your own event class. For example, if the message field contains 'biff' and the where field contains 'cmd1', then the high-level event indicates the type of event defined by 'cmd1' within the class of events defined by the application with the signature 'biff'.

Note
If your application supports Apple events, you can call the AEProcessAppleEvent function to determine the type of Apple event received, rather than examining the message and where fields.
Note that because the where field of an event record for a high-level event is used to select a specific kind of event (within the class determined by the message field), high-level event records do not contain the mouse location at the time of the event. You should not interpret the where field before interpreting the what field because different event classes can contain overlapping sets of event IDs.

Unlike low-level events and operating-system events, high-level events may not be completely determined by the event record returned to your application when it calls WaitNextEvent. For example, you might still need to know which other application sent you the high-level event or what additional data that application wants to send you. Your application can obtain this further information about the high-level event by calling the AcceptHighLevelEvent function. The additional information associated with a high-level event includes

To obtain this additional information, your application must call AcceptHighLevelEvent before calling WaitNextEvent again. By convention,
calling AcceptHighLevelEvent indicates that your application intends to process
the high-level event.

To accept an Apple event, call the AEProcessAppleEvent function instead of
the AcceptHighLevelEvent function. The Apple Event Manager also extracts
any additional information associated with the Apple event at your application's request. This chapter discusses how to accept high-level events using the AcceptHighLevelEvent function; for information on the AEProcessAppleEvent function, see Inside Macintosh: Interapplication Communication.

Responding to Events From Other Applications

You can identify high-level events by the value in the what field of the event record. The message and where fields further classify the type of high-level event. Your application can choose to recognize as many events as are appropriate. Some high-level events may be fully specified by their event record only, while others may include additional information in an optional buffer. To get that additional information or to find the sender of the event, use the AcceptHighLevelEvent function.

Note
To respond to an Apple event, use the Apple Event Manager, as described in Inside Macintosh: Interapplication Communication. u
Listing 2-16 on the next page illustrates how to respond to a high-level event.

The DoHighLevelEvent procedure in Listing 2-16 first determines the type of high- level event received by checking the message and where fields of the event record. It then uses AcceptHighLevelEvent to get any additional data associated with the event. This particular application recognizes only one type of high-level event. If the event is not of this type, the code assumes that the event is an Apple event and calls AEProcessAppleEvent to handle the event.

In general, you cannot know in advance how big the optional data buffer is, so you can allocate a zero-length buffer and then resize it if the call to AcceptHighLevelEvent returns the bufferIsSmall result code.

Listing 2-16 Accepting a high-level event

PROCEDURE DoHighLevelEvent (event: EventRecord);
VAR
   myTarg:     TargetID;      {target ID record}
   myRefCon:   LongInt;
   myBuff:     Ptr;
   myLen:      LongInt;
   myErr:      OSErr;
BEGIN
   IF (event.message = LongInt(kMySpecialHLEventClass)) AND
      (LongInt(event.where) = LongInt(kMySpecialHLEventID)) THEN
   BEGIN
      {it's a high-level event that doesn't use AEIMP}
      myLen := 0;                {start with a 0-byte buffer}
      myBuff := NIL;
      myErr:=AcceptHighLevelEvent(myTarg,myRefCon, myBuff, myLen);
      IF myErr = bufferIsSmall THEN
      BEGIN
         myBuff := NewPtr(myLen);{allocate needed storage}
         myErr := AcceptHighLevelEvent(myTarg, myRefCon, myBuff,
                                       myLen);
         IF myErr = noErr THEN
            ;  {perform any action requested by the event}
      END;
      IF myErr <> noErr THEN
         DoError(myErr);{perform the necessary error handling}
   END
   ELSE
   BEGIN {otherwise, assume that the event is an Apple event}
      myErr := AEProcessAppleEvent(event);
      IF myErr <> noErr THEN
         DoError(myErr);{perform the necessary error handling}
   END;
END;
The AcceptHighLevelEvent function returns additional information and data associated with the event. The ID of the sender of the event is returned in the first parameter, which is a target ID record. You can inspect the fields of that record to determine which application sent the event. The target ID record contains the session reference number that identifies the connection with the other application as well as the port name and location name of the sender. If the high-level event requires that you return information, you can use the information returned in the target ID record to
send an event back to the requesting application. See "Determining the Sender of a High-Level Event" on page 2-72 and "Sending High-Level Events" on page 2-73 for specific information on the target ID record.

The second parameter to AcceptHighLevelEvent, the reference constant parameter, is a unique number that identifies the request associated with the event or identifies that the particular event is related to a request from a previous event. If you send a response to this event, you should use the same value for the reference constant so that the sender of the event can associate the reply with the original request.

The third parameter points to any additional data associated with the event. Any data
in this additional buffer is defined by the particular high-level event. On input, the fourth parameter to AcceptHighLevelEvent, the length parameter, contains the
size of the buffer. If no error occurs, on output the length parameter contains the size
of the message accepted. If the AcceptHighLevelEvent function returns the result code bufferIsSmall, the length parameter contains the size of the message yet to
be received.

Searching for a Specific High-Level Event

Sometimes you do not want to accept the next available high-level event pending for your application. Instead, you might want to select one event from among all the high-level events in your application's high-level event queue. For example, you might want to look for a return receipt for a high-level event you previously posted before processing other high-level events.

You can select a specific high-level event by calling the GetSpecificHighLevelEvent function. One of the parameters you pass to this function is a filter function that you provide. Your filter function should examine an event in your application's high-level event queue and determine whether it is the kind of event you wish to receive. If it is, your filter function returns TRUE. This indicates that your filter function does not want to inspect any more events. If the filter function finds an event of the desired type, it should call AcceptHighLevelEvent to retrieve the event. When your function returns TRUE, the GetSpecificHighLevelEvent function itself returns TRUE.

If your filter function returns FALSE for an event in the high-level event queue, then GetSpecificHighLevelEvent looks at the next event in the high-level event queue and executes your filter function. If the filter function returns FALSE for all the high-
level events in the queue, then GetSpecificHighLevelEvent itself returns FALSE to your application.

Here's how you declare the filter function whose address you pass to the GetSpecificHighLevelEvent function:

FUNCTION MyFilter (yourDataPtr: Ptr; 
                   msgBuff: HighLevelEventMsgPtr;
                   sender: TargetID): Boolean;
When your application calls GetSpecificHighLevelEvent, you pass it a parameter that indicates the criteria your filter function should use to search for a specific event. The GetSpecificHighLevelEvent function passes this information to your filter function in the yourDataPtr parameter. The GetSpecificHighLevelEvent function also provides your filter function with information about the event record of the high-level event in the msgBuff parameter as well as information about the sender of the high-level event in the sender parameter.

The msgBuff parameter contains a pointer to a high-level event message record that has this structure:

TYPE  HighLevelEventMsg =
      RECORD
         HighLevelEventMsgHeaderLength:      Integer;
         version:                            Integer;
         reserved1:                          LongInt;
         theMsgEvent:                        EventRecord;
         userRefCon:                         LongInt;
         postingOptions:                     LongInt;
         msgLength:                          LongInt;
      END;

      HighLevelEventMsgPtr= ^HighLevelEventMsg;
When you call GetSpecificHighLevelEvent and it executes your filter function for a high-level event waiting in the high-level event queue, the fields of the high-level event message record are filled in by the Event Manager. You can then compare the fields of this record to the information in the yourDataPtr parameter to determine whether that event suits your needs. For example, the yourDataPtr parameter might contain the signature of a return receipt. You can test its value against the event class of the event record contained in the theMsgEvent field of the high-level event message record.

Determining the Sender of a High-Level Event

When you receive a high-level event, part of the information returned by AcceptHighLevelEvent is the identity of the sender of the event. You can use that information to respond selectively to requests made by other applications or to find which application to send any replies to. The information about the sender is provided in the form of a target ID record, defined as follows:

TYPE  TargetID = 
      RECORD
         sessionID:  LongInt;          {session reference number}
         name:       PPCPortRec;       {sender's port name}
         location:   LocationNameRec;  {sender's location name}
         recvrName:  PPCPortRec;       {reserved}
      END;
The sessionID field corresponds to the session reference number created by the PPC Toolbox. This is a 32-bit number that uniquely identifies a PPC Toolbox session (or connection) with another application. The name and location fields contain the sender's port name and location name. If the sending application is on the same computer as the receiving application, you can determine the sending application's process serial number by calling the GetProcessSerialNumberFromPortName function.

Sending High-Level Events

You use the PostHighLevelEvent function to send a high-level event to another application. When doing so, you need to provide six pieces of information:

Note
To send an Apple event, use the Apple Event Manager function
AESend. The Apple Event Manager uses the Event Manager to post Apple events. For information on posting Apple events, see Inside Macintosh: Interapplication Communication. u
When you post a high-level event to an application on the same computer, you can specify its recipient in one of four ways:

To specify the recipient of a high-level event sent across a network, you can use only
the receiving application's port name and location name or its session reference number. You can use any of the four ways when sending high-level events to applications on the local computer.

You specify the recipient of a high-level event in the receiverID parameter when you use the PostHighLevelEvent function. To specify a port name and location name, provide the address of a target ID record in the receiverID parameter. To specify a process serial number, provide its address in the receiverID parameter. To specify a session reference number, or signature, provide the data in the receiverID parameter.

When you are replying to a high-level event, it is easy to identify the recipient because you can use the target ID record that you receive from AcceptHighLevelEvent, the session reference number contained in that target ID record, or the process serial number (if the receiving process is local). Note that replying by session reference number is always the fastest way to respond to a high-level event.

When you are not replying to a previous event, you need to determine the identity of
the target application yourself. You can use one of several methods to do this. If the target application is on the local computer, you can search for that application's creator signature or its process serial number by calling the GetProcessInformation function. See the chapter "Process Manager" in Inside Macintosh: Processes for a detailed explanation of the GetProcessInformation function and for examples of how to use it to generate a list of process serial numbers of all open processes on the local computer.

If the application to which you want to send a high-level event is located on a remote computer, you need to identify it either by its session reference number or by its port name and location name. You can call the PPCBrowser function to let the user browse for a specific port. You can call the IPCListPorts function to obtain a list of all ports registered with the target PPC Toolbox. See the chapter "Program-to-Program Communications Toolbox" in Inside Macintosh: Interapplication Communication for an explanation of both of these functions.

As just described, you can identify the recipient of the high-level event in one of four ways. Listing 2-17 illustrates how to send a high-level event to an application on the local computer using the application's creator signature. In this example, an application is sending a high-level event to the application with the creator signature of 'boff'. The specific high-level event being sent is identified by the event class 'boff' and the event ID 'cmd1'.

Listing 2-17 Posting a high-level event by application signature

PROCEDURE MyPostTest;
VAR
   myEvent:    EventRecord;   {an event record}
   myRecvID:   OSType;        {receiver ID}
   myOpts:     LongInt;       {posting options}
   myErr:      OSErr;
BEGIN
   myEvent.what := kHighLevelEvent;
   myEvent.message := LongInt('boff');       {event class}
   myEvent.where := Point(LongInt('cmd1'));  {event ID}
   {the receiver is identified by its signature and }
   { a return receipt is requested}
   myOpts := receiverIDisSignature + nReturnReceipt;
   myRecvID := 'boff';                    {receiver's signature}
   myErr := PostHighLevelEvent(myEvent, Ptr(myRecvID), 0, NIL, 0, 
                                 myOpts);
   IF myErr <> noErr THEN
      DoError(myErr);
END;
In this example of using the PostHighLevelEvent function, there is no additional data to transmit, so the sending application provides NIL as the pointer to the data buffer and sets the buffer length to 0. The myOpts variable specifies posting options.

Posting options are of two types: delivery options and options associated with the receiverID parameter. You can specify one or more delivery options to indicate if you want the other application to receive the event at the next opportunity and to indicate if you want acknowledgment that the other application received the event. You use the options associated with the receiverID parameter to indicate how you are specifying the recipient of the event. To set the various posting options, use these constants:

CONST nAttnMsg                = $00000001;{give this message priority}
      nReturnReceipt          = $00000200;{return receipt requested}
      receiverIDisTargetID    = $00005000;{ID is port name and location name}
      receiverIDisSessionID   = $00006000;{ID is PPC session ref number}
      receiverIDisSignature   = $00007000;{ID is creator signature}
      receiverIDisPSN         = $00008000;{ID is process serial number}
When you specify the receiving application in the receiverID parameter, you can use these constants to specify the receiver of the event by port name and location name, session reference number, process serial number, or signature. Any of these specifications allows you to send an event to another application on the local computer. For example, in Listing 2-17 the myOpts variable indicates that the receiver is identified by its creator signature, and the myRecvID variable contains the receiver's creator signature. To send events to an application on a remote computer, you can specify the recipient only by the session reference number or by the port name and location name.

When you specify the receiver of the event by port name and location name, use the receiverIDisTargetID constant in the posting options parameter and specify the address of a target ID record in the receiverID parameter.

TYPE  TargetID = 
      RECORD
         sessionID:  LongInt;       {unused for posting}
         name:       PPCPortRec;    {recipient's port name}
         location:   LocationNameRec;{recipient's port loc}
         recvrName:  PPCPortRec;    {unused for posting}
      END;
When you pass a target ID record, you need to specify only the name and location fields. You can use the IPCListPorts function to list all of the existing port names along with information on whether the port will accept authenticated service on the computer specified by the location name. For information on how to use the IPCListPorts function, see the chapter "Program-to-Program Communications Toolbox" in Inside Macintosh: Interapplication Communication.

You can also use the PPCBrowser function to fill in a target ID record. Listing 2-18 on the next page illustrates how to use the PPCBrowser function to post a high-level event. In this example, the sending application wants to locate a dictionary application and have the dictionary return the definition of a word to it.

Listing 2-18 Using the PPCBrowser function to post a high-level event

FUNCTION MyPostWithPPCBrowser (aTextPtr: Ptr; textlength: LongInt): OSErr;
VAR
   myHLEvent:     EventRecord;
   myErr:         OSErr;
   myNumTries:    Integer;
   myPortInfo:    PortInfoRec;
   myTarget:      TargetID;
BEGIN
   {use PPCBrowser to get the target}
   myErr := PPCBrowser('Select an Application', 'Application', FALSE,
                       myTarget.location, myPortInfo, NIL, '');
   IF myErr = NoErr THEN
   BEGIN
      {copy port name into myTarget.name}
      myTarget.name := myPortInfo.name;
      
      myHLEvent.what := kHighLevelEvent;
      myHLEvent.message := LongInt('Dict');
      myHLEvent.where := Point(LongInt('Defn'));

      {if a connection is broken, then sessClosedErr is returned to }
      { PostHighLevelEvent; to reestablish the connection, just post }
      { the event one more time}
      myNumTries := 0;
      REPEAT
         myErr := PostHighLevelEvent(myHLEvent, @myTarget, 0, aTextPtr,
                                     textlength, receiverIDisTargetID);
         myNumTries := myNumTries + 1;
      UNTIL (myErr <> sessClosedErr) OR (myNumTries > 1);
   END;
   MyPostWithPPCBrowser := myErr;   {return any error}
END;
The application-defined function in Listing 2-18 uses the PPCBrowser function to display a dialog box asking the user to select a dictionary. (For additional information
on the PPCBrowser function, see Inside Macintosh: Interapplication Communication.) If
the user selects a dictionary, this code posts a high-level event to that dictionary application asking for the definition of the selected text. Note that the sending application and the receiving application must both agree that definition queries are to be of event class 'Dict' and event ID 'Defn'. It is necessary to define a private protocol only in cases in which no suitable Apple event exists.

Note
You should avoid passing handles to the receiving application in an attempt to share a block of data. It is better to put the relevant data into a buffer (as illustrated in Listing 2-18) and pass the address of the buffer. If you absolutely must share data by passing a handle, make sure that the block of data is located in the system heap. u
If a high-level event is posted successfully, PostHighLevelEvent returns the result code noErr, which indicates only that the event was successfully passed to the PPC Toolbox. Your application needs to call another Event Manager routine (EventAvail, GetNextEvent, or WaitNextEvent) to give the other application an opportunity to receive the event.

The event you send might require the other application to return some information to your application by sending a high-level event back to your application. You can scan for the response by using GetSpecificHighLevelEvent. If your application must wait for this event, you might want to display a wristwatch cursor or take other action as appropriate to your application. You also might want to implement a timeout mechanism in case your application never receives a response to the event.

Requesting Return Receipts

When you post a high-level event, you can request a return receipt by including the nReturnReceipt constant as one of the posting options. This requests that the Event Manager send your application a high-level event that tells you whether the other application accepted your event. Note that this does not necessarily mean that the other application performed any action you might have requested from it.

A return receipt is a high-level event having an event class and an event ID indicated by these two constants:

CONST HighLevelEventMsgClass  = 'jaym';
      rtrnReceiptMsgID        = 'rtrn';
Return receipts are posted by the Event Manager on the computer of the receiving application (and not by the receiving application itself). No data buffer is associated with a return receipt. However, the posting Event Manager sets the modifiers field of the high-level event record to one of the following values:

CONST msgWasNotAccepted       = 0;
      msgWasFullyAccepted     = 1;
      msgWasPartiallyAccepted = 2;
The msgWasNotAccepted constant indicates that your event was not accepted by
the receiving application. This means that the receiving application was notified
of the arrival of your event (through WaitNextEvent) but did not call AcceptHighLevelEvent to accept the event. The msgWasFullyAccepted constant indicates that the receiving application did call AcceptHighLevelEvent and retrieved all the data in the optional data buffer. The msgWasPartiallyAccepted constant indicates that the receiving application called AcceptHighLevelEvent, but the application's data buffer was too small to hold the data sent with your application, and the receiving application called WaitNextEvent before retrieving the rest of the buffer.

Note that a return receipt does not indicate the identity of the receiving application. To determine on whose behalf the Event Manager has sent you a particular return receipt, you need to call AcceptHighLevelEvent. When AcceptHighLevelEvent returns successfully, the sender parameter contains a target ID record with the fields filled in for the receiving application. With return receipts, the msgLen parameter is 0, the msgBuff parameter is NIL, and the msgRefCon parameter contains the unique number of the refCon parameter of the original high-level event sender (that is, your application).

Handling Apple Events

If your application uses high-level events, your application must respond to the
required Apple events sent by the Finder. The four required Apple events are Open Application, Open Documents, Print Documents, and Quit Application. See Inside Macintosh: Interapplication Communication for information on how to handle the required Apple events.

When your application receives a high-level event (as indicated by the kHighLevelEvent constant in the what field of the event record), and if your application supports Apple events, call the AEProcessAppleEvent function. The AEProcessAppleEvent function provides an easy way for your application to identify the event class and event ID of the Apple event and to direct the Apple Event Manager to call the code in your program that handles the Apple event.

To send Apple events to other applications, use the AESend function.

To ensure compatibility and smooth interaction with other Macintosh applications, you should use the Apple event protocol for high-level events whenever possible. By implementing the capabilities to send Apple events to and receive Apple events from other applications, you allow other applications to interact with your application and provide enhanced capabilities to your users.

See Inside Macintosh: Interapplication Communication for complete information on how to send and receive Apple events.


Previous Book Contents Book Index Next

© Apple Computer, Inc.
11 JUL 1996