找出什么程序注册了全局热键?(Windows API)


114

据我所知,Windows没有提供API函数来告诉哪个应用程序已经注册了全局热键(通过RegisterHotkey)。我只能发现如果RegisterHotkey返回false,而不是谁“拥有”该热键,则该热键已注册。

在没有直接API的情况下,是否会有回旋路?Windows维护与每个已注册热键关联的句柄-有点令人发疯的是,应该无法获取此信息。

可能不起作用的示例:发送(模拟)已注册的热键,然后拦截Windows将发送给已注册热键的进程的热键消息。首先,我认为拦截消息不会显示目标窗口句柄。其次,即使有可能,也将是一件坏事,因为发送热键会触发各种程序的各种潜在有害活动。

并不是很关键,但是我经常看到对此类功能的要求,并且我自己是注册热键的应用程序的受害者,甚至没有在UI或文档中的任何位置公开它。

(请在Delphi中工作,并且只不过是WinAPI的学徒,请客气。)

Answers:


20

您的问题激起了我的兴趣,因此我做了一些挖掘工作,但不幸的是,我没有为您提供适当的答案,我想我会分享我所拥有的。

我发现了这个创建于1998年的键盘钩子示例(在Delphi中),但是可以通过一些调整在Delphi 2007中进行编译。

这是一个DLL,其调用SetWindowsHookEx通过回调函数传递,该回调函数随后可以截获按键:在这种情况下,它是为了乐趣而作修改,将左光标更改为右等。然后,一个简单的应用程序调用DLL并报告其结果基于TTimer事件。如果您有兴趣,我可以发布基于Delphi 2007的代码。

它有充分的文档记录和评论,您可以将其用作确定按键操作方向的基础。如果您可以获取发送按键的应用程序的句柄,则可以通过这种方式进行跟踪。使用该句柄,您将能够轻松获得所需的信息。

其他应用程序尝试通过快捷方式来确定热键,因为它们可以包含快捷键,这只是热键的另一个术语。但是,大多数应用程序都不倾向于设置此属性,因此它可能不会返回太多。如果您对该路由感兴趣,Delphi可以访问IShellLinkCOM界面,您可以使用它来加载快捷方式并获取其热键:

uses ShlObj, ComObj, ShellAPI, ActiveX, CommCtrl;

procedure GetShellLinkHotKey;
var
  LinkFile : WideString;
  SL: IShellLink;
  PF: IPersistFile;

  HotKey : Word;
  HotKeyMod: Byte;
  HotKeyText : string;
