source: trunk/src/user32/HOOK.CPP@ 2364

Last change on this file since 2364 was 2097, checked in by sandervl, 26 years ago

hook removal bugfix

File size: 18.5 KB
Line 
1/* $Id: HOOK.CPP,v 1.7 1999-12-17 16:56:54 sandervl Exp $ */
2
3/*
4 * Windows hook functions
5 *
6 * Copyright 1999 Sander van Leeuwen (OS/2 Port)
7 *
8 * Port of Wine code (windows\hook.c; dated 990920)
9 * All 16 bits code removed
10 *
11 * Copyright 1994, 1995 Alexandre Julliard
12 * 1996 Andrew Lewycky
13 *
14 * Based on investigations by Alex Korobka
15 *
16 *
17 * Project Odin Software License can be found in LICENSE.TXT
18 */
19
20/*
21 * Warning!
22 * A HHOOK is a 32-bit handle for compatibility with Windows 3.0 where it was
23 * a pointer to the next function. Now it is in fact composed of a USER heap
24 * handle in the low 16 bits and of a HOOK_MAGIC value in the high 16 bits.
25 */
26
27#include <os2win.h>
28#include "hook.h"
29#include "win.h"
30#include "queue.h"
31#include "task.h"
32#include "winproc.h"
33#include "debugtools.h"
34#include <misc.h>
35#include <heapstring.h>
36#include <vmutex.h>
37#include <wprocess.h>
38
39DEFAULT_DEBUG_CHANNEL(hook)
40
41#include "pshpack1.h"
42
43 /* Hook data (pointed to by a HHOOK) */
44typedef struct
45{
46 HANDLE next; /* 00 Next hook in chain */
47 HOOKPROC proc; /* 04 Hook procedure (original) */
48 INT id; /* 08 Hook id (WH_xxx) */
49 DWORD ownerThread; /* 0C Owner thread (0 for system hook) */
50 HMODULE ownerModule; /* 10 Owner module */
51 DWORD flags; /* 14 flags */
52 DWORD magic; /* 18 magic dword */
53} HOOKDATA;
54
55#include "poppack.h"
56
57#define HOOK_MAGIC1 ((int)'H' | (int)'K' << 8) /* 'HK' */
58#define HOOK_MAGIC ((HOOK_MAGIC1<<16)|HOOK_MAGIC1) // 'HKHK'
59
60#define CHECK_MAGIC(a) ((a != 0) && (((HOOKDATA *)a)->magic == HOOK_MAGIC))
61
62//Global DLL Data
63#pragma data_seg(_GLOBALDATA)
64static HANDLE HOOK_systemHooks[WH_NB_HOOKS] = { 0 };
65static VMutex systemHookMutex(TRUE);
66#pragma data_seg()
67static HANDLE HOOK_threadHooks[WH_NB_HOOKS] = { 0 };
68static VMutex threadHookMutex;
69
70typedef VOID (*HOOK_MapFunc)(INT, INT, WPARAM *, LPARAM *);
71typedef VOID (*HOOK_UnMapFunc)(INT, INT, WPARAM, LPARAM, WPARAM,
72 LPARAM);
73
74/***********************************************************************
75 * HOOK_Map32ATo32W
76 */
77static void HOOK_Map32ATo32W(INT id, INT code, WPARAM *pwParam,
78 LPARAM *plParam)
79{
80 if (id == WH_CBT && code == HCBT_CREATEWND)
81 {
82 LPCBT_CREATEWNDA lpcbtcwA = (LPCBT_CREATEWNDA)*plParam;
83 LPCBT_CREATEWNDW lpcbtcwW = (LPCBT_CREATEWNDW)HeapAlloc(GetProcessHeap(), 0,
84 sizeof(*lpcbtcwW) );
85 lpcbtcwW->lpcs = (CREATESTRUCTW*)HeapAlloc( GetProcessHeap(), 0, sizeof(*lpcbtcwW->lpcs) );
86
87 lpcbtcwW->hwndInsertAfter = lpcbtcwA->hwndInsertAfter;
88 *lpcbtcwW->lpcs = *(LPCREATESTRUCTW)lpcbtcwA->lpcs;
89
90 if (HIWORD(lpcbtcwA->lpcs->lpszName))
91 {
92 lpcbtcwW->lpcs->lpszName = HEAP_strdupAtoW( GetProcessHeap(), 0,
93 lpcbtcwA->lpcs->lpszName );
94 }
95 else
96 lpcbtcwW->lpcs->lpszName = (LPWSTR)lpcbtcwA->lpcs->lpszName;
97
98 if (HIWORD(lpcbtcwA->lpcs->lpszClass))
99 {
100 lpcbtcwW->lpcs->lpszClass = HEAP_strdupAtoW( GetProcessHeap(), 0,
101 lpcbtcwA->lpcs->lpszClass );
102 }
103 else
104 lpcbtcwW->lpcs->lpszClass = (LPCWSTR)lpcbtcwA->lpcs->lpszClass;
105 *plParam = (LPARAM)lpcbtcwW;
106 }
107 return;
108}
109
110
111/***********************************************************************
112 * HOOK_UnMap32ATo32W
113 */
114static void HOOK_UnMap32ATo32W(INT id, INT code, WPARAM wParamOrig,
115 LPARAM lParamOrig, WPARAM wParam,
116 LPARAM lParam)
117{
118 if (id == WH_CBT && code == HCBT_CREATEWND)
119 {
120 LPCBT_CREATEWNDW lpcbtcwW = (LPCBT_CREATEWNDW)lParam;
121 if (HIWORD(lpcbtcwW->lpcs->lpszName))
122 HeapFree( GetProcessHeap(), 0, (LPWSTR)lpcbtcwW->lpcs->lpszName );
123 if (HIWORD(lpcbtcwW->lpcs->lpszClass))
124 HeapFree( GetProcessHeap(), 0, (LPWSTR)lpcbtcwW->lpcs->lpszClass );
125 HeapFree( GetProcessHeap(), 0, lpcbtcwW->lpcs );
126 HeapFree( GetProcessHeap(), 0, lpcbtcwW );
127 }
128 return;
129}
130
131
132/***********************************************************************
133 * HOOK_Map32WTo32A
134 */
135static void HOOK_Map32WTo32A(INT id, INT code, WPARAM *pwParam,
136 LPARAM *plParam)
137{
138 if (id == WH_CBT && code == HCBT_CREATEWND)
139 {
140 LPCBT_CREATEWNDW lpcbtcwW = (LPCBT_CREATEWNDW)*plParam;
141 LPCBT_CREATEWNDA lpcbtcwA = (LPCBT_CREATEWNDA)HeapAlloc(GetProcessHeap(), 0,
142 sizeof(*lpcbtcwA) );
143 lpcbtcwA->lpcs = (CREATESTRUCTA*)HeapAlloc( GetProcessHeap(), 0, sizeof(*lpcbtcwA->lpcs) );
144
145 lpcbtcwA->hwndInsertAfter = lpcbtcwW->hwndInsertAfter;
146 *lpcbtcwA->lpcs = *(LPCREATESTRUCTA)lpcbtcwW->lpcs;
147
148 if (HIWORD(lpcbtcwW->lpcs->lpszName))
149 lpcbtcwA->lpcs->lpszName = HEAP_strdupWtoA( GetProcessHeap(), 0,
150 lpcbtcwW->lpcs->lpszName );
151 else
152 lpcbtcwA->lpcs->lpszName = (LPSTR)lpcbtcwW->lpcs->lpszName;
153
154 if (HIWORD(lpcbtcwW->lpcs->lpszClass))
155 lpcbtcwA->lpcs->lpszClass = HEAP_strdupWtoA( GetProcessHeap(), 0,
156 lpcbtcwW->lpcs->lpszClass );
157 else
158 lpcbtcwA->lpcs->lpszClass = (LPSTR)lpcbtcwW->lpcs->lpszClass;
159 *plParam = (LPARAM)lpcbtcwA;
160 }
161 return;
162}
163
164
165/***********************************************************************
166 * HOOK_UnMap32WTo32A
167 */
168static void HOOK_UnMap32WTo32A(INT id, INT code, WPARAM wParamOrig,
169 LPARAM lParamOrig, WPARAM wParam,
170 LPARAM lParam)
171{
172 if (id == WH_CBT && code == HCBT_CREATEWND)
173 {
174 LPCBT_CREATEWNDA lpcbtcwA = (LPCBT_CREATEWNDA)lParam;
175 if (HIWORD(lpcbtcwA->lpcs->lpszName))
176 HeapFree( GetProcessHeap(), 0, (LPSTR)lpcbtcwA->lpcs->lpszName );
177 if (HIWORD(lpcbtcwA->lpcs->lpszClass))
178 HeapFree( GetProcessHeap(), 0, (LPSTR)lpcbtcwA->lpcs->lpszClass );
179 HeapFree( GetProcessHeap(), 0, lpcbtcwA->lpcs );
180 HeapFree( GetProcessHeap(), 0, lpcbtcwA );
181 }
182 return;
183}
184
185
186/***********************************************************************
187 * Map Function Tables
188 */
189static const HOOK_MapFunc HOOK_MapFuncs[3][3] =
190{
191 { NULL, NULL, NULL },
192 { NULL, NULL, HOOK_Map32ATo32W },
193 { NULL, HOOK_Map32WTo32A, NULL }
194};
195
196static const HOOK_UnMapFunc HOOK_UnMapFuncs[3][3] =
197{
198 { NULL, NULL, NULL },
199 { NULL, NULL, HOOK_UnMap32ATo32W },
200 { NULL, HOOK_UnMap32WTo32A, NULL }
201};
202
203
204/***********************************************************************
205 * Internal Functions
206 */
207
208/***********************************************************************
209 * HOOK_GetNextHook
210 *
211 * Get the next hook of a given hook.
212 */
213static HANDLE HOOK_GetNextHook( HANDLE hook )
214{
215 HOOKDATA *data = (HOOKDATA *)hook;
216
217 if (!data || !hook) return 0;
218 if (data->next) return data->next;
219 if (!data->ownerThread) return 0; /* Already system hook */
220
221 /* Now start enumerating the system hooks */
222 return HOOK_systemHooks[data->id - WH_MINHOOK];
223}
224
225
226/***********************************************************************
227 * HOOK_GetHook
228 *
229 * Get the first hook for a given type.
230 */
231static HANDLE HOOK_GetHook( INT id, DWORD threadId )
232{
233 MESSAGEQUEUE *queue;
234 HANDLE hook = 0;
235 THDB *thdb;
236
237 thdb = GetTHDBFromThreadId(threadId);
238 if(thdb) {
239 hook = thdb->hooks[id - WH_MINHOOK];
240 }
241 if (!hook) hook = HOOK_systemHooks[id - WH_MINHOOK];
242
243 return hook;
244}
245
246
247/***********************************************************************
248 * HOOK_SetHook
249 *
250 * Install a given hook.
251 */
252static HHOOK HOOK_SetHook( INT id, LPVOID proc, INT type,
253 HMODULE hModule, DWORD dwThreadId )
254{
255 HOOKDATA *data;
256 THDB *thdb;
257
258 if ((id < WH_MINHOOK) || (id > WH_MAXHOOK) || !proc )
259 {
260 SetLastError(ERROR_INVALID_PARAMETER);
261 return 0;
262 }
263
264 dprintf(("Setting hook %d: %08x %04x %08lx\n",
265 id, (UINT)proc, hModule, dwThreadId ));
266
267#ifndef __WIN32OS2__
268 /* Create task queue if none present */
269 GetFastQueue16();
270
271 if (id == WH_JOURNALPLAYBACK) EnableHardwareInput16(FALSE);
272#endif
273
274
275 if (dwThreadId) /* Task-specific hook */
276 {
277 if ((id == WH_JOURNALRECORD) || (id == WH_JOURNALPLAYBACK) ||
278 (id == WH_SYSMSGFILTER)) {
279 SetLastError(ERROR_INVALID_PARAMETER);
280 return 0; /* System-only hooks */
281 }
282 }
283
284 /* Create the hook structure */
285
286 data = (HOOKDATA *) HeapAlloc(GetProcessHeap(), 0, sizeof(HOOKDATA));
287 data->proc = (HOOKPROC)proc;
288 data->id = id;
289 data->ownerThread = dwThreadId;
290 data->ownerModule = hModule;
291 data->flags = type;
292 data->magic = HOOK_MAGIC;
293
294 /* Insert it in the correct linked list */
295 if(dwThreadId)
296 {
297 thdb = GetTHDBFromThreadId(dwThreadId);
298 if(!thdb) {
299 dprintf(("HOOK_SetHook: can't find thread database for thread %x", dwThreadId));
300 return 0;
301 }
302 threadHookMutex.enter();
303 data->next = thdb->hooks[id - WH_MINHOOK];
304 thdb->hooks[id - WH_MINHOOK] = (DWORD)data;
305 threadHookMutex.leave();
306 }
307 else
308 {
309 systemHookMutex.enter();
310 data->next = HOOK_systemHooks[id - WH_MINHOOK];
311 HOOK_systemHooks[id - WH_MINHOOK] = (HANDLE)data;
312 systemHookMutex.leave();
313 }
314
315 return (HHOOK)data;
316}
317
318
319/***********************************************************************
320 * HOOK_RemoveHook
321 *
322 * Remove a hook from the list.
323 */
324static BOOL HOOK_RemoveHook( HOOKDATA *data )
325{
326 HOOKDATA **prevHook;
327 THDB *thdb;
328 VMutex *hookMutex;
329
330 dprintf(("Removing hook %08x\n", data));
331
332 if (data->flags & HOOK_INUSE)
333 {
334 /* Mark it for deletion later on */
335 dprintf(("Hook still running, deletion delayed\n" ));
336 data->flags |= HOOK_DELAYED_DELETE;
337 return TRUE;
338 }
339
340#ifndef __WIN32OS2__
341 if (data->id == WH_JOURNALPLAYBACK) EnableHardwareInput16(TRUE);
342#endif
343
344 /* Remove it from the linked list */
345
346 if (data->ownerThread)
347 {
348 thdb = GetTHDBFromThreadId(data->ownerThread);
349 if(!thdb) {
350 dprintf(("HOOK_RemoveHook: can't find thread database for thread %x", data->ownerThread));
351 return FALSE;
352 }
353 hookMutex = &threadHookMutex;
354 hookMutex->enter();
355 prevHook = (HOOKDATA **)&thdb->hooks[data->id - WH_MINHOOK];
356 }
357 else {
358 hookMutex = &systemHookMutex;
359 hookMutex->enter();
360 prevHook = (HOOKDATA **)&HOOK_systemHooks[data->id - WH_MINHOOK];
361 }
362 while (*prevHook && *prevHook != data)
363 prevHook = (HOOKDATA **)&(*prevHook)->next;
364
365 if (!prevHook) {
366 hookMutex->leave();
367 return FALSE;
368 }
369 *prevHook = (HOOKDATA *)data->next;
370 hookMutex->leave();
371
372 HeapFree(GetProcessHeap(), 0, (LPVOID)data );
373 return TRUE;
374}
375
376
377/***********************************************************************
378 * HOOK_FindValidHook
379 */
380static HANDLE HOOK_FindValidHook( HANDLE hook )
381{
382 HOOKDATA *data;
383
384 for (;;)
385 {
386 if (!(data = (HOOKDATA *)hook)) return 0;
387 if (data->proc) return hook;
388 hook = data->next;
389 }
390}
391
392
393/***********************************************************************
394 * HOOK_CallHook
395 *
396 * Call a hook procedure.
397 */
398static LRESULT HOOK_CallHook( HANDLE hook, INT fromtype, INT code,
399 WPARAM wParam, LPARAM lParam )
400{
401 MESSAGEQUEUE *queue;
402 HANDLE prevHook;
403 HOOKDATA *data = (HOOKDATA *)hook;
404 LRESULT ret;
405
406 WPARAM wParamOrig = wParam;
407 LPARAM lParamOrig = lParam;
408 HOOK_MapFunc MapFunc;
409 HOOK_UnMapFunc UnMapFunc;
410
411 MapFunc = HOOK_MapFuncs[fromtype][data->flags & HOOK_MAPTYPE];
412 UnMapFunc = HOOK_UnMapFuncs[fromtype][data->flags & HOOK_MAPTYPE];
413
414 if (MapFunc)
415 MapFunc( data->id, code, &wParam, &lParam );
416
417 /* Now call it */
418
419 data->flags |= HOOK_INUSE;
420
421 dprintf2(("Calling hook %04x: %d %08x %08lx\n", hook, code, wParam, lParam ));
422
423 ret = data->proc(code, wParam, lParam);
424
425 data->flags &= ~HOOK_INUSE;
426
427 if (UnMapFunc)
428 UnMapFunc( data->id, code, wParamOrig, lParamOrig, wParam, lParam );
429
430 if(data->flags & HOOK_DELAYED_DELETE) HOOK_RemoveHook( data );
431
432 return ret;
433}
434
435/***********************************************************************
436 * Exported Functions & APIs
437 */
438
439/***********************************************************************
440 * HOOK_IsHooked
441 *
442 * Replacement for calling HOOK_GetHook from other modules.
443 */
444BOOL HOOK_IsHooked( INT id )
445{
446 /* Hmmm. Use GetThreadQueue(0) instead of GetFastQueue() here to
447 avoid queue being created if someone wants to merely check ... */
448
449 return HOOK_GetHook( id, GetCurrentThreadId() ) != 0;
450}
451
452/***********************************************************************
453 * HOOK_CallHooks32A
454 *
455 * Call a hook chain.
456 */
457LRESULT HOOK_CallHooksA( INT id, INT code, WPARAM wParam,
458 LPARAM lParam )
459{
460 HANDLE hook;
461
462 if (!(hook = HOOK_GetHook( id, GetCurrentThreadId() ))) return 0;
463 if (!(hook = HOOK_FindValidHook(hook))) return 0;
464 return HOOK_CallHook( hook, HOOK_WIN32A, code, wParam, lParam );
465}
466
467/***********************************************************************
468 * HOOK_CallHooks32W
469 *
470 * Call a hook chain.
471 */
472LRESULT HOOK_CallHooksW( INT id, INT code, WPARAM wParam,
473 LPARAM lParam )
474{
475 HANDLE hook;
476
477 if (!(hook = HOOK_GetHook( id, GetCurrentThreadId() ))) return 0;
478 if (!(hook = HOOK_FindValidHook(hook))) return 0;
479 return HOOK_CallHook( hook, HOOK_WIN32W, code, wParam,
480 lParam );
481}
482
483
484#if 0
485/***********************************************************************
486 * HOOK_ResetQueueHooks
487 */
488void HOOK_ResetQueueHooks( HQUEUE hQueue )
489{
490 MESSAGEQUEUE *queue;
491
492 if ((queue = (MESSAGEQUEUE *)QUEUE_Lock( hQueue )) != NULL)
493 {
494 HOOKDATA* data;
495 HHOOK hook;
496 int id;
497 for( id = WH_MINHOOK; id <= WH_MAXHOOK; id++ )
498 {
499 hook = queue->hooks[id - WH_MINHOOK];
500 while( hook )
501 {
502 if( (data = (HOOKDATA *)hook) )
503 {
504 data->ownerQueue = hQueue;
505 hook = data->next;
506 } else break;
507 }
508 }
509
510 QUEUE_Unlock( queue );
511 }
512}
513#endif
514
515/***********************************************************************
516 * HOOK_FreeModuleHooks
517 */
518void HOOK_FreeModuleHooks( HMODULE hModule )
519{
520 /* remove all system hooks registered by this module */
521
522 HOOKDATA* hptr;
523 HHOOK hook, next;
524 int id;
525
526 for( id = WH_MINHOOK; id <= WH_MAXHOOK; id++ )
527 {
528 hook = HOOK_systemHooks[id - WH_MINHOOK];
529 while( hook )
530 if( (hptr = (HOOKDATA *)hook) )
531 {
532 next = hptr->next;
533 if( hptr->ownerModule == hModule )
534 {
535 hptr->flags &= HOOK_MAPTYPE;
536 HOOK_RemoveHook(hptr);
537 }
538 hook = next;
539 }
540 else hook = 0;
541 }
542}
543
544/***********************************************************************
545 * HOOK_FreeQueueHooks
546 */
547void HOOK_FreeQueueHooks( DWORD threadId )
548{
549 /* remove all hooks registered by this queue */
550
551 HOOKDATA* hptr = NULL;
552 HHOOK hook, next;
553 int id;
554
555 for( id = WH_MINHOOK; id <= WH_MAXHOOK; id++ )
556 {
557 hook = HOOK_GetHook( id, threadId );
558 while( hook )
559 {
560 next = HOOK_GetNextHook(hook);
561
562 hptr = (HOOKDATA *)hook;
563 if( hptr && hptr->ownerThread == threadId )
564 {
565 hptr->flags &= HOOK_MAPTYPE;
566 HOOK_RemoveHook(hptr);
567 }
568 hook = next;
569 }
570 }
571}
572
573
574/***********************************************************************
575 * SetWindowsHook32A (USER32.525)
576 */
577HHOOK WINAPI SetWindowsHookA( INT id, HOOKPROC proc )
578{
579 return SetWindowsHookExA( id, proc, 0, GetCurrentThreadId() );
580}
581
582/***********************************************************************
583 * SetWindowsHook32W (USER32.528)
584 */
585HHOOK WINAPI SetWindowsHookW( INT id, HOOKPROC proc )
586{
587 return SetWindowsHookExW( id, proc, 0, GetCurrentThreadId() );
588}
589
590/***********************************************************************
591 * SetWindowsHookEx32A (USER32.526)
592 */
593HHOOK WINAPI SetWindowsHookExA( INT id, HOOKPROC proc, HINSTANCE hInst,
594 DWORD dwThreadId )
595{
596 return HOOK_SetHook( id, proc, HOOK_WIN32A, hInst, dwThreadId );
597}
598
599/***********************************************************************
600 * SetWindowsHookEx32W (USER32.527)
601 */
602HHOOK WINAPI SetWindowsHookExW( INT id, HOOKPROC proc, HINSTANCE hInst,
603 DWORD dwThreadId )
604{
605 return HOOK_SetHook( id, proc, HOOK_WIN32W, hInst, dwThreadId );
606}
607
608/***********************************************************************
609 * UnhookWindowsHook32 (USER32.557)
610 */
611BOOL WINAPI UnhookWindowsHook( INT id, HOOKPROC proc )
612{
613 HANDLE hook = HOOK_GetHook( id, GetCurrentThreadId() );
614
615 dprintf(("UnhookWindowsHook: %d %08lx\n", id, (DWORD)proc ));
616
617 while (hook)
618 {
619 HOOKDATA *data = (HOOKDATA *)hook;
620 if (data->proc == proc) break;
621 hook = HOOK_GetNextHook( hook );
622 }
623 if (!hook) return FALSE;
624 return HOOK_RemoveHook( (HOOKDATA *)hook );
625}
626
627
628/***********************************************************************
629 * UnhookWindowHookEx32 (USER32.558)
630 */
631BOOL WINAPI UnhookWindowsHookEx( HHOOK hhook )
632{
633 if (CHECK_MAGIC(hhook) == FALSE)
634 return FALSE;
635
636 return HOOK_RemoveHook( (HOOKDATA *)hhook );
637}
638
639/***********************************************************************
640 * CallNextHookEx32 (USER32.17)
641 *
642 * There aren't ANSI and UNICODE versions of this.
643 */
644LRESULT WINAPI CallNextHookEx( HHOOK hhook, INT code, WPARAM wParam,
645 LPARAM lParam )
646{
647 HANDLE next;
648 INT fromtype; /* figure out Ansi/Unicode */
649 HOOKDATA *oldhook;
650
651 if (CHECK_MAGIC(hhook) == FALSE)
652 return FALSE;
653
654 if (!(next = HOOK_GetNextHook( hhook ))) return 0;
655
656 oldhook = (HOOKDATA *)hhook ;
657 fromtype = oldhook->flags & HOOK_MAPTYPE;
658
659 return HOOK_CallHook( next, fromtype, code, wParam, lParam );
660}
661
662
663/***********************************************************************
664 * CallMsgFilter32A (USER32.15)
665 */
666/*
667 * FIXME: There are ANSI and UNICODE versions of this, plus an unspecified
668 * version, plus USER (the 16bit one) has a CallMsgFilter32 function.
669 */
670BOOL WINAPI CallMsgFilterA( LPMSG msg, INT code )
671{
672 if (GetSysModalWindow()) return FALSE; /* ??? */
673 if (HOOK_CallHooksA( WH_SYSMSGFILTER, code, 0, (LPARAM)msg ))
674 return TRUE;
675 return HOOK_CallHooksA( WH_MSGFILTER, code, 0, (LPARAM)msg );
676}
677
678
679/***********************************************************************
680 * CallMsgFilter32W (USER32.16)
681 */
682BOOL WINAPI CallMsgFilterW( LPMSG msg, INT code )
683{
684 if (GetSysModalWindow()) return FALSE; /* ??? */
685 if (HOOK_CallHooksW( WH_SYSMSGFILTER, code, 0, (LPARAM)msg ))
686 return TRUE;
687 return HOOK_CallHooksW( WH_MSGFILTER, code, 0, (LPARAM)msg );
688}
689
Note: See TracBrowser for help on using the repository browser.