PMGuide - Messages and Message Queues: Difference between revisions
Line 354: | Line 354: | ||
! Prefix !! Message category | ! Prefix !! Message category | ||
|- | |- | ||
| | | BKM_ || Notebook control | ||
|- | |- | ||
| | | BM_ || Button control | ||
|- | |- | ||
| <tt>:hp4.CBM_:ehp4.</tt> || Combination-box control | | <tt>:hp4.CBM_:ehp4.</tt> || Combination-box control | ||
Line 388: | Line 388: | ||
* Menu- and dialog-input | * Menu- and dialog-input | ||
* Window creation and management | * Window creation and management | ||
* Dynamic data exchange (DDE) | * Dynamic data exchange (DDE) | ||
==== Application-Defined Messages ==== | ==== Application-Defined Messages ==== |
Revision as of 03:25, 29 April 2025
The OS/2 operating system uses messages and message queues to communicate with applications and the windows belonging to those applications. This chapter explains how to create and use messages and message queues in PM applications.
About Messages and Message Queues
Unlike traditional applications that take complete control of the computer's keyboard, mouse, and screen, PM applications must share these resources with other applications that are running at the same time. All applications run independently and rely on the operating system to help them manage shared resources. The operating system does this by controlling the operation of each application, communicating with each application when there is keyboard or mouse input or when an application must move and size its windows.
Messages
A message is information, a request for information, or a request for an action to be carried out by a window in an application.
The operating system, or an application, sends or posts a message to a window so that the window can use the information or respond to the request.
There are three types of messages:
- User-initiated
- Application-initiated
- System-initiated
A user-initiated message is the direct result of a user action, such as selecting a menu item or pressing a key. An application-initiated message is generated by one window in the application to communicate with another window. System -initiated messages are generated by the interface as the indirect result of a user action (for example, resizing a window) or as the direct result of a system event (such as creating a window).
A message that requires an immediate response from a window is sent directly to the window by passing the message data as arguments to the window procedure. The window procedure carries out the request or lets the operating system carry out default processing for the message.
A message that does not require an immediate response from a window is :hp1.posted :ehp1. (the message data is copied) to the application's :hp1.message queue:ehp1.. The message queue is a storage area that the application creates to receive and hold its posted messages. Then, the application can retrieve a message at the appropriate time, sending it to the addressed window for processing.
Every message contains a :hp1.message identifier:ehp1., which is a 16-bit integer that indicates the purpose of the message. When a window processes a message, it uses the message identifier to determine what to do.
Every message contains a :hp1.window handle:ehp1., which identifies the window the message is for. The window handle is important because most message queues and window procedures serve more than one window. The window handle ensures that the application forwards the message to the proper window.
A message contains two message parameters—32-bit values that specify data or the location of data that a window uses when processing the message. The meaning and value of a message parameter depend on the message. A message parameter can contain an integer, packed bit flags, a pointer to a structure that contains additional data, and so forth. Some messages do not use message parameters and, typically, set the parameters to NULL. An application always checks the message identifier to determine how to interpret the message parameters.
A queue message is a QMSG data structure that contains six data items, representing the window handle, message identifier, two message parameters, message time, and mouse-pointer position. The time and position are included because most queue messages are input messages, representing keyboard or mouse input from the user. The time and position also help the application identify the context of the message. The operating system posts a queue message by filling the QMSG structure and copying it to a message queue.
A window message consists of the window handle, the message identifier, and two message parameters. A window message does not include the message time and mouse-pointer position, because most window messages are requests to perform a task that is not related to the current time or mouse-pointer position. The operating system sends a window message by passing these values, as individual arguments, to a window procedure.
Message Queues
Every PM application must have a :hp1.message queue:ehp1.. A message queue is the only means an application has to receive input from the keyboard or mouse. :hp1.Only applications that create message queues can create windows:ehp1..
An application creates a message queue by using the WinCreateMsgQueue function. This function returns a handle that the application can use to access the message queue. After an application creates a message queue, the system posts messages intended for windows in the application to that queue. The application can retrieve queue messages by specifying the message-queue handle in the WinGetMsg function. It also can examine messages, without retrieving them, by using the WinPeekMsg function. When an application no longer needs the message queue, it can destroy the queue by using the WinDestroyMsgQueue function.
One message queue serves all the windows in a thread. This means a queue can hold messages for several windows. A message specifies the handle of the window to which it belongs so the application can forward a message easily to the appropriate window. The message loop recognizes a NULL window handle and the message is processed within the message loop rather than passed to WinDispatchMessage. See the following figure for an example of an input-message processing loop.
An application that has more than one thread can create more than one message queue. The system allows one message queue for each thread. A message queue created by a thread belongs to that thread and has no connection to other queues in the application. When an application creates a window in a given thread, the system associates the window with the message queue in that thread. The system then posts all subsequent messages intended for that window to that queue.
:hp2.Note: :ehp2. The recommended way to structure PM applications is to have at least two threads and two message queues. The first thread and message queue control all the user-interface windows, and the second thread and message queue control all the object windows.
Several windows can use one message queue; it is important that the message queue be large enough to hold all messages that possibly can be posted to it. An application can set the size of the message queue when it creates the queue by specifying the maximum number of messages the queue can hold. The default maximum number of messages is 10.
To minimize queue size, several types of posted messages are not actually stored in a message queue. Instead, the operating system keeps a record in the queue of the message being posted and combines any information contained in the message with information from previous messages. Timer, semaphore, and paint messages are handled this way. For example, if more than one WM_PAINT message is posted, the operating system combines the :hp1.update regions :ehp1. for each into a single update region. Although there is no actual WM_PAINT message in the queue, the operating system constructs one WM_PAINT message with the single update region when an application uses the WinGetMsg function.
The operating system handles mouse and keyboard input messages differently from the way it handles other types of messages. The operating system receives all keyboard and mouse events, such as keystrokes and mouse movements, into the system message queue. The operating system converts these events into messages and posts them, one at a time, to the appropriate application message queue. The application retrieves the messages from its queue and dispatches them to the appropriate window, which processes the messages.
The operating system message queue usually is large enough to hold all input messages, even if the user types or moves the mouse very quickly. If the operating system message queue does run out of space, the system :hp1.ignores :ehp1. the most recent keyboard input (usually by beeping to indicate the input is ignored) and collects mouse motions into a WM_MOUSEMOVE message.
Every message queue has a corresponding MQINFO data structure that specifies the identifiers of the process and thread that own the message queue and gives a count of the maximum number of messages the queue can receive. An application can retrieve the structure by using the WinQueryQueueInfo function.
A message queue also has a current status that indicates the types of messages currently in the queue. An application can retrieve the queue status by using the WinQueryQueueStatus function. An application also can use the WinPeekMsg function to examine the contents of a message queue. WinPeekMsg checks for a specific message or range of messages in the queue and gives the application the option of removing messages from the queue. An application :hp1.can :ehp1. call the WinQueryQueueStatus function to determine the contents of the queue before calling the WinPeekMsg or WinGetMsg function to remove a message from the queue.
Message Handling
To handle and process messages, an application can use a :hp1.message loop :ehp1. and the :hp1. window procedure:ehp1.. These terms are explained in the following two sections.
Message Loops
Every application with a message queue is responsible for retrieving the messages from that queue. An application can do this by using a message loop, usually in the application's main function, that retrieves messages from the message queue and dispatches them to the appropriate windows. The message loop consists of two calls: one to the WinGetMsg function; the other to the WinDispatchMsg function. The message loop has the following form:
HAB hab; QMSG qmsg; while (WinGetMsg(hab, &qmsg, NULL, 0, 0)) WinDispatchMsg(hab, &qmsg);
An application starts the message loop after creating the message queue and at least one application window. Once started, the message loop continues to retrieve messages from the message queue and to dispatch (send) them to the appropriate windows. WinDispatchMsg sends each message to the window specified by the window handle in the message.
The following figure illustrates the typical routing of an input message through the operating system's and application's message loops.
Mouse ───┐ ┌─── Keystrokes � � ┌─┴───┴─┐ ├───────┤ System ├───────┤ Event (time ordered) ├───────┤ Queue └───┬───┘ │ � ┌────┴───┐ │ Input │ Scancode ┌�─────┤ Router │ Translation │ └────────┘ � ┌───────┴──────┐ │ Message │ Accelerator │ Preprocessor │ Key Translation └───────┬──────┘ ┌─ ── ─ ── ── ── ── ── ── ── ─┐ ┌───┴───┐ │ ├───────┤ Appl Priority │ ├───────┤ MssgQ Ordered │ ├───────┤ │ └───┬───┘ │ └────────┐ │ │ ┌────── ┴─WinGetMsgQ │ � WinDispatchMsg │ │ │ │ App's │ � │ Message │ Window Procedure │ Loop │ │ │ │ � │ └�───────────return; └─ ── ── ── ── ── ── ── ── ── ─┘
Input Message Processing Loop Only one message loop is needed for a message queue, even if the queue contains messages for more than one window. Each queue message is a QMSG structure that contains the handle of the window to which the message belongs. WinDispatchMsg always dispatches the message to the proper window. WinGetMsg retrieves messages from the queue in first-in, first-out (FIFO) order, so the messages are dispatched to windows in the same order they are received.
If there are no messages in the queue, the operating system temporarily stops processing the WinGetMsg function until a message arrives. This means that processor time that, otherwise, would be spent waiting for a message can be given to the applications (or threads) that do have messages in their queues.
The message loop continues to retrieve and dispatch messages until WinGetMsg retrieves a WM_QUIT message. This message causes the function to return FALSE, terminating the loop. In most cases, terminating the message loop is the first step in terminating the application. An application can terminate its own loop by posting the WM_QUIT message in its own queue.
An application can modify its message loop in a variety of ways. For example, it can retrieve messages from the queue without dispatching them to a window. This is useful for applications that post messages without specifying a window. (These messages apply to the application rather than a specific window; they have NULL window handles.) Also, an application can direct the WinGetMsg function to search for specific messages, leaving other messages in the queue. This is useful for applications that temporarily need to bypass the usual FIFO order of the message queue.
Window Procedures
A :hp1.window procedure :ehp1. is a function that receives and processes all input and requests for action sent to the windows. Every window class has a window procedure; every window created using that class uses that window procedure to respond to messages.
The system sends a message to the window procedure by passing the message data as arguments. The window procedure takes the appropriate action for the given message. Most window procedures check the message identifier, then use the information specified by the message parameters to carry out the request. When it has completed processing the message, the window procedure returns a message result. Each message has a particular set of possible return values. The window procedure must return the appropriate value for the processing it performed.
A window procedure cannot ignore a message. If it does not process a message, it must pass the message back to the operating system for default processing. The window procedure does this by calling the WinDefWindowProc function to carry out a default action and return the message result. Then, the window procedure must return this value as its own message result.
A window procedure commonly processes messages for several windows. It uses the window handle specified in the message to identify the appropriate window. Most window procedures process just a few types of messages and pass the others on to the operating system by calling WinDefWindowProc.
Posting and Sending Messages
Any application can post and send messages. Like the operating system, an application :hp1.posts :ehp1. a message by copying it to a message queue. It :hp1.sends :ehp1. a message by passing the message data as arguments to a window procedure. To post and send messages, an application uses the WinPostMsg and WinSendMsg functions.
An application posts a message to notify a specific window to perform a task. The WinPostMsg function creates a QMSG structure for the message and copies the message to the message queue corresponding to the given window. The application's message loop eventually retrieves the message and dispatches it to the appropriate window procedure. For example, one message commonly posted is WM_QUIT. This message terminates the application by terminating the message loop.
An application sends a message to cause a specific window procedure to carry out a task immediately. The WinSendMsg function passes the message to the window procedure corresponding to the given window. The function waits until the window procedure completes processing and then returns the message result. Parent and child windows often communicate by sending messages to each other. For example, a parent window that has an entry-field control as its child window can set the text of the control by sending a message to the child window. The control can notify the parent window of changes to the text (carried out by the user) by sending messages back to the parent window.
Occasionally, an application might need to send or post a message to all windows in the system. For example, if the application changes a system value, it must notify all windows about the change by sending a WM_SYSVALUECHANGED message. An application can send or post messages to any number of windows by using the WinBroadcastMsg function. The options in WinBroadcastMsg determine whether the message is sent or posted and specify the windows that will receive the message.
Any thread in the application can post a message to a message queue, even if the thread has no message queue of its own. However, only a thread that has a message queue can send a message. Sending a message between threads is relatively uncommon. For one reason, sending a message is costly in terms of system performance. If an application posts a message between threads, it is likely to be a semaphore message, which permits window procedures to manage a shared resource jointly.
An application can post a message without specifying a window. If the application supplies a NULL window handle when it calls the WinPostMsg function, the function posts the message to the queue associated with the current thread. The application must process the message in the message loop. This is one way to create a message that applies to the entire application instead of to a specific window.
A window procedure can determine whether it is processing a message sent by another thread by using the WinInSendMsg function. This is useful when message processing depends on the origin of the message.
A common programming error is to assume that the WinPostMsg function always succeeds. It fails when the message queue is full. An application should check the return value of the WinPostMsg function to see whether the message was posted. In general, if an application intends to post many messages to the queue, it should set the message queue to an appropriate size when it creates the queue. The default message-queue size is 10 messages.
Message Types
This section describes the three types of OS/2 messages:
- System-defined
- Application-defined
- Semaphore
System-Defined Messages
There are many :hp1.system-defined :ehp1. messages that are used to control the operations of applications and to provide input and other information for applications to process. The system sends or posts a system-defined message when it communicates with an application. An application also can send or post system-defined messages. Usually, applications use these messages to control the operation of control windows created by using preregistered window classes.
Each system message has a unique message identifier and a corresponding symbolic constant. The symbolic constant, defined in the system header files, states the purpose of the message. For example, the WM_PAINT constant represents the paint message, which requests that a window paint its contents.
The symbolic constants also specify the :hp1.message category:ehp1.. System-defined messages can belong to several categories; the prefix identifies the type of window that can interpret and process the messages. The following table lists the prefixes and their related message categories:
Prefix | Message category |
---|---|
BKM_ | Notebook control |
BM_ | Button control |
:hp4.CBM_:ehp4. | Combination-box control |
:hp4.CM_:ehp4. | Container control |
:hp4.EM_:ehp4. | Entry-field control |
:hp4.LM_:ehp4. | List-box control |
:hp4.MLM_:ehp4. | Multiple-line entry field control |
:hp4.MM_:ehp4. | Menu control |
:hp4.SBM_:ehp4. | Scroll-bar control |
:hp4.SLM_:ehp4. | Slider control |
:hp4.SM_:ehp4. | Static control |
:hp4.TBM_:ehp4. | Title-bar control |
:hp4.VM_:ehp4. | Value set control |
:hp4.WM_:ehp4. | General window |
General window messages cover a wide range of information and requests, including:
- Mouse and keyboard-input
- Menu- and dialog-input
- Window creation and management
- Dynamic data exchange (DDE)
Application-Defined Messages
An application can create messages to use in its own windows. If an application does create messages, the window procedure that receives the messages must interpret them and provide the appropriate processing.
The operating system reserves the message-identifier values in the range :hp1. 0x0000 :ehp1. through :hp1.0x0FFF :ehp1. (the value of WM_USER - 1) for system-defined messages. Applications cannot use these values for their private messages.
In addition, the operating system uses certain message values higher than WM_ USER. Applications should not use these message values. A partial listing of these messages is in the following figure:
From PMSTDDLG.H: #define FDM_FILTER WM_USER+40 #define FDM_VALIDATE WM_USER+41 #define FDM_ERROR WM_USER+42 #define FNTM_FACENAMECHANGED WM_USER+50 #define FNTM_POINTSIZECHANGED WM_USER+51 #define FNTM_STYLECHANGED WM_USER+52 #define FNTM_COLORCHANGED WM_USER+53 #define FNTM_UPDATEPREVIEW WM_USER+54 #define FNTM_FILTERLIST WM_USER+55
You should scan your header files to see if other messages have been defined with values higher than WM_USER.
Aside from the message values used by the operating system, values in the range 0x1000 : (the value of WM_USER) through :hp1.0xBFFF :ehp1. are available for message identifiers, defined by an application, for use in that application.
- Warning
- It is very important that applications do not broadcast messages in the 0x1000 through :hp1.0xBFFF :ehp1. range due to the risk of misinterpretation by other applications.
Values in the range :hp1.0xC000 :ehp1. through :hp1.0xFFFF :ehp1. are reserved for message identifiers that an application defines and registers with the system atom table; these can be used in any application. Values above :hp1.0xFFFF :ehp1. (0x00010000 through 0xFFFFFFFF ) are reserved for future use; applications must not use messages in this range.
Semaphore Messages
A :hp1.semaphore message :ehp1. provides a way of signaling, through the message queue, the end of an event. An application uses a semaphore message the same way it uses system semaphore functions—to coordinate events by passing signals. A semaphore message often is used in conjunction with system semaphores.
There are four semaphore messages:
- WM_SEM1
- WM_SEM2
- WM_SEM3
- WM_SEM4.
An application posts one of these messages to signal the end of a given event. The window that is waiting for the given event receives the semaphore message when the message loop retrieves and dispatches the message.
Each semaphore message includes a bit flag that an application can use to uniquely identify the 32 possible semaphores for each semaphore message. The application passes the bit flag (with the appropriate bit set) as a message parameter with the message. The window procedure that receives the message then uses the bit flag to identify the semaphore.
To save space, the system does not store semaphore messages in the message queue. Instead, it sets a record in the queue, indicating that the semaphore message has been received, and then combines the bit flag for the message with the bit flags from previous messages. When the window procedure eventually receives the message, the bit flag specifies each semaphore message posted since the last message was retrieved.