xref: /reactos/win32ss/user/ntuser/event.c (revision 4e5e72fa)
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
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
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
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
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
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
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
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
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
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