2009. április 29., szerda

How to send key events to any control in a process


Problem/Question/Abstract:

I want to create a software keyboard in one form. When I press a key on the sofware keyboard, I would like to send the key's char to any form in my application.

Answer:

The following method will send key events to any control in your own process. It will not work reliably for windows in other processes.

{
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 Peter Below
}

procedure PostKeyEx(hWindow: HWnd; key: Word; const shift: TShiftState; specialkey:
  Boolean);
type
  TBuffers = array[0..1] of TKeyboardState;
var
  pKeyBuffers: ^TBuffers;
  lparam: LongInt;
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
        PostMessage(hWindow, WM_SYSKEYDOWN, key, lparam);
        PostMessage(hWindow, WM_SYSKEYUP, key, lparam or $C0000000);
      end
      else
      begin
        PostMessage(hWindow, WM_KEYDOWN, key, lparam);
        PostMessage(hWindow, WM_KEYUP, key, lparam or $C0000000);
      end;
      {process the messages}
      Application.ProcessMessages;
      {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;
  end;
end;

procedure TForm1.SpeedButton2Click(Sender: TObject);
var
  W: HWnd;
begin
  W := Memo1.Handle;
  PostKeyEx(W, VK_END, [ssCtrl, ssShift], False); {select all}
  PostKeyEx(W, Ord('C'), [ssCtrl], False); {copy to clipboard}
  PostKeyEx(W, Ord('C'), [ssShift], False); {replace with C}
  PostKeyEx(W, VK_RETURN, [], False); {new line}
  PostKeyEx(W, VK_END, [], False); {go to end}
  PostKeyEx(W, Ord('V'), [ssCtrl], False); {paste from keyboard}
end;

Nincsenek megjegyzések:

Megjegyzés küldése