Normal PowerBuilder keyboard processing will trap a number of key combinations such that they cannot be accessed using normal PowerBuilder methods such as using Key events or trying to tap into the underlying WM_KEYDOWN message using the Other event.
Fortunately, Windows API provides raw keyboard input processing. It is not excessively difficult to do. The main idea is that a Window wishing to get raw keyboard input, registers with Windows that it wants raw input. Then Windows sends WM_INPUT messages when a key is pressed. I had an app where I needed to trap for the Tab and Alt keys as well as determine if the key pressed was extended from within a Multiline edit.
Here is the code I used:
You need some structures
type str_rawinputdevice from structure
unsignedinteger ususagepage
unsignedinteger ususage
unsignedlong dwflags
unsignedlong hwndtarget
end type
type str_rawinputheader from structure
unsignedlong dwtype
unsignedlong dwsize
unsignedlong hdevice
unsignedlong wparam
end type
type str_rawkeyboard from structure
unsignedinteger makecode
unsignedinteger flags
unsignedinteger reserved
unsignedinteger vkey
unsignedlong message
unsignedlong extrainformation
end type
type str_rawinput from structure
str_rawinputheader header
str_rawkeyboard keyboard
end type
and some external function declarations:
// Registers the devices that supply the raw input data so that the application may receive WM_INPUT messages.
FUNCTION Boolean WinAPI_RegisterRawInputDevices( &
READONLY str_rawinputdevice pRawInputDevices[], &
uLong uiNumDevices, &
ulong cbSize &
) &
LIBRARY "USER32.DLL" ALIAS FOR "RegisterRawInputDevices"
// Retrieves the raw input from the specified device.
FUNCTION uLong WinAPI_GetRawInputData( &
uLong hRawInput, &
uLong uiCommand, &
REF str_rawinput rawinput, &
REF uLong pcbSize, &
uLong cbSizeHeader &
) &
LIBRARY "USER32.DLL" ALIAS FOR "GetRawInputData"
// Calls the default raw input procedure to provide default processing for any raw input messages that an
// application does not process. This function ensures that every message is processed. DefRawInputProc is
// called with the same parameters received by the window procedure.
FUNCTION uLong WinAPI_DefRawInputProc( &
READONLY str_rawinput rawinput, &
Long nInput, &
uLong cbSizeHeader &
) &
LIBRARY "USER32.DLL" ALIAS FOR "DefRawInputProc"
In the Open event of your window, you need to register to receive raw keyboard input:
str_rawinputdevice pRawInputDevices[]
pRawInputDevices[1].usUsagePage = 1 // 1 for Keyboard input
pRawInputDevices[1].usUsage = 6 // 6 for Keyboard input
pRawInputDevices[1].dwflags = 0 // 0 for default flags
pRawInputDevices[1].hwndTarget = Handle(THIS) // handle of window to receive WM_INPUT messages
// register this window to recieve WM_INPUT messages with raw keyboard input,
WinAPI_RegisterRawInputDevices(pRawInputDevices[], UpperBound(pRawInputDevices[]), 2+2+4+4) // size is two 2-byte uInts and two 4-byte uLongs
You catch the input in your window's other event: Note the ib_raw_keyboard_input_active flag. I found that my window received WM_INPUT messages even when it did not have focus. To get around that, I made a Boolean flag that I set in the Window's Activate event and cleared in the Window's Deactivate event
CHOOSE CASE Message.Number
CASE 255 // WM_INPUT
IF ib_raw_keyboard_input_active = TRUE THEN
// Process the raw keyboard input.
RETURN EVENT ue_Input(Message.LongParm)
END IF
END CHOOSE
Lastly here is my event in which I process the WM_INPUT messages. This could be simply coded in the Other event, programmer's choice.
event type long ue_input(unsignedlong ah_rawinput);// Process the raw keyboard input. This allows us to bypass the limitations of normal PowerBuilder
// keyboard processing via the built-in Key event such as Tab keystrokes and Alt-key combinations being
// intercepted and thus not available as well as not being able to distinguish between keypad keys and
// non-keypad keys. For example, if numberlock is not down, KeyHome! is sent to the Key event regardless
// of which Home key was pressed.
//
// INPUTS: ah_RawInput - A handle to the RAWINPUT structure. This comes from the lParam in WM_INPUT
str_RawInput lRawInput
uInt li_key_flags
uLong lul_command
uLong lul_header_size
uLong lul_data_size
Long ll_result
Boolean lb_E0, lb_control, lb_alt, lb_shift
KeyCode l_keycode
Long ll_command_code = 0
// Bit masks for the raw input keyboard flags
CONSTANT uInt RI_KEY_MAKE = 0 // The key is down.
CONSTANT uInt RI_KEY_BREAK = 1 // The key is up.
CONSTANT uInt RI_KEY_E0 = 2 // The scan code has the E0 prefix.
CONSTANT uInt RI_KEY_E1 = 4 // The scan code has the E1 prefix.
lul_command = 268435459 /* RID_INPUT = 0x10000003 = 268435459 */
lul_header_size = 4 * 4 // str_RawInputHeader has four 4-byte uLongs
lul_data_size = lul_header_size + 4 * 2 + 2 * 4 // str_RawKeyboard has four 2-byte uInts and two 4-byte uLongs
ll_result = WinAPI_GetRawInputData(ah_RawInput, lul_command, REF lRawInput, REF lul_data_size, lul_header_size)
// check the least signifcant bit in the flags and process only when the key is not up i.e. handle KeyDown and not KeyUp
li_key_flags = lRawInput.KeyBoard.Flags
IF Mod(li_key_flags, 2) <> RI_KEY_BREAK THEN
// shift the flag bits 1 bit to the right and check the new least signifcant bit to see if the key has the E0 prefix.
// The Keypad Enter has E0 set, for the other keypad navigation keys, the E0 is clear
li_key_flags /= 2
IF Mod(li_key_flags, 2) = 1 THEN
lb_E0 = TRUE
ELSE
lb_E0 = FALSE
END IF
lb_control = KeyDown(KeyControl!)
lb_alt = KeyDown(KeyAlt!)
lb_shift = KeyDown(KeyShift!)
IF lRawInput.KeyBoard.vKey = 9 THEN
// do something with the Tab key press
ELSE
WinAPI_DefRawInputProc(lRawInput, 1, lul_header_size)
END IF
RETURN 0
end event