We help IT Professionals succeed at work.

Interpret ASCII Char from Word variable in OnKeyDown event

enigmasolutions
on
I have a grid and I am trapping the OnKeyDown event.

I am trying to detect when the user either hits the Insert key or the letter A or the backslash key.

So in my OnKeyDown evnet I have the code

if Key=VK_Insert then
  Showmessage('Insert key was hit - this works!!!');
if Char(Key)='A' then
  Showmessage('A key was hit - this works!!!');
if Char(Key)='\' then
  Showmessage('Backslash key was hit');

Why does the test for the backslash key fail?

I note that when I hit the backslash then key=220 but Ord('\')=92.  Strange!

I prefer to use OnKeyDown because it is the first event and I can set the Key:=#0 to kill the keystroke and I can test virtual keys.  I can't do this with OnKeyPress.
Comment
Watch Question

Freelance Project Manager
CERTIFIED EXPERT
Top Expert 2010
Commented:
'\' is not a KEY, it's a character. In my french keyboard, '\' is obtained by pressing simultaneously Ctrl + Alt + '8'
The virtual key code you get is specific to a certain keyboard layout (US keyboard) :
220 = 0xDC = VK_OEM_5. OEM is the keyword here : it might not work the same for another manufacturer or model or country. Probably not with french keyboards.
besides, relying on onKeyDown to detect '\' will not see the more subtil ways to enter a '\' characters, like by typing the ascii code (92) on the keypad while holding Alt pressed (old trick from DOS time that still work)

The only relyable way to detect '\' character is OnKeyPress.
I'm not shocked by the fact that you'll have to use BOTH onKeyPress and onKeyDown to manage all your key-stroke scenarios. Looks pretty much usual to me.

You can kill the key from onKeyPress the same way as in onKeyDown, just that the Key parameter is a Char so you have to set Key:=#0 in onKeyPress , and Key := 0 (Word) in onKeyDown
Also, you should say
  if Key=ord('A')
instead of
  if Char(Key)='A'

because otherwise it would ignore the top half of the word if it's not an ASCII character code (or maybe throw an exception if you have range-checking on, I'm not sure about that).

But that isn't your problem here.

Author

Commented:
epasquier,

Good answer, I get the point.

Actually I started hunting through StdCtrls.pas and Controls.pas to see how Delphi (or Windows) converts keys into characters.  It got ugly fast!

The bottom line is... too hard.  I will use BOTH events for my purposes.

epasquier, you get the points.

But, for the record, I bet there would be a Windows API call that could convert a key into a char.  
I will leave this question open for a day or two to see if anyone works it out.

Author

Commented:
Actually,

i wanted to trap the backslash and convert it to an Insert key.

Thats why I wanted to use OnKeyDown

Author

Commented:
It worked but I couldn't trick my application into thinking the Insert key was pressed.

Author

Commented:
epasquier,  

Thanks for your input.

Although I ended up using my own solution.
Emmanuel PASQUIERFreelance Project Manager
CERTIFIED EXPERT
Top Expert 2010

Commented:
you can emulate Key stroke at your application form level :
procedure PostKeyExHWND(hWindow: HWnd; key: Word; const shift: TShiftState=[]; specialkey: Boolean=False);
{************************************************************
 * Procedure PostKeyEx
 *
 * Parameters:
 *  hWindow: target window to be send the keystroke
 *  key    : virtual keycode of the key to send. For printable
 *           keys this is simply the ANSI code (Ord(character)).
 *  shift  : state of the modifier keys. This is a set, so you
 *           can set several of these keys (shift, control, alt,
 *           mouse buttons) in tandem. The TShiftState type is
 *           declared in the Classes Unit.
 *  specialkey: normally this should be False. Set it to True to
 *           specify a key on the numeric keypad, for example.
 *           If this parameter is true, bit 24 of the lparam for
 *           the posted WM_KEY* messages will be set.
 * Description:
 *  This procedure sets up Windows key state array to correctly
 *  reflect the requested pattern of modifier keys and then posts
 *  a WM_KEYDOWN/WM_KEYUP message pair to the target window. Then
 *  Application.ProcessMessages is called to process the messages
 *  before the keyboard state is restored.
 * Error Conditions:
 *  May fail due to lack of memory for the two key state buffers.
 *  Will raise an exception in this case.
 * NOTE:
 *  Setting the keyboard state will not work across applications
 *  running in different memory spaces on Win32 unless AttachThreadInput
 *  is used to connect to the target thread first.
 *Created: 02/21/96 16:39:00 by P. Below
 ************************************************************}
type
  TBuffers = array [0..1] of TKeyboardState;
var
  pKeyBuffers: ^TBuffers;
  lParam: LongInt;

  procedure check(b:Boolean);
  begin
   if Not b
    then ShowMessage(IntToStr(GetLastError));
  end;
begin
  (* check if the target window exists *)
  if IsWindow(hWindow) then
  begin
    (* set local variables to default values *)
    pKeyBuffers := nil;
    lParam := MakeLong(0, MapVirtualKey(key, 0));

    (* modify lparam if special key requested *)
    if specialkey then
      lParam := lParam or $1000000;

    (* allocate space for the key state buffers *)
    New(pKeyBuffers);
    try
      (* Fill buffer 1 with current state so we can later restore it.
         Null out buffer 0 to get a "no key pressed" state. *)
      GetKeyboardState(pKeyBuffers^[1]);
      FillChar(pKeyBuffers^[0], SizeOf(TKeyboardState), 0);

      (* set the requested modifier keys to "down" state in the buffer*)
      if ssShift in shift then
        pKeyBuffers^[0][VK_SHIFT] := $80;
      if ssAlt in shift then
      begin
        (* Alt needs special treatment since a bit in lparam needs also be set *)
        pKeyBuffers^[0][VK_MENU] := $80;
        lParam := lParam or $20000000;
      end;
      if ssCtrl in shift then
        pKeyBuffers^[0][VK_CONTROL] := $80;
      if ssLeft in shift then
        pKeyBuffers^[0][VK_LBUTTON] := $80;
      if ssRight in shift then
        pKeyBuffers^[0][VK_RBUTTON] := $80;
      if ssMiddle in shift then
        pKeyBuffers^[0][VK_MBUTTON] := $80;

      (* make out new key state array the active key state map *)
      SetKeyboardState(pKeyBuffers^[0]);
      (* post the key messages *)
      if ssAlt in Shift then
      begin
        check(PostMessage(hWindow, WM_SYSKEYDOWN, key, lParam));
        check(PostMessage(hWindow, WM_SYSKEYUP, key, Cardinal(lParam) or $C0000000));
      end
      else
      begin
        check(PostMessage(hWindow, WM_KEYDOWN, key, lParam));
        check(PostMessage(hWindow, WM_KEYUP, key, Cardinal(lParam) or $C0000000));
      end;
      (* process the messages *)
      Application.ProcessMessages;
      Sleep(10);

      (* restore the old key state map *)
      SetKeyboardState(pKeyBuffers^[1]);
    finally
      (* free the memory for the key state buffers *)
      if pKeyBuffers <> nil then
        Dispose(pKeyBuffers);
    end; { If }
  end;
end; { PostKeyEx }

Open in new window

Author

Commented:
Awesome,

That completes the topic all round.

Thank you.
Emmanuel PASQUIERFreelance Project Manager
CERTIFIED EXPERT
Top Expert 2010

Commented:
If those answers satisfied you much, you can consider raising the point value ;o)