Release 961023
[wine] / windows / hook.c
1 /*
2  * Windows hook functions
3  *
4  * Copyright 1994, 1995 Alexandre Julliard
5  *
6  * Based on investigations by Alex Korobka
7  */
8
9 /*
10  * Warning!
11  * A HHOOK is a 32-bit handle for compatibility with Windows 3.0 where it was
12  * a pointer to the next function. Now it is in fact composed of a USER heap
13  * handle in the low 16 bits and of a HOOK_MAGIC value in the high 16 bits.
14  */
15
16 #define NO_TRANSITION_TYPES  /* This file is Win32-clean */
17 #include "hook.h"
18 #include "queue.h"
19 #include "user.h"
20 #include "stddebug.h"
21 #include "debug.h"
22
23 #pragma pack(1)
24
25   /* Hook data (pointed to by a HHOOK) */
26 typedef struct
27 {
28     HANDLE16   next;               /* 00 Next hook in chain */
29     HOOKPROC16 proc WINE_PACKED;   /* 02 Hook procedure */
30     INT16      id;                 /* 06 Hook id (WH_xxx) */
31     HQUEUE16   ownerQueue;         /* 08 Owner queue (0 for system hook) */
32     HMODULE16  ownerModule;        /* 0a Owner module */
33     WORD       inHookProc;         /* 0c TRUE if in this->proc */
34 } HOOKDATA;
35
36 #pragma pack(4)
37
38 #define HOOK_MAGIC  ((int)'H' | (int)'K' << 8)  /* 'HK' */
39
40   /* This should probably reside in USER heap */
41 static HANDLE16 HOOK_systemHooks[WH_NB_HOOKS] = { 0, };
42
43
44 /***********************************************************************
45  *           HOOK_GetNextHook
46  *
47  * Get the next hook of a given hook.
48  */
49 static HANDLE16 HOOK_GetNextHook( HANDLE16 hook )
50 {
51     HOOKDATA *data = (HOOKDATA *)USER_HEAP_LIN_ADDR( hook );
52     if (!data || !hook) return 0;
53     if (data->next) return data->next;
54     if (!data->ownerQueue) return 0;  /* Already system hook */
55     /* Now start enumerating the system hooks */
56     return HOOK_systemHooks[data->id - WH_MINHOOK];
57 }
58
59
60 /***********************************************************************
61  *           HOOK_GetHook
62  *
63  * Get the first hook for a given type.
64  */
65 HANDLE16 HOOK_GetHook( INT16 id , HQUEUE16 hQueue )
66 {
67     MESSAGEQUEUE *queue;
68     HANDLE16 hook = 0;
69
70     if ((queue = (MESSAGEQUEUE *)GlobalLock16( hQueue )) != NULL)
71         hook = queue->hooks[id - WH_MINHOOK];
72     if (!hook) hook = HOOK_systemHooks[id - WH_MINHOOK];
73     return hook;
74 }
75
76
77 /***********************************************************************
78  *           HOOK_GetProc16
79  */
80 HOOKPROC16 HOOK_GetProc16( HHOOK hhook )
81 {
82     HOOKDATA *data;
83     if (HIWORD(hhook) != HOOK_MAGIC) return NULL;
84     if (!(data = (HOOKDATA *)USER_HEAP_LIN_ADDR( LOWORD(hhook) ))) return NULL;
85     return data->proc;
86 }
87
88
89 /***********************************************************************
90  *           HOOK_SetHook
91  *
92  * Install a given hook.
93  */
94 static HANDLE16 HOOK_SetHook( INT16 id, HOOKPROC16 proc, HINSTANCE16 hInst,
95                               HTASK16 hTask )
96 {
97     HOOKDATA *data;
98     HANDLE16 handle;
99     HQUEUE16 hQueue = 0;
100
101     if ((id < WH_MINHOOK) || (id > WH_MAXHOOK)) return 0;
102     if (!(hInst = GetExePtr( hInst ))) return 0;
103
104     dprintf_hook( stddeb, "Setting hook %d: %08x %04x %04x\n",
105                   id, (UINT32)proc, hInst, hTask );
106
107     if (hTask)  /* Task-specific hook */
108     {
109         if ((id == WH_JOURNALRECORD) || (id == WH_JOURNALPLAYBACK) ||
110             (id == WH_SYSMSGFILTER)) return 0;  /* System-only hooks */
111         if (!(hQueue = GetTaskQueue( hTask ))) return 0;
112     }
113
114     if (id == WH_DEBUG)
115     {
116         fprintf( stdnimp,"WH_DEBUG is broken in 16-bit Windows.\n");
117         return 0;
118     }
119     else if (id == WH_CBT || id == WH_SHELL)
120     {
121         fprintf( stdnimp, "Half-implemented hook set: (%s,%08lx,%04x,%04x)!\n",
122                  (id==WH_CBT)?"WH_CBT":"WH_SHELL", (DWORD)proc, hInst, hTask );
123     }
124
125     if (id == WH_JOURNALPLAYBACK) EnableHardwareInput(FALSE);
126      
127     /* Create the hook structure */
128
129     if (!(handle = USER_HEAP_ALLOC( sizeof(HOOKDATA) ))) return 0;
130     data = (HOOKDATA *) USER_HEAP_LIN_ADDR( handle );
131     data->proc        = proc;
132     data->id          = id;
133     data->ownerQueue  = hQueue;
134     data->ownerModule = hInst;
135     data->inHookProc  = 0;
136     dprintf_hook( stddeb, "Setting hook %d: ret=%04x\n", id, handle );
137
138     /* Insert it in the correct linked list */
139
140     if (hQueue)
141     {
142         MESSAGEQUEUE *queue = (MESSAGEQUEUE *)GlobalLock16( hQueue );
143         data->next = queue->hooks[id - WH_MINHOOK];
144         queue->hooks[id - WH_MINHOOK] = handle;
145     }
146     else
147     {
148         data->next = HOOK_systemHooks[id - WH_MINHOOK];
149         HOOK_systemHooks[id - WH_MINHOOK] = handle;
150     }
151     return handle;
152 }
153
154
155 /***********************************************************************
156  *           HOOK_RemoveHook
157  *
158  * Remove a hook from the list.
159  */
160 static BOOL32 HOOK_RemoveHook( HANDLE16 hook )
161 {
162     HOOKDATA *data;
163     HANDLE16 *prevHook;
164
165     dprintf_hook( stddeb, "Removing hook %04x\n", hook );
166
167     if (!(data = (HOOKDATA *)USER_HEAP_LIN_ADDR(hook))) return FALSE;
168     if (data->inHookProc)
169     {
170         /* Mark it for deletion later on */
171         dprintf_hook( stddeb, "Hook still running, deletion delayed\n" );
172         data->proc = (HOOKPROC16)0;
173         return TRUE;
174     }
175
176     if (data->id == WH_JOURNALPLAYBACK) EnableHardwareInput(TRUE);
177      
178     /* Remove it from the linked list */
179
180     if (data->ownerQueue)
181     {
182         MESSAGEQUEUE *queue = (MESSAGEQUEUE *)GlobalLock16( data->ownerQueue );
183         if (!queue) return FALSE;
184         prevHook = &queue->hooks[data->id - WH_MINHOOK];
185     }
186     else prevHook = &HOOK_systemHooks[data->id - WH_MINHOOK];
187
188     while (*prevHook && *prevHook != hook)
189         prevHook = &((HOOKDATA *)USER_HEAP_LIN_ADDR(*prevHook))->next;
190
191      if (!*prevHook) return FALSE;
192     *prevHook = data->next;
193     USER_HEAP_FREE( hook );
194     return TRUE;
195 }
196
197
198 /***********************************************************************
199  *           HOOK_CallHook
200  *
201  * Call a hook procedure.
202  */
203 static LRESULT HOOK_CallHook( HANDLE16 hook, INT16 code,
204                               WPARAM16 wParam, LPARAM lParam )
205 {
206     HOOKDATA *data;
207     MESSAGEQUEUE *queue;
208     HANDLE16 prevHook;
209     LRESULT ret;
210
211     /* Find the first hook with a valid proc */
212
213     for (;;)
214     {
215         if (!hook) return 0;
216         if (!(data = (HOOKDATA *)USER_HEAP_LIN_ADDR(hook))) return 0;
217         if (data->proc) break;
218         hook = data->next;
219     }
220
221     /* Now call it */
222
223     if (!(queue = (MESSAGEQUEUE *)GlobalLock16( GetTaskQueue(0) ))) return 0;
224     prevHook = queue->hCurHook;
225     queue->hCurHook = hook;
226     data->inHookProc = 1;
227
228     dprintf_hook( stddeb, "Calling hook %04x: proc=%p %d %04lx %08lx\n",
229                   hook, data->proc, code, (DWORD)wParam, lParam );
230     ret = data->proc( code, wParam, lParam );
231     dprintf_hook( stddeb, "Ret hook %04x = %08lx\n", hook, ret );
232
233     data->inHookProc = 0;
234     queue->hCurHook = prevHook;
235     if (!data->proc) HOOK_RemoveHook( hook );
236     return ret;
237 }
238
239
240 /***********************************************************************
241  *           HOOK_CallHooks
242  *
243  * Call a hook chain.
244  */
245 LRESULT HOOK_CallHooks( INT16 id, INT16 code, WPARAM16 wParam, LPARAM lParam )
246 {
247     HANDLE16 hook = HOOK_GetHook( id , GetTaskQueue(0) );
248     if (!hook) return 0;
249     return HOOK_CallHook( hook, code, wParam, lParam );
250 }
251
252
253 /***********************************************************************
254  *           HOOK_FreeModuleHooks
255  */
256 void HOOK_FreeModuleHooks( HMODULE16 hModule )
257 {
258  /* remove all system hooks registered by this module */
259
260   HOOKDATA*     hptr;
261   HHOOK         hook, next;
262   int           id;
263
264   for( id = WH_MINHOOK; id <= WH_MAXHOOK; id++ )
265     {
266        hook = HOOK_systemHooks[id - WH_MINHOOK];
267        while( hook )
268           if( (hptr = (HOOKDATA *)USER_HEAP_LIN_ADDR(hook)) )
269             {
270               next = hptr->next;
271               if( hptr->ownerModule == hModule )
272                 {
273                   hptr->inHookProc = 0;
274                   HOOK_RemoveHook(hook);
275                 }
276               hook = next;
277             }
278           else hook = 0;
279     }
280 }
281
282 /***********************************************************************
283  *           HOOK_FreeQueueHooks
284  */
285 void HOOK_FreeQueueHooks( HQUEUE16 hQueue )
286 {
287   /* remove all hooks registered by this queue */
288
289   HOOKDATA*     hptr = NULL;
290   HHOOK         hook, next;
291   int           id;
292
293   for( id = WH_MINHOOK; id <= WH_MAXHOOK; id++ )
294     {
295        hook = HOOK_GetHook( id, hQueue );
296        while( hook )
297         {
298           next = HOOK_GetNextHook(hook);
299
300           hptr = (HOOKDATA *)USER_HEAP_LIN_ADDR(hook);
301           if( hptr && hptr->ownerQueue == hQueue )
302             {
303               hptr->inHookProc = 0;
304               HOOK_RemoveHook(hook);
305             }
306           hook = next;
307         }
308     }
309 }
310
311
312 /***********************************************************************
313  *           SetWindowsHook16   (USER.121)
314  */
315 FARPROC16 SetWindowsHook16( INT16 id, HOOKPROC16 proc )
316 {
317     HTASK16     hTask = (id == WH_MSGFILTER) ? GetCurrentTask() : 0;
318     HANDLE16    handle = HOOK_SetHook( id, proc, 0, hTask );
319
320     return (handle) ? (FARPROC16)MAKELONG( handle, HOOK_MAGIC ) : NULL;
321 }
322
323
324 /***********************************************************************
325  *           UnhookWindowsHook16   (USER.234)
326  */
327 BOOL16 UnhookWindowsHook16( INT16 id, HOOKPROC16 proc )
328 {
329     HANDLE16 hook = HOOK_GetHook( id , 0 );
330
331     dprintf_hook( stddeb, "UnhookWindowsHook: %d %08lx\n", id, (DWORD)proc );
332
333     while (hook)
334     {
335         HOOKDATA *data = (HOOKDATA *)USER_HEAP_LIN_ADDR(hook);
336         if (data->proc == proc) break;
337         hook = HOOK_GetNextHook( hook );
338     }
339     if (!hook) return FALSE;
340     return HOOK_RemoveHook( hook );
341 }
342
343
344 /***********************************************************************
345  *           DefHookProc   (USER.235)
346  */
347 LRESULT DefHookProc( INT16 code, WPARAM16 wParam, LPARAM lParam, HHOOK *hhook )
348 {
349     /* Note: the *hhook parameter is never used, since we rely on the
350      * current hook value from the task queue to find the next hook. */
351     MESSAGEQUEUE *queue;
352     HANDLE16 next;
353
354     if (!(queue = (MESSAGEQUEUE *)GlobalLock16( GetTaskQueue(0) ))) return 0;
355     if (!(next = HOOK_GetNextHook( queue->hCurHook ))) return 0;
356     return HOOK_CallHook( next, code, wParam, lParam );
357 }
358
359
360 /***********************************************************************
361  *           CallMsgFilter   (USER.123)
362  */
363 BOOL16 CallMsgFilter( SEGPTR msg, INT16 code )
364 {
365     if (GetSysModalWindow16()) return FALSE;
366     if (HOOK_CallHooks( WH_SYSMSGFILTER, code, 0, (LPARAM)msg )) return TRUE;
367     return HOOK_CallHooks( WH_MSGFILTER, code, 0, (LPARAM)msg );
368 }
369
370
371 /***********************************************************************
372  *           SetWindowsHookEx16   (USER.291)
373  */
374 HHOOK SetWindowsHookEx16( INT16 id, HOOKPROC16 proc, HINSTANCE16 hInst,
375                           HTASK16 hTask )
376 {
377     HANDLE16 handle = HOOK_SetHook( id, proc, hInst, hTask );
378     return (handle) ? MAKELONG( handle, HOOK_MAGIC ) : NULL;
379 }
380
381
382 /***********************************************************************
383  *           UnhookWindowHookEx16   (USER.292)
384  */
385 BOOL16 UnhookWindowsHookEx16( HHOOK hhook )
386 {
387     if (HIWORD(hhook) != HOOK_MAGIC) return FALSE;  /* Not a new format hook */
388     return HOOK_RemoveHook( LOWORD(hhook) );
389 }
390
391
392 /***********************************************************************
393  *           CallNextHookEx   (USER.293)
394  */
395 LRESULT CallNextHookEx(HHOOK hhook, INT16 code, WPARAM16 wParam, LPARAM lParam)
396 {
397     HANDLE16 next;
398     if (HIWORD(hhook) != HOOK_MAGIC) return 0;  /* Not a new format hook */
399     if (!(next = HOOK_GetNextHook( LOWORD(hhook) ))) return 0;
400     return HOOK_CallHook( next, code, wParam, lParam );
401 }