21
21
From MSDN, re WH_KEYBOARD_LL & WH_MOUSE_LL:
23
This hook is called in the context of the thread that
24
installed it. The call is made by sending a message to
25
the thread that installed the hook.
23
This hook is called in the context of the thread that
24
installed it. The call is made by sending a message to
25
the thread that installed the hook.
27
Therefore, the thread that installed the hook must
27
Therefore, the thread that installed the hook must
31
Because a low-level hook is called in the context of
32
the thread that installed it, we can install the hook
31
Because a low-level hook is called in the context of
32
the thread that installed it, we can install the hook
33
33
from a time critical thread, and run a message loop.
35
35
This should solve the Philips slowdown problem:
36
1. high priority callback thread returns immediately after
36
1. high priority callback thread returns immediately after
37
37
posting a message to the dispatch thread
38
38
2. the dispatch thread processes and notifies main thread
78
78
Therefore BOOL functions can return -1.
79
79
MSDN notes GetMessage BOOL return is not only 0,1 but also -1.
81
BOOL ( WINAPI *W32LowLevelMonitor::GetMessageW )
82
( LPMSG, HWND, UINT, UINT ) = NULL;
83
BOOL ( WINAPI *W32LowLevelMonitor::PeekMessageW )
84
( LPMSG, HWND, UINT, UINT, UINT ) = NULL;
85
BOOL ( WINAPI *W32LowLevelMonitor::PostThreadMessageW )
86
( DWORD, UINT, WPARAM, LPARAM ) = NULL;
87
HHOOK ( WINAPI *W32LowLevelMonitor::SetWindowsHookExW )
88
( int, HOOKPROC, HINSTANCE, DWORD ) = NULL;
89
BOOL ( WINAPI *W32LowLevelMonitor::SwitchToThread ) ( void ) = NULL;
92
83
W32LowLevelMonitor::W32LowLevelMonitor()
94
85
TRACE_ENTER( "W32LowLevelMonitor::W32LowLevelMonitor" );
96
87
if( singleton != NULL )
98
89
TRACE_RETURN( " singleton != NULL " );
104
95
dispatch = new thread_struct;
105
96
dispatch->name = "Dispatch";
107
98
callback = new thread_struct;
108
99
callback->name = "Callback";
104
process_handle = GetModuleHandle( NULL );
117
110
W32LowLevelMonitor::~W32LowLevelMonitor()
119
112
TRACE_ENTER( "W32LowLevelMonitor::~W32LowLevelMonitor" );
121
114
if( singleton != this )
123
116
TRACE_RETURN( " singleton != this " );
134
127
singleton = NULL;
140
bool W32LowLevelMonitor::check_api()
142
TRACE_ENTER( "W32LowLevelMonitor::check_api" );
144
process_handle = GetModuleHandle( NULL );
145
HMODULE user32_handle = GetModuleHandleA( "user32.dll" );
147
GetMessageW = ( BOOL ( WINAPI * ) ( LPMSG, HWND, UINT, UINT ) )
148
GetProcAddress( user32_handle, "GetMessageW" );
150
PeekMessageW = ( BOOL ( WINAPI * ) ( LPMSG, HWND, UINT, UINT, UINT ) )
151
GetProcAddress( user32_handle, "PeekMessageW" );
153
PostThreadMessageW = ( BOOL ( WINAPI * ) ( DWORD, UINT, WPARAM, LPARAM ) )
154
GetProcAddress( user32_handle, "PostThreadMessageW" );
156
SetWindowsHookExW = ( HHOOK ( WINAPI * ) ( int, HOOKPROC, HINSTANCE, DWORD ) )
157
GetProcAddress( user32_handle, "SetWindowsHookExW" );
159
SwitchToThread = ( BOOL ( WINAPI * ) ( void ) )
160
GetProcAddress( GetModuleHandleA( "kernel32.dll" ), "SwitchToThread" );
162
if( process_handle && GetMessageW && PeekMessageW &&
163
PostThreadMessageW && SetWindowsHookExW && SwitchToThread )
170
TRACE_MSG( " failed." );
171
TRACE_MSG( "process_handle == " << process_handle );
172
TRACE_MSG( "GetMessageW == " << GetMessageW );
173
TRACE_MSG( "PeekMessageW == " << PeekMessageW );
174
TRACE_MSG( "PostThreadMessageW == " << PostThreadMessageW );
175
TRACE_MSG( "SetWindowsHookExW == " << SetWindowsHookExW );
176
TRACE_MSG( "SwitchToThread == " << SwitchToThread );
183
133
bool W32LowLevelMonitor::init()
185
135
TRACE_ENTER( "W32LowLevelMonitor::init" );
187
137
if( singleton != this )
189
139
TRACE_RETURN( " singleton != this " );
195
TRACE_RETURN( " : init failed. " );
202
146
CreateThread( NULL, 0, thread_Dispatch, this, 0, &dispatch->id );
204
148
if( !wait_for_thread_queue( dispatch ) )
212
156
CreateThread( NULL, 0, thread_Callback, this, 0, &callback->id );
214
158
if( !wait_for_thread_queue( callback ) )
221
165
Harpoon::init(NULL);
227
171
bool W32LowLevelMonitor::wait_for_thread_queue( thread_struct *thread )
229
173
TRACE_ENTER_MSG( "W32LowLevelMonitor::wait_for_thread_queue : ", thread->name);
231
175
if( !thread->handle || !thread->id )
233
177
TRACE_RETURN( " thread: creation failed." );
237
181
DWORD thread_exit_code;
241
185
thread_exit_code = 0;
242
187
GetExitCodeThread( thread->handle, &thread_exit_code );
243
188
if( thread_exit_code != STILL_ACTIVE )
245
190
TRACE_RETURN( " thread: terminated prematurely." );
248
( *SwitchToThread )();
249
193
} while( thread->active == false );
251
195
SetLastError( 0 );
252
BOOL ret = ( *PostThreadMessageW )( thread->id, 0xFFFF, 0, 0 );
196
BOOL ret = PostThreadMessageW( thread->id, 0xFFFF, 0, 0 );
253
197
DWORD gle = GetLastError();
255
199
if( !ret || gle )
257
201
TRACE_MSG( " thread: PostThreadMessage test failed." );
330
283
DWORD W32LowLevelMonitor::dispatch_thread()
332
285
dispatch->active = false;
337
// It's good practice to force creation of the thread
290
// It's good practice to force creation of the thread
338
291
// message queue before setting active.
339
( *PeekMessageW )( &msg, NULL, WM_USER, WM_USER, PM_NOREMOVE );
292
PeekMessageW( &msg, NULL, WM_USER, WM_USER, PM_NOREMOVE );
340
293
dispatch->active = true;
342
while( ret = ( *GetMessageW )( &msg, NULL, 0, 0 ) > 0 && dispatch->active )
295
while( ( ret = GetMessageW( &msg, NULL, 0, 0 ) > 0 ) && dispatch->active )
344
297
msg.message &= 0xFFFF;
345
298
if( msg.message > WM_APP)
403
356
DWORD W32LowLevelMonitor::time_critical_callback_thread()
405
358
callback->active = false;
408
HANDLE handle = GetCurrentThread();
410
// An attempt to set the thread priority to
363
// An attempt to set the thread priority to
411
364
// THREAD_PRIORITY_TIME_CRITICAL.
412
// Try for several seconds.
413
while( SetThreadPriority( handle, 15 ) == 0 )
365
for( i = 0; ( !SetThreadPriority( GetCurrentThread(), 15 ) && ( i < 100 ) ); ++i )
418
//Give up our time slice, try next schedule.
419
( *SwitchToThread )();
422
371
Do not double check using GetThreadPriority.
423
It's possible, throughout this thread's lifetime, that
372
It's possible, throughout this thread's lifetime, that
424
373
an administrative application could change the priority.
428
The system creates a thread's message queue when the thread
429
makes its first call to one of the User or GDI functions.
431
unhook(); // thread message queue created here
434
( *SetWindowsHookExW )( WH_KEYBOARD_LL, &k_hook_callback, process_handle, 0 );
436
( *SetWindowsHookExW )( WH_MOUSE_LL, &m_hook_callback, process_handle, 0 );
376
// It's good practice to force creation of the thread
377
// message queue before setting active.
378
PeekMessageW( &msg, NULL, WM_USER, WM_USER, PM_NOREMOVE );
383
SetWindowsHookExW( WH_KEYBOARD_LL, &k_hook_callback, process_handle, 0 );
385
SetWindowsHookExW( WH_MOUSE_LL, &m_hook_callback, process_handle, 0 );
438
387
if( !k_hook || !m_hook )
441
390
return (DWORD) 0;
447
// It's good practice to force creation of the thread
448
// message queue before setting active.
449
( *PeekMessageW )( &msg, NULL, WM_USER, WM_USER, PM_NOREMOVE );
450
393
callback->active = true;
452
// Message loop. As noted, a hook is called in the
395
// Message loop. As noted, a hook is called in the
453
396
// context of the thread that installed it. i.e. this one.
454
while( ( *GetMessageW )( &msg, NULL, 0, 0 ) && callback->active )
397
while( GetMessageW( &msg, NULL, 0, 0 ) && callback->active )
458
401
callback->active = false;
460
403
// Always return a value != STILL_ACTIVE (259)
461
404
return (DWORD) 1;
465
LRESULT CALLBACK W32LowLevelMonitor::k_hook_callback(
408
LRESULT CALLBACK W32LowLevelMonitor::k_hook_callback(
466
409
int nCode, WPARAM wParam, LPARAM lParam )
468
411
DWORD flags = ( (_KBDLLHOOKSTRUCT *) lParam )->flags;
470
413
if( !nCode && !( flags & LLKHF_INJECTED ) )
471
414
// If there is an event, and it's not injected, notify.
473
( *PostThreadMessageW )
475
418
dispatch->id, //idThread
477
420
(WPARAM) flags, //wParam
478
421
(LPARAM) 0 //lParam
482
425
return CallNextHookEx( (HHOOK) 0, nCode, wParam, lParam );
486
LRESULT CALLBACK W32LowLevelMonitor::m_hook_callback(
429
LRESULT CALLBACK W32LowLevelMonitor::m_hook_callback(
487
430
int nCode, WPARAM wParam, LPARAM lParam )
489
432
DWORD flags = ( (_MSLLHOOKSTRUCT *) lParam )->flags;
492
435
if( !nCode ) // && !( flags & LLMHF_INJECTED ) )
493
436
// If there is an event, and it's not injected, notify.
494
437
// RC: My Wacom tablet driver injects mouse move events...
496
( *PostThreadMessageW )
498
441
dispatch->id, //idThread
499
442
WM_APP + (DWORD) wParam, //Msg