Windows 全局键盘挂钩 - Delphi

Posted

技术标签:

【中文标题】Windows 全局键盘挂钩 - Delphi【英文标题】:Windows Global Keyboard Hook - Delphi 【发布时间】:2010-07-01 09:11:36 【问题描述】:

我使用互联网上的源代码创建了一个 GLOBAL 键盘挂钩 DLL。最好的部分是它工作得很好,除了浏览器。

它会拾取浏览器中的每个键,但似乎当浏览器获得焦点时,它会丢失第一个被按下的键。在 IE 和 Firefox 中测试过,似乎两者都一样。

例如,如果我打开 IE 并开始输入 www。 ,我只回来了ww。如果浏览器窗口保持焦点,则不会丢失更多键。一旦浏览器失去焦点并重新获得焦点,第一个键就会再次丢失。

可能是因为只使用 WH_KEYDOWN 而不是 WH_KEYPRESS / WH_KEYUP ?有人能解释一下吗?

谢谢

PS:钩子函数本身如下:向 DLL 发送一个备忘录框和应用程序句柄,DLL 将发送消息和用户消息。

    function KeyHookFunc(Code, VirtualKey, KeyStroke: Integer): LRESULT; stdcall; 
var
  KeyState1: TKeyBoardState; 
  AryChar: array[0..1] of Char; 
  Count: Integer; 
begin 
  Result := 0; 
  if Code = HC_NOREMOVE then Exit;

  Result := CallNextHookEx(hKeyHook, Code, VirtualKey, KeyStroke); 
  I moved the CallNextHookEx up here but if you want to block 
   or change any keys then move it back down 
  if Code < 0 then 
    Exit; 
  if Code = HC_ACTION then 
  begin 
    if ((KeyStroke and (1 shl 30)) <> 0) then 
      if not IsWindow(hMemo) then
      begin 
       I moved the OpenFileMapping up here so it would not be opened 
        unless the app the DLL is attatched to gets some Key messages 
        hMemFile  := OpenFileMapping(FILE_MAP_WRITE, False, 'NetParentMAP');//Global7v9k
        PHookRec1 := MapViewOfFile(hMemFile, FILE_MAP_WRITE, 0, 0, 0); 
        if PHookRec1 <> nil then 
        begin 
          hMemo := PHookRec1.MemoHnd; 
          hApp  := PHookRec1.AppHnd; 
        end; 
      end;
    if ((KeyStroke AND (1 shl 31))  = 0) then //if ((KeyStroke and (1 shl 30)) <> 0) then
    begin 
      GetKeyboardState(KeyState1); 
      Count := ToAscii(VirtualKey, KeyStroke, KeyState1, AryChar, 0); 
      if Count = 1 then
      begin
        SendMessage(hMemo, WM_CHAR, Ord(AryChar[0]), 0); 
        I included 2 ways to get the Charaters, a Memo Hnadle and 
         a WM_USER+1678 message to the program 
        PostMessage(hApp, WM_USER + 1678, Ord(AryChar[0]), 0);
      end;
    end; 
  end; 
end; 

【问题讨论】:

您的代码示例对尝试回答您的代码的人有很大帮助。 是的,我知道,问题是我将不得不提出整个项目,因为我不知道问题可能出在哪里。我可以说的是我使用以下设置挂钩: hKeyHook := SetWindowsHookEx(WH_KEYBOARD, KeyHookFunc, hInstance, 0);但我见过一个似乎使用 WH_KEYBOARD_LL 的 C# 项目,这会有所作为吗? 至少你可以告诉我们你在互联网上找到的来源。也许这段代码不正确...... 【参考方案1】:

您没有足够早地分配 hMemohApp 值。您正在等待“先前状态”标志为 1 的通知,这表明一个键已被按住至少 1 次重复计数,或者正在被释放,以先发生者为准。因此,hMemohApp 在你的钩子检测到它的第一个按键通知时还不可用。这就是为什么你会错过角色。试试这个:

function KeyHookFunc(Code, VirtualKey, KeyStroke: Integer): LRESULT; stdcall;
var
  KeyState1: TKeyBoardState;
  AryChar: array[0..1] of Char;
  Count: Integer;
begin
  Result := CallNextHookEx(hKeyHook, Code, VirtualKey, KeyStroke);
  if Code <> HC_ACTION then Exit;

   a key notification had occured, prepare the HWNDs
  before checking the actual key state 
  if (hMemo = 0) or (hApp = 0) then
  begin
    if hMemFile = 0 then
    begin
      hMemFile := OpenFileMapping(FILE_MAP_WRITE, False, 'NetParentMAP');
      if hMemFile = 0 then Exit;
    end;
    if PHookRec1 = nil then
    begin
      PHookRec1 := MapViewOfFile(hMemFile, FILE_MAP_WRITE, 0, 0, 0);
      if PHookRec1 = nil then Exit;
    end;
    hMemo := PHookRec1.MemoHnd;
    hApp  := PHookRec1.AppHnd;
    if (hMemo = 0) and (hApp = 0) then Exit;
  end;

  if ((KeyStroke and (1 shl 31)) = 0) then // a key is down
  begin
    GetKeyboardState(KeyState1);
    Count := ToAscii(VirtualKey, KeyStroke, KeyState1, AryChar, 0);
    if Count = 1 then
    begin
      if hMemo <> 0 then SendMessage(hMemo, WM_CHAR, Ord(AryChar[0]), 0);
      if hApp <> 0 then PostMessage(hApp, WM_USER + 1678, Ord(AryChar[0]), 0);
    end;
  end;
end;

【讨论】:

老兄,你是明星。可能是愚蠢的错误,但仍然如此。谢谢

以上是关于Windows 全局键盘挂钩 - Delphi的主要内容,如果未能解决你的问题,请参考以下文章

我无法在提升的 Windows 应用程序中全局挂钩键盘

是否可以从 .NET 的全局键盘挂钩中确定当前用户

全局键盘挂钩

MFC 全局键盘挂钩

用于 C# 和 WPF 的高级全局键盘钩子,用于读取键盘楔形卡扫描仪

想用VC++做个全局键盘改键,键盘钩子代码,详细怎么做。跪求代码。