首页 > 解决方案 > 如何从 WM_KeyUp 或任何地方提取正确大小写的 CharCode

问题描述

我正在尝试以区分大小写的方式处理 WM_KeyUp 消息以识别按下了哪个字母数字键(如果有)。在 TApplicationEvents 我们有 OnMessage 处理程序,在我的项目中分配给

procedure TForm1.DoOnAppMessage(var Msg: tagMSG; var Handled: Boolean);
var
  CH : Char;
  [...]
begin
  Inc(MsgCount);

  case Msg.Message of
    WM_KeyUp : begin
      CH := Chr(Msg.WParam);
      // do something with CH
    end;

  end; { case ]

就目前而言,这很好,当然除了我总是得到这封信的大写版本。

所以我显然需要解码 Msg 的 LParam。谷歌搜索,我遇到了许多解码 LParam 的例子,但没有一个我能找到处理我认为在正确的情况下呈现字母数字键的“简单”任务。我的问题是,请有人告诉我如何做到这一点。

请注意:我知道我可以通过处理 WM_Char 消息获得正确大写的字母,但我无法在我试图处理的实际情况中使用它(实际上是在键盘钩子内)。

标签: delphiwinapimessaging

解决方案


我找到了一个基于早期 SO q&a的解决方案,其中涉及设置低级键盘挂钩。最初,当我尝试这个时,我得到了与我的 q 中发布的代码相同的结果,即所有返回的字符都是小写的。

有趣的是,没有大写和正确大写之间的区别 - 下面的代码所做的 - 是包含对 GetKeyState 的调用的 4 行之一,即KeyState[VK_SHIFT] := GetKeyState(VK_SHIFT). 没有它,返回的字符都是小写的(不像我的 q 中的代码,它们都是大写的);

type
  TOutProc = procedure(AString : String) of object;
var
  OutProc : TOutProc; // requires assignment to a suitable proc in the host application

type
  PKbdLlHookStruct = ^TKbdLlHookStruct;

  TKbdLlHookStruct = packed record
    vkCode: DWORD;
    scanCode: DWORD;
    flags: DWORD;
    time: DWORD;
    dwExtraInfo: DWORD;
  end;

const
  WH_KEYBOARD_LL   =   13;

var
  FKeyboardLayoutHandle: HKL;
  hhkLowLevelKybd:   HHOOK;

function LowLevelKeyBoardProc(nCode:   Integer;   awParam:   WPARAM;
  alParam:   LPARAM):   LRESULT;   stdcall;
const
  LLKHF_UP             =  $0080;
var
  act:   PKbdllHookStruct;
  CH : Char;
  S : String;
  KeyState : TKeyboardState;
  NewChar: array[0..1] of Char;
begin

  //  adapted from https://stackoverflow.com/q/1590983
  if (nCode = HC_ACTION) then begin
    case awParam  of
      WM_SYSKEYDOWN,
      WM_KEYUP,
      WM_SYSKEYUP: begin
        act := PKbdLlHookStruct(alParam);
        if awParam=WM_KEYUP then begin
          FillChar(NewChar,2,#0);
          GetKeyboardState(KeyState);
          //  Next four lines from https://stackoverflow.com/a/10480563
          KeyState[VK_CAPITAL] := GetKeyState(VK_CAPITAL);
          KeyState[VK_SHIFT] := GetKeyState(VK_SHIFT);
          KeyState[VK_CONTROL] := GetKeyState(VK_CONTROL);
          KeyState[VK_MENU] := GetKeyState(VK_MENU);

          if ToAsciiEx(act^.vkCode, act^.scanCode, KeyState, NewChar, 0, FKeyboardLayoutHandle) = 1 then
          CH := NewChar[0];
          if (CH in [#8, #10, #13]) Or (CH >= ' ') then begin
            S := CH;
            OutProc(S);
            GetClassName(GetForegroundWindow, @ClassBuffer, 100);
          end;
        end;
      end; { case }
    end;  { case }
  end;

  Result := CallNextHookEx(hhkLowLevelKybd, nCode, awParam, alParam);
end;

推荐阅读