找出注册全球热键的流程? (Windows API)
-
06-07-2019 - |
题
据我所知,Windows没有提供API函数来告诉应用程序注册了一个全局热键(通过RegisterHotkey)。我只能发现,如果RegisterHotkey返回false,则注册热键,而不是谁“拥有”。热键。
在没有直接API的情况下,是否会有迂回的方式? Windows维护与每个注册热键关联的句柄 - 这有点令人抓狂,因此无法获取此信息。
可能不起作用的示例:发送(模拟)已注册的热键,然后拦截Windows将发送给注册它的进程的热键消息。首先,我不认为拦截消息会显示目标窗口句柄。其次,即使有可能,这也是一件坏事,因为发送热键会触发各种程序中各种可能不需要的活动。
这并不重要,但我已经看到频繁请求此类功能,并且我自己一直是注册热键的应用程序的受害者,甚至没有在UI或文档中的任何地方公开它。
(在Delphi工作,只不过是WinAPI的学徒,请善待。)
解决方案
你的问题引起了我的兴趣,所以我做了一些挖掘,但不幸的是,我没有给你一个正确的答案,我想我会分享我所拥有的。
我发现了这个创建键盘钩子的示例(在Delphi中) 写于1998年,但在Delphi 2007中可以通过几个调整进行编译。
这是一个调用 SetWindowsHookEx
的DLL,它通过一个回调函数,然后可以拦截键击:在这种情况下,它是为了好玩而修改它们,将左光标改为右边等等然后,一个简单的应用程序调用DLL并根据TTimer事件报告其结果。如果您有兴趣,我可以发布基于Delphi 2007的代码。
它有详细记录和评论,您可能会将其用作确定按键操作的基础。如果您可以获得发送击键的应用程序的句柄,您可以以这种方式跟踪它。有了这个句柄,您就可以轻松获得所需的信息。
其他应用尝试通过快捷方式确定热键,因为它们可以包含快捷键,这只是热键的另一个术语。但是,大多数应用程序不倾向于设置此属性,因此它可能不会返回太多。如果您对该路线感兴趣,Delphi可以访问 IShellLink
COM接口,您可以使用该接口加载快捷方式并获取其热键:
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 Books Online ,则有关于使用快捷方式/ shell链接的好部分在Steve Teixeira和Xavier Pacheco撰写的Borland Delphi 6开发人员指南中。我上面的例子是那里的屠宰版本和这个网站。
希望有所帮助!
其他提示
一种可能的方法是使用 Visual Studio工具Spy ++ 。
尝试一下:
- 运行该工具(对我而言,它位于
C:\ Program Files(x86)\ Microsoft Visual Studio \ 2017 \ Community \ Common7 \ Tools \ spyxx_amd64.exe
) - 在菜单栏中,选择 Spy - > 记录消息... (或点击 Ctrl + M )
- 在其他Windows 框架中检查系统中的所有Windows
- 切换到消息标签
- 点击全部清除按钮
- 在列表框中选择
WM_HOTKEY
,或者在消息组中查看键盘(如果你的电话有更多潜在噪音) - 点击确定按钮
- 按下有问题的热键(例如 Win + R )
- 在消息(所有Windows)窗口中选择
WM_HOTKEY
行,右键单击,然后在上下文菜单中选择属性... - 在消息属性对话框中,单击窗口句柄链接(这将是接收消息的窗口的句柄)
- 单击窗口属性对话框中的同步按钮。这将在主Spy ++窗口树视图中显示该窗口。
- 在窗口属性对话框中,选择进程选项卡
- 点击进程ID 链接。这将显示过程(在我的 Win + R 案例:
EXPLORER
)
醇>
经过一些研究,您似乎需要访问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内部构件的情况下需要进行一些深潜探测。
这似乎告诉了你很多: http://hkcmdr.anymania.com/help.html
另一个线程提到全局NT级键盘钩子:
也许你可以获得以这种方式调用钩子的进程句柄,然后你可以将其解析为进程名称
(免责声明:我在我的书签中有这个,没有真正尝试/测试过)
我知道您可以在自己的进程中的任何窗口中拦截消息流 - 我们以前在VB6中调用子类。 (虽然我不记得这个函数,也许是SetWindowLong?)我不确定你是否可以在自己的进程之外的窗口中执行此操作。但是为了这个帖子,我们假设您找到了一种方法。然后,您可以简单地拦截所有顶级窗口的消息,监视WM_HOTKEY消息。您无法立即知道所有键,但是当它们被按下时,您可以轻松找出使用它们的应用程序。如果将结果保存到磁盘并在每次运行监视器应用程序时重新加载,则可以随着时间的推移提高应用程序的性能。
这并不能完全回答有关Windows API的问题部分,但它回答的问题部分是关于全局热键列表和“拥有”的应用程序。他们。
http://hkcmdr.anymania.com/ 上的免费热键资源管理器显示了所有全球的列表热键和拥有它们的应用程序。这只是帮助我弄清楚为什么特定于应用程序的快捷键停止工作以及如何修复它(通过在已注册的应用程序中重新配置注册的全局热键),在几秒钟内。