¿Averigüe qué proceso registró una tecla de acceso rápido global? (API de Windows)

114

Por lo que he podido averiguar, Windows no ofrece una función API para saber qué aplicación ha registrado una tecla de acceso rápido global (a través de RegisterHotkey). Solo puedo averiguar que una tecla de acceso rápido está registrada si RegisterHotkey devuelve falso, pero no quién "posee" la tecla de acceso rápido.

En ausencia de una API directa, ¿podría haber una forma indirecta? Windows mantiene el identificador asociado con cada tecla de acceso rápido registrada; es un poco exasperante que no haya forma de obtener esta información.

Ejemplo de algo que probablemente no funcionaría: envíe (simule) una tecla de acceso rápido registrada, luego intercepte el mensaje de tecla de acceso rápido que Windows enviará al proceso que lo registró. Primero, no creo que interceptar el mensaje revelaría el identificador de la ventana de destino. En segundo lugar, incluso si fuera posible, sería algo malo, ya que enviar teclas de acceso rápido desencadenaría todo tipo de actividad potencialmente no deseada de varios programas.

No es nada crítico, pero he visto solicitudes frecuentes para dicha funcionalidad, y yo mismo he sido víctima de aplicaciones que registran teclas de acceso rápido sin siquiera revelarlas en ninguna parte de la interfaz de usuario o documentos.

(Trabajando en Delphi, y no más que un aprendiz en WinAPI, sea amable).

Marek Jedliński
fuente

Respuestas:

20

Su pregunta despertó mi interés, así que investigué un poco y aunque, desafortunadamente, no tengo una respuesta adecuada para usted, pensé en compartir lo que tengo.

Encontré este ejemplo de cómo crear un gancho de teclado (en Delphi) escrito en 1998, pero se puede compilar en Delphi 2007 con un par de ajustes.

Es una DLL con una llamada a SetWindowsHookEx que pasa a través de una función de devolución de llamada, que luego puede interceptar las pulsaciones de las teclas: en este caso, está jugando con ellas por diversión, cambiando el cursor de la izquierda a la derecha, etc. Una aplicación simple luego llama a la DLL y le informa. sus resultados basados ​​en un evento TTimer. Si está interesado, puedo publicar el código basado en Delphi 2007.

Está bien documentado y comentado y potencialmente podría usarlo como base para determinar hacia dónde se dirige la pulsación de una tecla. Si pudiera obtener el identificador de la aplicación que envió las pulsaciones de tecla, podría rastrearla de esa manera. Con ese identificador, podrá obtener la información que necesita con bastante facilidad.

Otras aplicaciones han intentado determinar las teclas de acceso rápido mediante sus accesos directos, ya que pueden contener una tecla de acceso directo, que es solo otro término para la tecla de acceso rápido. Sin embargo, la mayoría de las aplicaciones no suelen establecer esta propiedad, por lo que es posible que no devuelva mucho. Si está interesado en esa ruta, Delphi tiene acceso a la IShellLinkinterfaz COM que puede usar para cargar un acceso directo y obtener su tecla de acceso rápido:

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;

Si tiene acceso a Safari Books Online , hay una buena sección sobre cómo trabajar con accesos directos / enlaces de shell en la Guía del desarrollador de Borland Delphi 6 de Steve Teixeira y Xavier Pacheco. Mi ejemplo anterior es una versión sacrificada de allí y este sitio .

¡Espero que ayude!

Pauk
fuente
59

Una forma posible es utilizar la herramienta de Visual Studio Spy ++ .

Prueba esto:

  1. Ejecuta la herramienta (para mí, está en C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools\spyxx_amd64.exe)
  2. En la barra de menú, seleccione Espiar -> Registrar mensajes ... (o presione Ctrl+ M)
  3. Verifique todas las ventanas en el sistema en el marco de Windows adicional
  4. Cambiar a la pestaña Mensajes
  5. Haga clic en el botón Borrar todo
  6. Seleccione WM_HOTKEYen el cuadro de lista o marque Teclado en Grupos de mensajes (si está de acuerdo con más ruido potencial)
  7. Haga clic en el botón Aceptar
  8. Presione la tecla de acceso rápido en cuestión ( Win+ R, por ejemplo)
  9. Seleccione la WM_HOTKEYlínea en la ventana Mensajes (todas las ventanas) , haga clic derecho y seleccione Propiedades ... en el menú contextual
  10. En el cuadro de diálogo Propiedades del mensaje , haga clic en el enlace Identificador de ventana (este será el identificador de la ventana que recibió el mensaje)
  11. Haga clic en el botón Sincronizar en el cuadro de diálogo Propiedades de la ventana. Esto mostrará la ventana en la vista de árbol de la ventana principal de Spy ++.
  12. En el cuadro de diálogo Propiedades de la ventana, seleccione el proceso pestaña
  13. Haga clic en el enlace ID de proceso . Esto le mostrará el proceso (En mi Win+ Rcaso: EXPLORER)
usuario995048
fuente
6
¡Gran respuesta! Solo tenga en cuenta que la versión de 64 bits de Spy ++ captura solo mensajes para aplicaciones de 64 bits , por lo que si no ve el WM_HOTKEYmensaje en el registro de mensajes después de presionar la tecla de acceso rápido, es posible que deba ejecutar la versión de 32 bits de Spy ++ .
David Ferenczy Rogožan
¡¡¡MUCHAS GRACIAS!!! Básicamente, dejé de usar algunos atajos hasta que encontré esto.
thewindev
3
En el paso 8, no solicita una tecla de acceso rápido, la ventana de mensajes permanece vacía después de presionar cualquier tecla de acceso rápido. Usé versiones de 32 y 64 bits de github.com/westoncampbell/SpyPlusPlus (en Windows 10).
CoolMind
9

