Q's and A's - April 1995

From EDM2
Jump to: navigation, search

by Amir Kolsky

Question

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

Answer

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.

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 is set. If you want to send a virtual key (a key without a visual representation), you need to set KC_VIRTUALKEY. 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 field is a USHORT. This is important when dealing with DBCS characters.

However, our woes are not over yet. Many applications use accelerators. When WinGetMsg 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 message. The first parameter of WM_TRANSLATEACCEL is a pointer to the qmsg structure. If a translation takes place, WM_TRANSLATEACCEL returns TRUE and the qmsg structure contains the new message (WM_COMMAND, WM_SYSCOMMAND, or WM_HELP). In this case, WinGetMsg sends the new message to the window.

So, you need to call WM_TRANSLATEACCEL 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 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.

Question

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?

Answer

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.

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.

Question

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

Answer

Yes. Use '\xA' as a newline character.

#define IDS_SPLITSTRING  212

STRINGTABLE {
   IDS_SPLITSTRING,  This string is split xA across two lines  
}
Question

Can I split the text in notebook tabs by putting a '\n' in the text?

Answer

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 BKM_SETTABTEXT or BKM_SETTABBITMAP message, it will send the owner a WM_DRAWITEM 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 WinDrawText or the GpiCharStr variants. You need to set the tab size to be large enough to hold the text and you can use WinDrawText or GpiQueryTextBox to calculate the needed tab size. You can change the size of the tabs using BKM_SETDIMENSIONS to make them large enough to hold your text.