Q's and A's - April 1995

by Amir Kolsky

How can I send keystrokes to a window to simulate the user pressing a key?
 * Question:

Post a WM_CHAR message to the window. Sound simple? You bet! And just as easy to get wrong. Knowing how the target window processes WM_CHAR messages is crucial. First of all, consider the WM_CHAR message itself.
 * Answer:

param1 USHORT fsflags     Keyboard control codes (KC_*) UCHAR  ucrepeat    Repeat count UCHAR  ucscancode  Hardware scan code param2 USHORT usch        Character code         (ASCII) USHORT usvk        Virtual key codes      (VK_*)

The fsflags field describes the keystroke. The most important flags are KC_CHAR, KC_VIRTUALKEY, KC_KEYUP, KC_SHIFT, KC_ALT, and KC_CTRL.

A keystroke normally comprises a down stroke and an up stroke, with KC_KEYUP signaling the up stroke. So, whenever a keystroke is to be simulated, two WM_CHAR messages need to be posted: one without KC_KEYUP and one with it (in that order). Do you really need two messages? Most applications deal only with the down transition. If you know that the application disregards the up stroke, you can safely post just the down stroke.

If you want to send a character, make sure KC_CHAR</tt> is set. If you want to send a virtual key (a key without a visual representation), you need to set KC_VIRTUALKEY</tt>. Why two flags? A keystroke is either a character or a virtual key, right? Wrong. Four keys have both a character and a virtual key associated with them - the Spacebar, Tab, and the two Enter keys (the 0x20, 0x09, and 0x0d characters, respectively). If you don't know which of the two representations the application uses, make sure you set all the relevant information in both parameters. Note that the usch</tt> field is a USHORT</tt>. This is important when dealing with DBCS characters.

However, our woes are not over yet. Many applications use accelerators. When WinGetMsg</tt> retrieves a message from the system input queue, it runs it through the accelerator table associated with the window. It does this by sending the window a WM_TRANSLATEACCEL</tt> message. The first parameter of WM_TRANSLATEACCEL</tt> is a pointer to the qmsg structure. If a translation takes place, WM_TRANSLATEACCEL</tt> returns TRUE</tt> and the qmsg</tt> structure contains the new message (WM_COMMAND, WM_SYSCOMMAND</tt>, or WM_HELP</tt>). In this case, WinGetMsg</tt> sends the new message to the window.

So, you need to call WM_TRANSLATEACCEL</tt> unless you are sure that no accelerator table is installed, and send the result to the window if a translation took place. On the other hand, if you know what the accelerator table is, and you have a handle to the accelerator table, you can call WinTranslateAccel</tt> directly to perform the translation. You should perform the translation on both the up and down strokes.

To summarize, posting keystrokes can be a quick and effective way to simulate user keyboard input. Another important use is to provide remote access to applications without designing a special protocol to send keystrokes from the remote client. If a few minor caveats are taken into consideration, you can save a lot of programming effort.

I want my secondary windows to appear indented under the primary window entry in the task list, just as the Workplace Shell does for different views of the same object. What's the trick?
 * Question

The only way you can do this is by subclassing the Window List and doing the shuffling yourself. The Window List is a container. What you see is not the container's tree view.
 * Answer

When another view of an object is opened in the shell or a process is started from one of the objects, the Window List is manipulated to provide the indented look. The indentation is managed by adding spaces to the start of some entries in the list. In these cases, an extraneous dummy entry is added to the one requested by the caller, to serve as a group title to the real entries that follow it. Whenever any item is added or deleted, the Window List is reworked and dummy entries are added or destroyed as necessary.

To summarize, no API is provided for an arbitrary process to group its windows beneath a group title. This is a Workplace Shell privilege.

Is it possible to have multi-line text in a STRINGTABLE?
 * Question

Yes. Use '\xA'</tt> as a newline character.
 * Answer

STRINGTABLE { IDS_SPLITSTRING, This string is split xA across two lines }
 * 1) define IDS_SPLITSTRING 212

Can I split the text in notebook tabs by putting a <tt>'\n'</tt> in the text?
 * Question

No. Currently the notebook tab can only display text in a single line. If you want to split the text, you will have to draw it yourself. If you do not send the notebook a <tt>BKM_SETTABTEXT</tt> or <tt>BKM_SETTABBITMAP</tt> message, it will send the owner a <tt>WM_DRAWITEM</tt> anytime a tab needs painting. You can then parse the text to be written by looking for the new line character, and draw the individual lines of text yourself using <tt>WinDrawText</tt> or the <tt>GpiCharStr</tt> variants. You need to set the tab size to be large enough to hold the text and you can use <tt>WinDrawText</tt> or <tt>GpiQueryTextBox</tt> to calculate the needed tab size. You can change the size of the tabs using <tt>BKM_SETDIMENSIONS</tt> to make them large enough to hold your text.
 * Answer