Introducción
Manejar eventos del sistema (hooks) sin una librería o una librería nativa (C ó C++) es problemático. ¿Cómo hacerlo desde código administrado sin ninguna librería? En resumen utilizando SetWinEventHook (no SetWinEventHookEx) con el flag WINEVENT_OUTOFCONTEXT.
Entorno
Un hook tradicional requiere crear una función o un método. Adicionalmente este método o función debe “ser mapeada en el espacio de direcciones del proceso que genera el evento”. Lo que significa que el método debe ser un método residente en una biblioteca nativa (DLL en C++). La razón de esto es que el sistema cargará y mapeará la ubicación de la DLL y requiere esto para encontrar el puntero de la función de tu método.
Entonces ¿cómo podemos hacer si parece que no es posible hacerlo con código administrado? Bien, hay una trampa en el escenario descrito. Cuando se utiliza el término “Hook” aplica a lo que significa el “hook”. Pero hay un tipo de hook que ha llegado a ser sinónimo de todos los hook. Espero entiendas, es algo confuso. Ese tipo de hook es llamado un "In-Context Hook" (hook en contexto).
Microsoft dice que la siguientes características describen los aspectos clave en las funciones de in-context hook:
Entonces, ya sabemos que hay varios tipos de hooks. Nosotros no podemos simplemente y eficientemente crear callabacks por hooks in-context utilizando lenguajes administrados. ¿Cuál podría ser tus opciones? Ahí están las funciones de "Out-of-Context Hook".
Microsoft dice que las siguientes características son los aspectos clave de las funciones out-of-context hook:
Como puedes ver que los hooks Out-of-Context son muy diferentes de los hooks tradicionales. Nota la parte que dice que las funciones hooks "Out-of-context” son notablemente más lentas debido a la serialización. Esto llega a ser menos relevante en estos días. Código más lento pero confiable es mejor que código rápido no confiable; las soluciones administradas son el futuro. Como puedes ver no es necesario algo especial para implementar un hook Out-of-Context. La única cosa que se necesita es hacerlo.
El Código
Considera la siguiente clase:
C#:
public enum SystemEvents : uint
{
EVENT_SYSTEM_FOREGROUND = 3, //Active Foreground Window
EVENT_SYSTEM_CAPTURESTART = 8, //Active Foreground Window Mouse Capture
EVENT_OBJECT_CREATE = 32768, //An object has been created. The system sends this event for the following user interface elements: caret, header control, list-view control, tab control, toolbar control, tree view control, and window object.
EVENT_OBJECT_DESTROY = 32769, //An object has been destroyed. The system sends this event for the following user interface elements: caret, header control, list-view control, tab control, toolbar control, tree view control, and window object.
EVENT_OBJECT_FOCUS = 32773 //An object has received the keyboard focus. The system sends this event for the following user interface elements: list-view control, menu bar, pop-up menu, switch window, tab control, tree view control, and window object.
}
public class SystemEvent
{
private const uint WINEVENT_OUTOFCONTEXT = 0;
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern System.IntPtr SetWinEventHook(uint eventMin, uint eventMax, System.IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
private delegate void WinEventDelegate(System.IntPtr hWinEventHook, uint eventType, System.IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
public event SystemEventEventHandler SystemEventHandler;
public delegate void SystemEventEventHandler(System.IntPtr hWinEventHook, uint eventType, System.IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
private uint m_event = 0;
private WinEventDelegate m_delegate = null;
private System.IntPtr m_foregroundHwnd = System.IntPtr.Zero;
public SystemEvent(SystemEvents SystemEvent)
{
m_event =System.Convert.ToUInt32(SystemEvent);
m_delegate = new WinEventDelegate(WinEventProc);
try {
SetWinEventHook(m_event, m_event, System.IntPtr.Zero, m_delegate,System.Convert.ToUInt32(0),System.Convert.ToUInt32(0), WINEVENT_OUTOFCONTEXT);
} catch (System.Exception ex) {
System.Diagnostics.Debug.WriteLine(ex.ToString());
}
}
public void WinEventProc(System.IntPtr hWinEventHook, uint eventType, System.IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
if ((((SystemEventHandler != null)) && (SystemEventHandler.GetInvocationList().Length > 0))) {
m_foregroundHwnd = hwnd;
if (SystemEventHandler != null) {
SystemEventHandler(hWinEventHook, eventType, hwnd, idObject, idChild, dwEventThread, dwmsEventTime);
}
}
}
public System.IntPtr Hwnd
{
get { return m_foregroundHwnd; }
}
}
Vb.NET:
Public Enum SystemEvents As UInteger
EVENT_SYSTEM_FOREGROUND = 3 'Active Foreground Window
EVENT_SYSTEM_CAPTURESTART = 8 'Active Foreground Window Mouse Capture
EVENT_OBJECT_CREATE = 32768 'An object has been created. The system sends this event for the following user interface elements: caret, header control, list-view control, tab control, toolbar control, tree view control, and window object.
EVENT_OBJECT_DESTROY = 32769 'An object has been destroyed. The system sends this event for the following user interface elements: caret, header control, list-view control, tab control, toolbar control, tree view control, and window object.
EVENT_OBJECT_FOCUS = 32773 'An object has received the keyboard focus. The system sends this event for the following user interface elements: list-view control, menu bar, pop-up menu, switch window, tab control, tree view control, and window object.
End Enum
Public Class SystemEvent
Private Const WINEVENT_OUTOFCONTEXT As UInteger = 0
<System.Runtime.InteropServices.DllImport("user32.dll")> _
Private Shared Sub SetWinEventHook(ByVal eventMin As UInteger, ByVal eventMax As UInteger, ByVal hmodWinEventProc As IntPtr, ByVal lpfnWinEventProc As WinEventDelegate, ByVal idProcess As UInteger, ByVal idThread As UInteger, ByVal dwFlags As UInteger)
End Sub
Private Delegate Sub WinEventDelegate(ByVal hWinEventHook As IntPtr, ByVal eventType As UInteger, ByVal hwnd As IntPtr, ByVal idObject As Integer, ByVal idChild As Integer, ByVal dwEventThread As UInteger, ByVal dwmsEventTime As UInteger)
Public Event SystemEvent(ByVal hWinEventHook As IntPtr, ByVal eventType As UInteger, ByVal hwnd As IntPtr, ByVal idObject As Integer, ByVal idChild As Integer, ByVal dwEventThread As UInteger, ByVal dwmsEventTime As UInteger)
Private m_event As UInteger = 0
Private m_delegate As WinEventDelegate = Nothing
Private m_foregroundHwnd As IntPtr = IntPtr.Zero
Public Sub New(ByVal SystemEvent As SystemEvents)
m_event = CUInt(SystemEvent)
m_delegate = New WinEventDelegate(AddressOf WinEventProc)
Try
SetWinEventHook(m_event, m_event, IntPtr.Zero, m_delegate, CUInt(0), CUInt(0), WINEVENT_OUTOFCONTEXT)
Catch ex As Exception
Debug.WriteLine(ex.ToString)
End Try
End Sub
Public Sub WinEventProc(ByVal hWinEventHook As IntPtr, ByVal eventType As UInteger, ByVal hwnd As IntPtr, ByVal idObject As Integer, ByVal idChild As Integer, ByVal dwEventThread As UInteger, ByVal dwmsEventTime As UInteger)
If ((Not SystemEventEvent Is Nothing) AndAlso (SystemEventEvent.GetInvocationList.Length > 0)) Then
m_foregroundHwnd = hwnd
RaiseEvent SystemEvent(hWinEventHook, eventType, hwnd, idObject, idChild, dwEventThread, dwmsEventTime)
End If
End Sub
Public ReadOnly Property Hwnd() As IntPtr
Get
Return m_foregroundHwnd
End Get
End Property
End Class
Para utilizar esas clases, en Vb.NET:
Dim KeyboardFocus As New SystemEvent(SystemEvents.EVENT_OBJECT_FOCUS)
Dim ForegroundWindowWhoHasKeyboardFocus As IntPtr = KeyboardFocus.Hwnd
Para utilizar esas clases, en C#:
SystemEvent KeyboardFocus = new SystemEvent(SystemEvents.EVENT_OBJECT_FOCUS);
System.IntPtr ForegroundWindowWhoHasKeyboardFocus = KeyboardFocus.Hwnd;
En vez de llamar aGetForegroundWindow
cada vez que quieres saber cual aplicación tiene el foco del teclado. Simplemente lo lees desdeKeyboardFocus.Hwnd.
¿Hay una lista sustancial de EVENT_OBJECT_
? Puedes encontrar la lista haciendo una búsqueda para
"EVENT_OBJECT_FOCUS = 32773", en la clase sólo hay unos ejemplos.
Puntos de Interés
Sería grandioso ver otros hooks Out-of-Context de ustedes. Si conoces algunos, deja un comentario.
0 comentarios:
Publicar un comentario