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