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