Después de algunas investigaciones, parece que necesitaría acceder a la estructura interna que utiliza MS para almacenar las teclas de acceso rápido. ReactOS tiene una implementación de sala limpia que implementa la GetHotKeyllamada iterando una lista interna y extrayendo la tecla de acceso rápido que coincide con los parámetros de la llamada.

Dependiendo de qué tan cerca esté la implementación de ReactOS de la implementación de MS, es posible que pueda hurgar en la memoria para encontrar la estructura, pero eso está por encima de mi cabeza ...

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;
}

Supongo que alguien relacionado con esta pregunta hizo este hilo en sysinternals , pero pensé que lo vincularía de todos modos para mantener los dos juntos. El hilo se ve muy intrigante, pero sospecho que sería necesario realizar una inmersión profunda en espeleología para resolver esto sin acceso a los componentes internos de MS.

John Weldon
fuente
3

En la parte superior de mi cabeza, puede intentar enumerar todas las ventanas con EnumWindows, luego, en la devolución de llamada, envíe WM_GETHOTKEY a cada ventana.

Editar: Aparentemente, estaba equivocado en eso. MSDN tiene más información:

WM_HOTKEY no está relacionado con las teclas de acceso rápido WM_GETHOTKEY y WM_SETHOTKEY. El mensaje WM_HOTKEY se envía para teclas de acceso rápido genéricas, mientras que los mensajes WM_SETHOTKEY y WM_GETHOTKEY se relacionan con las teclas de acceso rápido de activación de ventana.

Nota: Aquí hay un programa que pretende tener la funcionalidad que está buscando. Podrías intentar descompilarlo.

David
fuente
3
El enlace al programa ahora está completamente roto. ¿Qué programa fue ese? Hay tantas veces que me gustaría averiguar qué programa registró mis teclas de acceso rápido porque de repente ya no funcionan o hacen cosas nuevas e irritantes.
James Oltmans
(A menudo, Wayback Machine puede ayudar en caso de desaparición de la página web, pero aquí también solo ha reflejado un 404 Not Found: http://web.archive.org/web/*/tds.diamondcs.com.au/dse/ detección / hotkeys.php )
Aaron Thoma
2

Esto parece decirte mucho: http://hkcmdr.anymania.com/help.html

Caveatrob
fuente
1
¿Lo hace? ¿Una DLL disfrazada de controlador? La función DLL exportada como hoousos SetWindowHookExpara establecer dos ganchos, uno WH_KEYBOARD_LLy uno WH_GETMESSAGE... el resto debería estar bastante documentado en MSDN.
0xC0000022L
No use esta aplicación en Windows 8 o 10.
Jeff Lange
0

Sé que puede interceptar el flujo de mensajes en cualquier ventana dentro de su propio proceso, lo que solíamos llamar subclases en VB6. (Aunque no recuerdo la función, ¿quizás SetWindowLong?) No estoy seguro de si puede hacer esto para Windows fuera de su propio proceso. Pero por el bien de esta publicación, supongamos que encuentra una manera de hacerlo. Luego, simplemente puede interceptar los mensajes para todas las ventanas de nivel superior, monitorear el mensaje WM_HOTKEY. No podría conocer todas las teclas de inmediato, pero a medida que las presione, podría averiguar fácilmente qué aplicación las estaba usando. Si conserva los resultados en el disco y los vuelve a cargar cada vez que se ejecuta la aplicación del monitor, puede aumentar el rendimiento de la aplicación con el tiempo.

Sam Axe
fuente
-1

Esto no responde exactamente a la parte de la pregunta que trata sobre la API de Windows, pero responde a la parte de la pregunta que trata sobre una lista de teclas de acceso rápido globales y las aplicaciones que las "poseen".

El Hotkey Explorer gratuito en http://hkcmdr.anymania.com/ muestra una lista de todas las teclas de acceso rápido globales y las aplicaciones que las poseen. Esto me ha ayudado a descubrir por qué una tecla de acceso directo específica de la aplicación dejó de funcionar y cómo solucionarlo (reconfigurando la tecla de acceso rápido global registrada en la aplicación que la tenía registrada), en unos pocos segundos.

Gerhard
fuente
4
lo instaló y actuó como un virus, al menos un programa con intención comercial, como una broma de mal gusto. Abriendo todo tipo de ventanas, activando el narador de voz, etc.
Flion
3
@Flion: consulte la discusión en superuser.com/a/191090/3617 . Básicamente, algunas utilidades de teclas de acceso rápido funcionan probando todas las combinaciones posibles de teclas. En Windows 8+, (y en menor medida en Windows 7), esta técnica de sondeo activa cada combinación de teclas, en lugar de simplemente consultarla.
Brian
@Flion FWIW, lo usé en Win7 con éxito. Pero si entiendo correctamente a Brian, es posible que esto no funcione tan bien con versiones posteriores, y lo bien que funcione también puede depender de las teclas de acceso rápido personalizadas que se definan.
Gerhard