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