1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * PURPOSE: Window event handlers
5 * PROGRAMER: James Tabor <james.tabor@reactos.org>
6 */
7
8 #include <win32k.h>
9 DBG_DEFAULT_CHANNEL(UserEvent);
10
11 typedef struct _EVENTPACK
12 {
13 PEVENTHOOK pEH;
14 LONG idObject;
15 LONG idChild;
16 LONG idThread;
17 } EVENTPACK, *PEVENTPACK;
18
19 static PEVENTTABLE GlobalEvents = NULL;
20
21 /* PRIVATE FUNCTIONS *********************************************************/
22
23 static
24 DWORD
25 FASTCALL
GetMaskFromEvent(DWORD Event)26 GetMaskFromEvent(DWORD Event)
27 {
28 DWORD Ret = 0;
29
30 if ( Event > EVENT_OBJECT_STATECHANGE )
31 {
32 if ( Event == EVENT_OBJECT_LOCATIONCHANGE ) return SRV_EVENT_LOCATIONCHANGE;
33 if ( Event == EVENT_OBJECT_NAMECHANGE ) return SRV_EVENT_NAMECHANGE;
34 if ( Event == EVENT_OBJECT_VALUECHANGE ) return SRV_EVENT_VALUECHANGE;
35 return SRV_EVENT_CREATE;
36 }
37
38 if ( Event == EVENT_OBJECT_STATECHANGE ) return SRV_EVENT_STATECHANGE;
39
40 Ret = SRV_EVENT_RUNNING;
41
42 if ( Event < EVENT_SYSTEM_MENUSTART ) return SRV_EVENT_CREATE;
43
44 if ( Event <= EVENT_SYSTEM_MENUPOPUPEND )
45 {
46 Ret = SRV_EVENT_MENU;
47 }
48 else
49 {
50 if ( Event <= EVENT_CONSOLE_CARET-1 ) return SRV_EVENT_CREATE;
51 if ( Event <= EVENT_CONSOLE_END_APPLICATION ) return SRV_EVENT_END_APPLICATION;
52 if ( Event != EVENT_OBJECT_FOCUS ) return SRV_EVENT_CREATE;
53 }
54 return Ret;
55 }
56
57 static
58 VOID
59 FASTCALL
IntSetSrvEventMask(UINT EventMin,UINT EventMax)60 IntSetSrvEventMask( UINT EventMin, UINT EventMax)
61 {
62 UINT event;
63 TRACE("SetSrvEventMask 1\n");
64 for ( event = EventMin; event <= EventMax; event++)
65 {
66 if ((event >= EVENT_SYSTEM_SOUND && event <= EVENT_SYSTEM_MINIMIZEEND) ||
67 (event >= EVENT_CONSOLE_CARET && event <= EVENT_CONSOLE_END_APPLICATION) ||
68 (event >= EVENT_OBJECT_CREATE && event <= EVENT_OBJECT_ACCELERATORCHANGE))
69 {
70 gpsi->dwInstalledEventHooks |= GetMaskFromEvent(event);
71 }
72 if (event > EVENT_SYSTEM_MINIMIZEEND && event < EVENT_CONSOLE_CARET)
73 {
74 event = EVENT_CONSOLE_CARET-1;
75 gpsi->dwInstalledEventHooks |= GetMaskFromEvent(event);
76 }
77 if (event > EVENT_CONSOLE_END_APPLICATION && event < EVENT_OBJECT_CREATE )
78 {
79 event = EVENT_OBJECT_CREATE-1;
80 gpsi->dwInstalledEventHooks |= GetMaskFromEvent(event);
81 }
82 if (event > EVENT_OBJECT_ACCELERATORCHANGE && event < EVENT_MAX)
83 {
84 event = EVENT_MAX-1;
85 gpsi->dwInstalledEventHooks |= GetMaskFromEvent(event);
86 break;
87 }
88 }
89 if (!gpsi->dwInstalledEventHooks)
90 gpsi->dwInstalledEventHooks |= SRV_EVENT_RUNNING; // Set something.
91 TRACE("SetSrvEventMask 2 : %x\n", gpsi->dwInstalledEventHooks);
92 }
93
94 static
95 LRESULT
96 FASTCALL
IntCallLowLevelEvent(PEVENTHOOK pEH,DWORD event,HWND hwnd,LONG idObject,LONG idChild,LONG idThread)97 IntCallLowLevelEvent( PEVENTHOOK pEH,
98 DWORD event,
99 HWND hwnd,
100 LONG idObject,
101 LONG idChild,
102 LONG idThread)
103 {
104 PEVENTPACK pEP;
105 MSG Msg;
106
107 pEP = ExAllocatePoolWithTag(NonPagedPool, sizeof(EVENTPACK), TAG_HOOK);
108 if (!pEP) return 0;
109
110 pEP->pEH = pEH;
111 pEP->idObject = idObject;
112 pEP->idChild = idChild;
113 pEP->idThread = idThread;
114
115 Msg.message = event;
116 Msg.hwnd = hwnd;
117 Msg.wParam = 0;
118 Msg.lParam = POSTEVENT_NWE;
119 Msg.time = 0;
120
121 MsqPostMessage(pEH->head.pti, &Msg, FALSE, QS_EVENT, POSTEVENT_NWE, (LONG_PTR)pEP);
122 return 0;
123 }
124
125 BOOLEAN
IntRemoveEvent(PVOID Object)126 IntRemoveEvent(PVOID Object)
127 {
128 PEVENTHOOK pEH = Object;
129 if (pEH)
130 {
131 TRACE("IntRemoveEvent pEH %p\n", pEH);
132 KeEnterCriticalRegion();
133 RemoveEntryList(&pEH->Chain);
134 GlobalEvents->Counts--;
135 if (!GlobalEvents->Counts) gpsi->dwInstalledEventHooks = 0;
136 UserDeleteObject(UserHMGetHandle(pEH), TYPE_WINEVENTHOOK);
137 KeLeaveCriticalRegion();
138 return TRUE;
139 }
140 return FALSE;
141 }
142
143 /* FUNCTIONS *****************************************************************/
144
145 //
146 // Dispatch MsgQueue Event Call processor!
147 //
148 LRESULT
149 APIENTRY
co_EVENT_CallEvents(DWORD event,HWND hwnd,UINT_PTR idObject,LONG_PTR idChild)150 co_EVENT_CallEvents( DWORD event,
151 HWND hwnd,
152 UINT_PTR idObject,
153 LONG_PTR idChild)
154 {
155 PEVENTHOOK pEH;
156 LRESULT Result;
157 PEVENTPACK pEP = (PEVENTPACK)idChild;
158
159 pEH = pEP->pEH;
160 TRACE("Dispatch Event 0x%lx, idObject %uI hwnd %p\n", event, idObject, hwnd);
161 Result = co_IntCallEventProc( UserHMGetHandle(pEH),
162 event,
163 hwnd,
164 pEP->idObject,
165 pEP->idChild,
166 pEP->idThread,
167 EngGetTickCount32(),
168 pEH->Proc,
169 pEH->ihmod,
170 pEH->offPfn);
171
172 ExFreePoolWithTag(pEP, TAG_HOOK);
173 return Result;
174 }
175
176 VOID
177 FASTCALL
IntNotifyWinEvent(DWORD Event,PWND pWnd,LONG idObject,LONG idChild,DWORD flags)178 IntNotifyWinEvent(
179 DWORD Event,
180 PWND pWnd,
181 LONG idObject,
182 LONG idChild,
183 DWORD flags)
184 {
185 PEVENTHOOK pEH;
186 PLIST_ENTRY ListEntry;
187 PTHREADINFO pti, ptiCurrent;
188 USER_REFERENCE_ENTRY Ref;
189
190 TRACE("IntNotifyWinEvent GlobalEvents = %p pWnd %p\n", GlobalEvents, pWnd);
191
192 if (!GlobalEvents || !GlobalEvents->Counts) return;
193
194 if (pWnd && pWnd->state & WNDS_DESTROYED) return;
195
196 ptiCurrent = PsGetCurrentThreadWin32Thread();
197
198 if (pWnd && flags & WEF_SETBYWNDPTI)
199 pti = pWnd->head.pti;
200 else
201 pti = ptiCurrent;
202
203 ListEntry = GlobalEvents->Events.Flink;
204 ASSERT(ListEntry != &GlobalEvents->Events);
205 while (ListEntry != &GlobalEvents->Events)
206 {
207 pEH = CONTAINING_RECORD(ListEntry, EVENTHOOK, Chain);
208 ListEntry = ListEntry->Flink;
209
210 // Must be inside the event window.
211 if ( Event >= pEH->eventMin && Event <= pEH->eventMax )
212 {
213 // if all process || all thread || other thread same process
214 // if ^skip own thread && ((Pid && CPid == Pid && ^skip own process) || all process)
215 if (!( (pEH->idProcess && pEH->idProcess != PtrToUint(pti->pEThread->Cid.UniqueProcess)) ||
216 (pEH->Flags & WINEVENT_SKIPOWNPROCESS && pEH->head.pti->ppi == pti->ppi) ||
217 (pEH->idThread && pEH->idThread != PtrToUint(pti->pEThread->Cid.UniqueThread)) ||
218 (pEH->Flags & WINEVENT_SKIPOWNTHREAD && pEH->head.pti == pti) ||
219 pEH->head.pti->rpdesk != ptiCurrent->rpdesk ) ) // Same as hooks.
220 {
221 UserRefObjectCo(pEH, &Ref);
222 if (pEH->Flags & WINEVENT_INCONTEXT)
223 {
224 TRACE("In Event 0x%x, idObject %d hwnd %p\n", Event, idObject, pWnd ? UserHMGetHandle(pWnd) : NULL);
225 co_IntCallEventProc( UserHMGetHandle(pEH),
226 Event,
227 pWnd ? UserHMGetHandle(pWnd) : NULL,
228 idObject,
229 idChild,
230 PtrToUint(NtCurrentTeb()->ClientId.UniqueThread),
231 EngGetTickCount32(),
232 pEH->Proc,
233 pEH->ihmod,
234 pEH->offPfn);
235 }
236 else
237 {
238 TRACE("Out Event 0x%x, idObject %d hwnd %p\n", Event, idObject, pWnd ? UserHMGetHandle(pWnd) : NULL);
239 IntCallLowLevelEvent( pEH,
240 Event,
241 pWnd ? UserHMGetHandle(pWnd) : NULL,
242 idObject,
243 idChild,
244 PtrToUint(NtCurrentTeb()->ClientId.UniqueThread));
245 }
246 UserDerefObjectCo(pEH);
247 }
248 }
249 }
250 }
251
252 VOID
253 APIENTRY
NtUserNotifyWinEvent(DWORD Event,HWND hWnd,LONG idObject,LONG idChild)254 NtUserNotifyWinEvent(
255 DWORD Event,
256 HWND hWnd,
257 LONG idObject,
258 LONG idChild)
259 {
260 PWND Window = NULL;
261 USER_REFERENCE_ENTRY Ref;
262 UserEnterExclusive();
263
264 /* Validate input */
265 if (hWnd && (hWnd != INVALID_HANDLE_VALUE))
266 {
267 Window = UserGetWindowObject(hWnd);
268 if (!Window)
269 {
270 UserLeave();
271 return;
272 }
273 }
274
275 if (gpsi->dwInstalledEventHooks & GetMaskFromEvent(Event))
276 {
277 if (Window) UserRefObjectCo(Window, &Ref);
278 IntNotifyWinEvent( Event, Window, idObject, idChild, WEF_SETBYWNDPTI);
279 if (Window) UserDerefObjectCo(Window);
280 }
281 UserLeave();
282 }
283
284 HWINEVENTHOOK
285 APIENTRY
NtUserSetWinEventHook(UINT eventMin,UINT eventMax,HMODULE hmodWinEventProc,PUNICODE_STRING puString,WINEVENTPROC lpfnWinEventProc,DWORD idProcess,DWORD idThread,UINT dwflags)286 NtUserSetWinEventHook(
287 UINT eventMin,
288 UINT eventMax,
289 HMODULE hmodWinEventProc,
290 PUNICODE_STRING puString,
291 WINEVENTPROC lpfnWinEventProc,
292 DWORD idProcess,
293 DWORD idThread,
294 UINT dwflags)
295 {
296 PEVENTHOOK pEH;
297 HWINEVENTHOOK Ret = NULL;
298 NTSTATUS Status;
299 HANDLE Handle;
300 PTHREADINFO pti;
301
302 TRACE("NtUserSetWinEventHook hmod %p, pfn %p\n", hmodWinEventProc, lpfnWinEventProc);
303
304 UserEnterExclusive();
305
306 if ( !GlobalEvents )
307 {
308 GlobalEvents = ExAllocatePoolWithTag(PagedPool, sizeof(EVENTTABLE), TAG_HOOK);
309 if (GlobalEvents == NULL)
310 {
311 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
312 goto SetEventExit;
313 }
314 GlobalEvents->Counts = 0;
315 InitializeListHead(&GlobalEvents->Events);
316 }
317
318 if (eventMin > eventMax)
319 {
320 EngSetLastError(ERROR_INVALID_HOOK_FILTER);
321 goto SetEventExit;
322 }
323
324 if (!lpfnWinEventProc)
325 {
326 EngSetLastError(ERROR_INVALID_FILTER_PROC);
327 goto SetEventExit;
328 }
329
330 if (dwflags & WINEVENT_INCONTEXT)
331 {
332 if (!hmodWinEventProc)
333 {
334 ERR("Hook needs a module\n");
335 EngSetLastError(ERROR_HOOK_NEEDS_HMOD);
336 goto SetEventExit;
337 }
338 if (puString == NULL)
339 {
340 ERR("Dll not found\n");
341 EngSetLastError(ERROR_DLL_NOT_FOUND);
342 goto SetEventExit;
343 }
344 }
345 else
346 {
347 TRACE("Out of Context\n");
348 hmodWinEventProc = 0;
349 }
350
351 if (idThread)
352 {
353 PETHREAD Thread;
354 Status = PsLookupThreadByThreadId(UlongToHandle(idThread), &Thread);
355 if (!NT_SUCCESS(Status))
356 {
357 EngSetLastError(ERROR_INVALID_THREAD_ID);
358 goto SetEventExit;
359 }
360 pti = PsGetThreadWin32Thread(Thread);
361 ObDereferenceObject(Thread);
362 }
363 else
364 {
365 pti = PsGetCurrentThreadWin32Thread();
366 }
367 // Creator, pti is set here.
368 pEH = UserCreateObject(gHandleTable, NULL, pti, &Handle, TYPE_WINEVENTHOOK, sizeof(EVENTHOOK));
369 if (pEH)
370 {
371 InsertTailList(&GlobalEvents->Events, &pEH->Chain);
372 GlobalEvents->Counts++;
373
374 UserHMSetHandle(pEH, Handle);
375 pEH->eventMin = eventMin;
376 pEH->eventMax = eventMax;
377 pEH->idProcess = idProcess; // These are cmp'ed
378 pEH->idThread = idThread; // "
379 pEH->Flags = dwflags;
380 /*
381 If WINEVENT_INCONTEXT, set offset from hmod and proc. Save ihmod from
382 the atom index table where the hmod data is saved to be recalled later
383 if fSync set by WINEVENT_INCONTEXT.
384 If WINEVENT_OUTOFCONTEXT just use proc..
385 Do this instead....
386 */
387 if (hmodWinEventProc != NULL)
388 {
389 pEH->offPfn = (ULONG_PTR)((char *)lpfnWinEventProc - (char *)hmodWinEventProc);
390 pEH->ihmod = (INT_PTR)hmodWinEventProc;
391 pEH->Proc = lpfnWinEventProc;
392 }
393 else
394 {
395 pEH->Proc = lpfnWinEventProc;
396 pEH->offPfn = 0;
397 pEH->ihmod = (INT_PTR)hmodWinEventProc;
398 }
399
400 UserDereferenceObject(pEH);
401
402 Ret = Handle;
403 IntSetSrvEventMask( eventMin, eventMax);
404 }
405
406 SetEventExit:
407 UserLeave();
408 return Ret;
409 }
410
411 BOOL
412 APIENTRY
NtUserUnhookWinEvent(HWINEVENTHOOK hWinEventHook)413 NtUserUnhookWinEvent(
414 HWINEVENTHOOK hWinEventHook)
415 {
416 PEVENTHOOK pEH;
417 BOOL Ret = FALSE;
418
419 UserEnterExclusive();
420
421 pEH = (PEVENTHOOK)UserGetObject(gHandleTable, hWinEventHook, TYPE_WINEVENTHOOK);
422 if (pEH)
423 {
424 Ret = IntRemoveEvent(pEH);
425 }
426
427 UserLeave();
428 return Ret;
429 }
430
431 /* EOF */
432