begin
  LinkFile := 'C:\Temp\Temp.lnk';

  OleCheck(CoCreateInstance(CLSID_ShellLink, nil, CLSCTX_INPROC_SERVER, IShellLink, SL));

  // The IShellLink implementer must also support the IPersistFile
  // interface. Get an interface pointer to it.
  PF := SL as IPersistFile;

  // Load file into IPersistFile object
  OleCheck(PF.Load(PWideChar(LinkFile), STGM_READ));

  // Resolve the link by calling the Resolve interface function.
  OleCheck(SL.Resolve(0, SLR_ANY_MATCH or SLR_NO_UI));

  // Get hotkey info
  OleCheck(SL.GetHotKey(HotKey));

  // Extract the HotKey and Modifier properties.
  HotKeyText := '';
  HotKeyMod := Hi(HotKey);

  if (HotKeyMod and HOTKEYF_ALT) = HOTKEYF_ALT then
    HotKeyText := 'ALT+';
  if (HotKeyMod and HOTKEYF_CONTROL) = HOTKEYF_CONTROL then
    HotKeyText := HotKeyText + 'CTRL+';
  if (HotKeyMod and HOTKEYF_SHIFT) = HOTKEYF_SHIFT then
    HotKeyText := HotKeyText + 'SHIFT+';
  if (HotKeyMod and HOTKEYF_EXT) = HOTKEYF_EXT then
    HotKeyText := HotKeyText + 'Extended+';

  HotKeyText := HotKeyText + Char(Lo(HotKey));

  if (HotKeyText = '') or (HotKeyText = #0) then
    HotKeyText := 'None';

  ShowMessage('Shortcut Key - ' + HotKeyText);
end;

如果您可以访问Safari联机丛书,那么Steve Teixeira和Xavier Pacheco撰写的《 Borland Delphi 6开发人员指南》中有一个很好的部分介绍了如何使用快捷方式/外壳链接。我上面的示例是该站点该站点的屠宰版本。

希望有帮助!


59

一种可能的方法是使用Visual Studio工具Spy ++

试试看:

  1. 运行工具(对我来说,位于C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools\spyxx_amd64.exe
  2. 在菜单栏中,选择间谍 -> 记录消息...(或按Ctrl+ M
  3. 其他Windows框架中检查系统中的所有Windows
  4. 切换到消息选项卡
  5. 单击全部清除按钮
  6. 选择WM_HOTKEY在列表框中,或检查键盘消息组(如果你有更多潜在的噪声OK)
  7. 点击确定按钮
  8. 按下有问题的热键(例如Win+ R
  9. WM_HOTKEY在“ 消息(所有Windows)”窗口中选择该行,右键单击,然后在上下文菜单中选择“ 属性...”
  10. 在“ 消息属性”对话框中,单击“ 窗口句柄”链接(这将是接收消息的窗口的句柄)
  11. 单击窗口属性上的同步按钮对话框。这将在主Spy ++窗口树视图中显示该窗口。
  12. 在“ 窗口属性”对话框上,选择“ 处理”选项卡
  13. 单击进程ID链接。这将显示你的过程(在我的Win+ R案例:EXPLORER

6
好答案!请注意,Spy ++的64位版本仅捕获针对64位应用程序的消息,因此,如果在按热键后WM_HOTKEY消息日志中看不到该消息,则可能需要运行32位版本的Spy ++ 。
David FerenczyRogožan17年

非常感谢!!!基本上我放弃使用一些快捷方式,直到找到了。
thewindev

3
在第8步中,它不需要输入热键,按任何热键后,消息窗口将保持空白。使用github.com/westoncampbell/SpyPlusPlus的 32位和64位版本(在Windows 10中)。
CoolMind

9

经过研究后,您似乎需要访问MS用于存储热键的内部结构。ReactOS具有一个洁净室实现,该实现GetHotKey通过迭代内部列表并提取与参数匹配的热键来实现该调用。

根据ReactOS的实现与MS的实现有多接近,您也许可以在内存中四处寻找结构,但这已经超出了我的脑海。

BOOL FASTCALL
GetHotKey (UINT fsModifiers,
           UINT vk,
           struct _ETHREAD **Thread,
           HWND *hWnd,
           int *id)
{
   PHOT_KEY_ITEM HotKeyItem;

   LIST_FOR_EACH(HotKeyItem, &gHotkeyList, HOT_KEY_ITEM, ListEntry)
   {
      if (HotKeyItem->fsModifiers == fsModifiers &&
            HotKeyItem->vk == vk)
      {
         if (Thread != NULL)
            *Thread = HotKeyItem->Thread;

         if (hWnd != NULL)
            *hWnd = HotKeyItem->hWnd;

         if (id != NULL)
            *id = HotKeyItem->id;

         return TRUE;
      }
   }

   return FALSE;
}

我猜想这个问题在sysinternals上有人问过,但是我想无论如何我都会链接到它,以使两者保持一致。该线程看上去很有趣,但是我怀疑在不访问MS内部组件的情况下,可能需要进行一些深入的研究才能找出答案。


3

在我脑海中,您可以尝试使用EnumWindows枚举所有窗口,然后在回调中将WM_GETHOTKEY发送到每个窗口。

编辑:显然我是错的。MSDN具有更多信息:

WM_HOTKEY与WM_GETHOTKEY和WM_SETHOTKEY热键无关。为通用热键发送WM_HOTKEY消息,而WM_SETHOTKEY和WM_GETHOTKEY消息与窗口激活热键有关。

注意:是一个声称具有您想要的功能的程序。您可以尝试反编译它。


3
程序的链接现在已完全断开。那是什么节目?我想想太多次找出哪个程序注册了我的热键,因为它们突然不起作用了或做了一些令人讨厌的新事情。
James Oltmans 2011年

(通常,Wayback Machine可以在网页消失的情况下提供帮助,但在这里它也仅反映了404 Not Found:http : //web.archive.org/web/*/tds.diamondcs.com.au/dse/ detection / hotkeys.php
亚伦·托马



0

我知道您可以在自己的过程中的任何窗口中截取消息流,这就是我们在VB6中用来调用子类的过程。(尽管我不记得该功能,也许是SetWindowLong?)我不确定是否可以在自己的进程之外的Windows上执行此操作。但是为了这篇文章的缘故,让我们假设您找到一种方法来做到这一点。然后,您可以简单地拦截所有顶级窗口的消息,监视WM_HOTKEY消息。您可能无法立即了解所有按键,但是当按下这些按键时,您可以轻松找出正在使用哪个应用程序。如果将结果持久保存到磁盘并在每次运行监视器应用程序时重新加载,则可以随着时间的推移提高应用程序的性能。


-1

这不能完全回答有关Windows API的部分问题,但可以回答有关全局热键和“拥有”它们的应用程序列表的部分问题。

http://hkcmdr.anymania.com/上的免费热键资源管理器显示了所有全局热键以及拥有它们的应用程序的列表。这只是帮助我弄清楚了为什么特定于应用程序的快捷键为何停止工作以及如何修复它(通过在注册了该应用程序的应用程序中重新配置已注册的全局热键)。


4
安装它,它就像病毒一样,至少是一个带有恶意软件的程序,就像一个恶作剧。打开所有窗户样的,激活语音narator等
Flion

3
@Flion:参见superuser.com/a/191090/3617上的讨论。基本上,一些热键实用程序通过探测每种可能的组合键来工作。在Windows 8+(和Windows 7)上,这种探测技术会触发每个键组合,而不仅仅是查询组合键。
布赖恩

@Flion FWIW,我在Win7上成功使用了它。但是,如果我正确理解Brian的话,这在以后的版本中可能会无法正常工作,而它的工作方式也可能取决于定义的自定义热键。
Gerhard
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.