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