【问题标题】:How to detect CTRL+A in other applications using Delphi如何使用 Delphi 在其他应用程序中检测 CTRL+A
【发布时间】:2021-04-11 10:37:12
【问题描述】:

每当用户按下 Ctrl+A 击键时,我都需要检测它们。这是我到目前为止所做的

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  if (GetKeyState(65)>0) then
  begin
    caption:='CTRL+A Pressed!';
  end else
  begin
    caption:='Not pressed';
  end;
end;

如何知道CTRL键是否被按下?

【问题讨论】:

  • 如果您的窗口没有输入焦点,那么您将永远看不到任何调用GetKeyState 的东西。至少这就是我从你所说的“在其他应用程序中”得到的。
  • 这是XY Problem。您的真正问题的解决方案是WinEvents
  • @DavidHeffernan 那么像 Camtasia 这样的许多应用程序如何检测击键呢?
  • @DavidHeffernan:只是出于好奇。 “如果您的窗口没有输入焦点,那么您将永远看不到任何调用 GetKeyState 的内容”您是否真的尝试过? (我问是因为过去几年我有几次,而且一直有效。不过,这可能是无证行为。)
  • @AndreasRejbrand 我刚刚测试了它。在 XP 上,GetKeyState() 确实看到了关键状态更改,而我的测试应用程序没有输入焦点。 直接矛盾 what Raymond Chen said 在这个问题上:“如果用户切换到另一个程序,那么 GetKeyState 函数将看不到用户在另一个程序中键入的输入

标签: delphi winapi delphi-10.3-rio


【解决方案1】:

它对我有用。

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  if (GetKeyState(VK_CONTROL)<0) and (GetKeyState(65)<0) then
  begin
    caption:='Pressed!';
  end else
  begin
    caption:='';
  end;
end;

【讨论】:

  • 定时器的Interval是什么?如果用户在两次调用这段代码之间快速按下 Ctrl+A 会怎样?如果按下 Ctrl+A 的时间足够长以连续多次触发某个操作,会发生什么情况?如何区分这与几个实际不同的按键?
  • @Andrzej "不知道为什么它被否决" - 可能是因为这种方法实际上并没有解决您提出的问题。此方法仅适用于您自己的应用程序,您无法通过这种方式检测其他应用程序中的击键。并且使用计时器来轮询关键状态有时会错过事件。您确实需要通过SetWindowsHookEx()RegisterRawInputDevices() 来代替键盘挂钩。或者,就像@IInspectable 所说,使用 WinEvents 来处理击键导致的状态变化(即,例如EVENT_OBJECT_SELECTION... 事件),而不是专注于击键本身。
  • @RemyLebeau:“您无法通过这种方式检测其他应用程序中的击键”您确定吗?
  • @Andrzej 不是GetKeyState(),你不能。它仅轮询 调用线程 的键盘状态,并且该状态仅在调用线程具有输入焦点并执行消息检索时更新。要检测其他应用程序的密钥状态,您必须改用GetAsyncKeyState()。但无论如何,如前所述,轮询关键状态并不是这种情况的最佳解决方案。
  • @RemyLebeau:我唯一的反对意见是我尝试过它并且它确实有效。当我在 Firefox 中按 Ctrl+A 时,我的带有上述 OP 代码的 Delphi 测试应用程序在按键实际按下时确实显示“已按下”。我还记得过去看到它起作用。但我当然同意你提出的所有其他观点。
【解决方案2】:

每当用户按下 Ctrl+A 按键时,我都需要检测它们。

如果你真的是这个意思,那么考虑RegisterHotkey()

  • 优势:始终通知您。无需轮询,不会浪费性能。
  • 缺点:这也意味着您“窃取”每个其他窗口的输入 - 由您来为焦点窗口合成此组合键,以便恢复现实。此函数不用于拦截。

示例:您的程序/窗口没有聚焦,甚至可能被最小化。在记事本中,您按 CTRL+A 选择所有文本。您的程序会收到通知,而记事本从未收到过这样的输入。您可以使用SendInput() 为记事本生成CTRL+A,然后最终选择所有文本。

【讨论】:

  • 我想在你调用 SendInput 之后你的程序会再次收到通知。
  • ...意思是,记事本不会接收您生成的输入。
  • @SertacAkyuz 确实。您可以通过取消注册热键,使用 SendInput,然后重新注册热键来解决此问题,但这似乎……不优雅。
【解决方案3】:

您可以使用Raw Input/LowLevelKeyboard hook 来获取按键按下/按下事件而不是轮询。

对于原始输入,您可以参考this answer

  • 使用RegisterRawInputDevices 注册您的窗口;
  • 那么当有任何原始输入时,您的窗口将收到WM_INPUT 消息;
  • 识别密钥;
  • 记录ctrl的状态;
  • 按下“A”键时,检查 ctrl 状态。

C++ 示例:

LRESULT CALLBACK WindProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
    static BOOL ctrl_state = FALSE; //up
    if (Msg == WM_INPUT)
    {
        HRAWINPUT hRawInput = (HRAWINPUT)lParam;
        RAWINPUT input = { 0 };
        UINT size = sizeof(input);
        GetRawInputData(hRawInput, RID_INPUT, &input, &size, sizeof(RAWINPUTHEADER));
        switch (input.data.keyboard.VKey)
        {
        case VK_CONTROL:
            if(input.data.keyboard.Flags & RI_KEY_BREAK)
                ctrl_state = TRUE;
            else
                ctrl_state = FALSE;
            break;
        case 0x41:
            if (input.data.keyboard.Flags & RI_KEY_BREAK)
                OutputDebugString(L"Ctrl + A pressed");
            break;
        default:
            break;
        }

    }
    return DefWindowProc(hWnd, Msg, wParam, lParam);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-01
    • 1970-01-01
    • 2011-01-19
    • 1970-01-01
    • 2020-05-15
    相关资源
    最近更新 更多