[WIN32K]
[reactos.git] / reactos / subsystems / win32 / win32k / ntuser / timer.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * PURPOSE: Window timers messages
5 * FILE: subsystems/win32/win32k/ntuser/timer.c
6 * PROGRAMER: Gunnar
7 * Thomas Weidenmueller (w3seek@users.sourceforge.net)
8 * REVISION HISTORY: 10/04/2003 Implemented System Timers
9 *
10 */
11
12 /* INCLUDES ******************************************************************/
13
14 #include <win32k.h>
15
16 #define NDEBUG
17 #include <debug.h>
18
19 /* GLOBALS *******************************************************************/
20
21 static PTIMER FirstpTmr = NULL;
22 static LONG TimeLast = 0;
23
24 #define MAX_ELAPSE_TIME 0x7FFFFFFF
25
26 /* Windows 2000 has room for 32768 window-less timers */
27 #define NUM_WINDOW_LESS_TIMERS 32768
28
29 static FAST_MUTEX Mutex;
30 static RTL_BITMAP WindowLessTimersBitMap;
31 static PVOID WindowLessTimersBitMapBuffer;
32 static ULONG HintIndex = 0;
33
34 ERESOURCE TimerLock;
35
36 #define IntLockWindowlessTimerBitmap() \
37 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(&Mutex)
38
39 #define IntUnlockWindowlessTimerBitmap() \
40 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(&Mutex)
41
42 #define TimerEnterExclusive() \
43 { \
44 KeEnterCriticalRegion(); \
45 ExAcquireResourceExclusiveLite(&TimerLock, TRUE); \
46 }
47
48 #define TimerLeave() \
49 { \
50 ExReleaseResourceLite(&TimerLock); \
51 KeLeaveCriticalRegion(); \
52 }
53
54
55 /* FUNCTIONS *****************************************************************/
56 static
57 PTIMER
58 FASTCALL
59 CreateTimer(VOID)
60 {
61 HANDLE Handle;
62 PTIMER Ret = NULL;
63
64 if (!FirstpTmr)
65 {
66 ExInitializeResourceLite(&TimerLock);
67 FirstpTmr = UserCreateObject(gHandleTable, NULL, &Handle, otTimer, sizeof(TIMER));
68 if (FirstpTmr)
69 {
70 FirstpTmr->head.h = Handle;
71 InitializeListHead(&FirstpTmr->ptmrList);
72 }
73 Ret = FirstpTmr;
74 }
75 else
76 {
77 Ret = UserCreateObject(gHandleTable, NULL, &Handle, otTimer, sizeof(TIMER));
78 if (Ret)
79 {
80 Ret->head.h = Handle;
81 InsertTailList(&FirstpTmr->ptmrList, &Ret->ptmrList);
82 }
83 }
84 return Ret;
85 }
86
87 static
88 BOOL
89 FASTCALL
90 RemoveTimer(PTIMER pTmr)
91 {
92 BOOL Ret = FALSE;
93 if (pTmr)
94 {
95 /* Set the flag, it will be removed when ready */
96 RemoveEntryList(&pTmr->ptmrList);
97 if ((pTmr->pWnd == NULL) && (!(pTmr->flags & TMRF_SYSTEM)))
98 {
99 DPRINT("Clearing Bit %d)\n", pTmr->nID);
100 IntLockWindowlessTimerBitmap();
101 RtlClearBit(&WindowLessTimersBitMap, pTmr->nID);
102 IntUnlockWindowlessTimerBitmap();
103 }
104 UserDereferenceObject(pTmr);
105 Ret = UserDeleteObject( UserHMGetHandle(pTmr), otTimer);
106 }
107 if (!Ret) DPRINT1("Warning unable to delete timer\n");
108
109 return Ret;
110 }
111
112 PTIMER
113 FASTCALL
114 FindTimer(PWINDOW_OBJECT Window,
115 UINT_PTR nID,
116 UINT flags,
117 BOOL Distroy)
118 {
119 PLIST_ENTRY pLE;
120 PTIMER pTmr = FirstpTmr, RetTmr = NULL;
121 TimerEnterExclusive();
122 do
123 {
124 if (!pTmr) break;
125
126 if ( pTmr->nID == nID &&
127 pTmr->pWnd == Window &&
128 (pTmr->flags & (TMRF_SYSTEM|TMRF_RIT)) == (flags & (TMRF_SYSTEM|TMRF_RIT)))
129 {
130 if (Distroy)
131 {
132 RemoveTimer(pTmr);
133 }
134 RetTmr = pTmr;
135 break;
136 }
137
138 pLE = pTmr->ptmrList.Flink;
139 pTmr = CONTAINING_RECORD(pLE, TIMER, ptmrList);
140 } while (pTmr != FirstpTmr);
141 TimerLeave();
142
143 return RetTmr;
144 }
145
146 PTIMER
147 FASTCALL
148 FindSystemTimer(PMSG pMsg)
149 {
150 PLIST_ENTRY pLE;
151 PTIMER pTmr = FirstpTmr;
152 TimerEnterExclusive();
153 do
154 {
155 if (!pTmr) break;
156
157 if ( pMsg->lParam == (LPARAM)pTmr->pfn &&
158 (pTmr->flags & TMRF_SYSTEM) )
159 break;
160
161 pLE = pTmr->ptmrList.Flink;
162 pTmr = CONTAINING_RECORD(pLE, TIMER, ptmrList);
163 } while (pTmr != FirstpTmr);
164 TimerLeave();
165
166 return pTmr;
167 }
168
169 BOOL
170 FASTCALL
171 ValidateTimerCallback(PTHREADINFO pti,
172 PWINDOW_OBJECT Window,
173 WPARAM wParam,
174 LPARAM lParam)
175 {
176 PLIST_ENTRY pLE;
177 PTIMER pTmr = FirstpTmr;
178
179 if (!pTmr) return FALSE;
180
181 TimerEnterExclusive();
182 do
183 {
184 if ( (lParam == (LPARAM)pTmr->pfn) &&
185 (pTmr->flags & (TMRF_SYSTEM|TMRF_RIT)) &&
186 (pTmr->pti->ppi == pti->ppi) )
187 break;
188
189 pLE = pTmr->ptmrList.Flink;
190 pTmr = CONTAINING_RECORD(pLE, TIMER, ptmrList);
191 } while (pTmr != FirstpTmr);
192 TimerLeave();
193
194 if (!pTmr) return FALSE;
195
196 return TRUE;
197 }
198
199 UINT_PTR FASTCALL
200 IntSetTimer( PWINDOW_OBJECT Window,
201 UINT_PTR IDEvent,
202 UINT Elapse,
203 TIMERPROC TimerFunc,
204 INT Type)
205 {
206 PTIMER pTmr;
207 UINT Ret= IDEvent;
208 LARGE_INTEGER DueTime;
209 DueTime.QuadPart = (LONGLONG)(-10000000);
210
211 #if 0
212 /* Windows NT/2k/XP behaviour */
213 if (Elapse > MAX_ELAPSE_TIME)
214 {
215 DPRINT("Adjusting uElapse\n");
216 Elapse = 1;
217 }
218 #else
219 /* Windows XP SP2 and Windows Server 2003 behaviour */
220 if (Elapse > MAX_ELAPSE_TIME)
221 {
222 DPRINT("Adjusting uElapse\n");
223 Elapse = MAX_ELAPSE_TIME;
224 }
225 #endif
226
227 /* Windows 2k/XP and Windows Server 2003 SP1 behaviour */
228 if (Elapse < 10)
229 {
230 DPRINT("Adjusting uElapse\n");
231 Elapse = 10;
232 }
233
234 if ((Window) && (IDEvent == 0))
235 IDEvent = 1;
236
237 pTmr = FindTimer(Window, IDEvent, Type, FALSE);
238
239 if ((!pTmr) && (Window == NULL) && (!(Type & TMRF_SYSTEM)))
240 {
241 IntLockWindowlessTimerBitmap();
242
243 IDEvent = RtlFindClearBitsAndSet(&WindowLessTimersBitMap, 1, HintIndex);
244
245 if (IDEvent == (UINT_PTR) -1)
246 {
247 IntUnlockWindowlessTimerBitmap();
248 DPRINT1("Unable to find a free window-less timer id\n");
249 SetLastWin32Error(ERROR_NO_SYSTEM_RESOURCES);
250 return 0;
251 }
252
253 Ret = IDEvent;
254 //HintIndex = IDEvent + 1;
255 IntUnlockWindowlessTimerBitmap();
256 }
257
258 if (!pTmr)
259 {
260 pTmr = CreateTimer();
261 if (!pTmr) return 0;
262
263 if (Window && (Type & TMRF_TIFROMWND))
264 pTmr->pti = Window->pti->pEThread->Tcb.Win32Thread;
265 else
266 {
267 if (Type & TMRF_RIT)
268 pTmr->pti = ptiRawInput;
269 else
270 pTmr->pti = PsGetCurrentThreadWin32Thread();
271 }
272
273 pTmr->pWnd = Window;
274 pTmr->cmsCountdown = Elapse;
275 pTmr->cmsRate = Elapse;
276 pTmr->pfn = TimerFunc;
277 pTmr->nID = IDEvent;
278 pTmr->flags = Type|TMRF_INIT; // Set timer to Init mode.
279 }
280 else
281 {
282 pTmr->cmsCountdown = Elapse;
283 pTmr->cmsRate = Elapse;
284 }
285
286 ASSERT(MasterTimer != NULL);
287 // Start the timer thread!
288 if (pTmr == FirstpTmr)
289 KeSetTimer(MasterTimer, DueTime, NULL);
290
291 return Ret;
292 }
293
294 //
295 // Process win32k system timers.
296 //
297 VOID
298 CALLBACK
299 SystemTimerProc(HWND hwnd,
300 UINT uMsg,
301 UINT_PTR idEvent,
302 DWORD dwTime)
303 {
304 DPRINT( "Timer Running!\n" );
305 }
306
307 VOID
308 FASTCALL
309 StartTheTimers(VOID)
310 {
311 // Need to start gdi syncro timers then start timer with Hang App proc
312 // that calles Idle process so the screen savers will know to run......
313 IntSetTimer(NULL, 0, 1000, SystemTimerProc, TMRF_RIT);
314 }
315
316 UINT_PTR
317 FASTCALL
318 SystemTimerSet( PWINDOW_OBJECT Window,
319 UINT_PTR nIDEvent,
320 UINT uElapse,
321 TIMERPROC lpTimerFunc)
322 {
323 if (Window && Window->pti->pEThread->ThreadsProcess != PsGetCurrentProcess())
324 {
325 SetLastWin32Error(ERROR_ACCESS_DENIED);
326 return 0;
327 }
328 return IntSetTimer( Window, nIDEvent, uElapse, lpTimerFunc, TMRF_SYSTEM);
329 }
330
331 BOOL
332 FASTCALL
333 PostTimerMessages(PWINDOW_OBJECT Window)
334 {
335 PLIST_ENTRY pLE;
336 PUSER_MESSAGE_QUEUE ThreadQueue;
337 MSG Msg;
338 PTHREADINFO pti;
339 BOOL Hit = FALSE;
340 PTIMER pTmr = FirstpTmr;
341
342 if (!pTmr) return FALSE;
343
344 pti = PsGetCurrentThreadWin32Thread();
345 ThreadQueue = pti->MessageQueue;
346
347 TimerEnterExclusive();
348
349 do
350 {
351 if ( (pTmr->flags & TMRF_READY) &&
352 (pTmr->pti == pti) &&
353 ((pTmr->pWnd == Window) || (Window == NULL) ) )
354 {
355 Msg.hwnd = (pTmr->pWnd) ? pTmr->pWnd->hSelf : 0;
356 Msg.message = (pTmr->flags & TMRF_SYSTEM) ? WM_SYSTIMER : WM_TIMER;
357 Msg.wParam = (WPARAM) pTmr->nID;
358 Msg.lParam = (LPARAM) pTmr->pfn;
359
360 MsqPostMessage(ThreadQueue, &Msg, FALSE, QS_TIMER);
361 pTmr->flags &= ~TMRF_READY;
362 ThreadQueue->WakeMask = ~QS_TIMER;
363 Hit = TRUE;
364 }
365
366 pLE = pTmr->ptmrList.Flink;
367 pTmr = CONTAINING_RECORD(pLE, TIMER, ptmrList);
368 } while (pTmr != FirstpTmr);
369
370 TimerLeave();
371
372 return Hit;
373 }
374
375 VOID
376 FASTCALL
377 ProcessTimers(VOID)
378 {
379 LARGE_INTEGER TickCount, DueTime;
380 LONG Time;
381 PLIST_ENTRY pLE;
382 PTIMER pTmr = FirstpTmr;
383 LONG TimerCount = 0;
384
385 if (!pTmr) return;
386
387 TimerEnterExclusive();
388
389 KeQueryTickCount(&TickCount);
390 Time = MsqCalculateMessageTime(&TickCount);
391
392 DueTime.QuadPart = (LONGLONG)(-1000000);
393
394 do
395 {
396 TimerCount++;
397 if (pTmr->flags & TMRF_WAITING)
398 {
399 pLE = pTmr->ptmrList.Flink;
400 pTmr = CONTAINING_RECORD(pLE, TIMER, ptmrList);
401 continue;
402 }
403
404 if (pTmr->flags & TMRF_INIT)
405 {
406 pTmr->flags &= ~TMRF_INIT; // Skip this run.
407 }
408 else
409 {
410 if (pTmr->cmsCountdown < 0)
411 {
412 ASSERT(pTmr->pti);
413 if ((!(pTmr->flags & TMRF_READY)) && (!(pTmr->pti->TIF_flags & TIF_INCLEANUP)))
414 {
415 if (pTmr->flags & TMRF_ONESHOT)
416 pTmr->flags |= TMRF_WAITING;
417
418 if (pTmr->flags & TMRF_RIT)
419 {
420 // Hard coded call here, inside raw input thread.
421 pTmr->pfn(NULL, WM_SYSTIMER, pTmr->nID, (LPARAM)pTmr);
422 }
423 else
424 {
425 pTmr->flags |= TMRF_READY; // Set timer ready to be ran.
426 // Set thread message queue for this timer.
427 if (pTmr->pti->MessageQueue)
428 { // Wakeup thread
429 ASSERT(pTmr->pti->MessageQueue->NewMessages != NULL);
430 KeSetEvent(pTmr->pti->MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
431 }
432 }
433 }
434 pTmr->cmsCountdown = pTmr->cmsRate;
435 }
436 else
437 pTmr->cmsCountdown -= Time - TimeLast;
438 }
439
440 pLE = pTmr->ptmrList.Flink;
441 pTmr = CONTAINING_RECORD(pLE, TIMER, ptmrList);
442 } while (pTmr != FirstpTmr);
443
444 // Restart the timer thread!
445 ASSERT(MasterTimer != NULL);
446 KeSetTimer(MasterTimer, DueTime, NULL);
447
448 TimeLast = Time;
449
450 TimerLeave();
451 DPRINT("TimerCount = %d\n", TimerCount);
452 }
453
454 //
455 //
456 // Old Timer Queueing
457 //
458 //
459 UINT_PTR FASTCALL
460 InternalSetTimer(HWND Wnd, UINT_PTR IDEvent, UINT Elapse, TIMERPROC TimerFunc, BOOL SystemTimer)
461 {
462 PWINDOW_OBJECT Window;
463 UINT_PTR Ret = 0;
464 PTHREADINFO pti;
465 PUSER_MESSAGE_QUEUE MessageQueue;
466
467 DPRINT("IntSetTimer wnd %x id %p elapse %u timerproc %p systemtimer %s\n",
468 Wnd, IDEvent, Elapse, TimerFunc, SystemTimer ? "TRUE" : "FALSE");
469
470 if ((Wnd == NULL) && ! SystemTimer)
471 {
472 DPRINT("Window-less timer\n");
473 /* find a free, window-less timer id */
474 IntLockWindowlessTimerBitmap();
475 IDEvent = RtlFindClearBitsAndSet(&WindowLessTimersBitMap, 1, HintIndex);
476
477 if (IDEvent == (UINT_PTR) -1)
478 {
479 IntUnlockWindowlessTimerBitmap();
480 DPRINT1("Unable to find a free window-less timer id\n");
481 SetLastWin32Error(ERROR_NO_SYSTEM_RESOURCES);
482 return 0;
483 }
484
485 HintIndex = ++IDEvent;
486 IntUnlockWindowlessTimerBitmap();
487 Ret = IDEvent;
488 pti = PsGetCurrentThreadWin32Thread();
489 MessageQueue = pti->MessageQueue;
490 }
491 else
492 {
493 if (!(Window = UserGetWindowObject(Wnd)))
494 {
495 DPRINT1("Invalid window handle\n");
496 return 0;
497 }
498
499 if (Window->pti->pEThread->ThreadsProcess != PsGetCurrentProcess())
500 {
501 DPRINT1("Trying to set timer for window in another process (shatter attack?)\n");
502 SetLastWin32Error(ERROR_ACCESS_DENIED);
503 return 0;
504 }
505
506 Ret = IDEvent;
507 MessageQueue = Window->pti->MessageQueue;
508 }
509
510 #if 0
511
512 /* Windows NT/2k/XP behaviour */
513 if (Elapse > 0x7fffffff)
514 {
515 DPRINT("Adjusting uElapse\n");
516 Elapse = 1;
517 }
518
519 #else
520
521 /* Windows XP SP2 and Windows Server 2003 behaviour */
522 if (Elapse > 0x7fffffff)
523 {
524 DPRINT("Adjusting uElapse\n");
525 Elapse = 0x7fffffff;
526 }
527
528 #endif
529
530 /* Windows 2k/XP and Windows Server 2003 SP1 behaviour */
531 if (Elapse < 10)
532 {
533 DPRINT("Adjusting uElapse\n");
534 Elapse = 10;
535 }
536
537 if (! MsqSetTimer(MessageQueue, Wnd,
538 IDEvent, Elapse, TimerFunc,
539 SystemTimer ? WM_SYSTIMER : WM_TIMER))
540 {
541 DPRINT1("Failed to set timer in message queue\n");
542 SetLastWin32Error(ERROR_NO_SYSTEM_RESOURCES);
543 return 0;
544 }
545
546 if (Ret == 0) ASSERT(FALSE);
547 return Ret;
548 }
549
550 BOOL FASTCALL
551 DestroyTimersForWindow(PTHREADINFO pti, PWINDOW_OBJECT Window)
552 {
553 PLIST_ENTRY pLE;
554 PTIMER pTmr = FirstpTmr;
555 BOOL TimersRemoved = FALSE;
556
557 if ((FirstpTmr == NULL) || (Window == NULL))
558 return FALSE;
559
560 TimerEnterExclusive();
561
562 do
563 {
564 if ((pTmr) && (pTmr->pti == pti) && (pTmr->pWnd == Window))
565 {
566 TimersRemoved = RemoveTimer(pTmr);
567 }
568 pLE = pTmr->ptmrList.Flink;
569 pTmr = CONTAINING_RECORD(pLE, TIMER, ptmrList);
570 } while (pTmr != FirstpTmr);
571
572 TimerLeave();
573
574 return TimersRemoved;
575 }
576
577 BOOL FASTCALL
578 DestroyTimersForThread(PTHREADINFO pti)
579 {
580 PLIST_ENTRY pLE;
581 PTIMER pTmr = FirstpTmr;
582 BOOL TimersRemoved = FALSE;
583
584 if (FirstpTmr == NULL)
585 return FALSE;
586
587 TimerEnterExclusive();
588
589 do
590 {
591 if ((pTmr) && (pTmr->pti == pti))
592 {
593 TimersRemoved = RemoveTimer(pTmr);
594 }
595 pLE = pTmr->ptmrList.Flink;
596 pTmr = CONTAINING_RECORD(pLE, TIMER, ptmrList);
597 } while (pTmr != FirstpTmr);
598
599 TimerLeave();
600
601 return TimersRemoved;
602 }
603
604 BOOL FASTCALL
605 IntKillTimer(PWINDOW_OBJECT Window, UINT_PTR IDEvent, BOOL SystemTimer)
606 {
607 PTIMER pTmr = NULL;
608 DPRINT("IntKillTimer Window %x id %p systemtimer %s\n",
609 Window, IDEvent, SystemTimer ? "TRUE" : "FALSE");
610
611 if ((Window) && (IDEvent == 0))
612 IDEvent = 1;
613
614 pTmr = FindTimer(Window, IDEvent, SystemTimer ? TMRF_SYSTEM : 0, TRUE);
615 return pTmr ? TRUE : FALSE;
616 }
617
618 //
619 //
620 // Old Kill Timer
621 //
622 //
623 BOOL FASTCALL
624 InternalKillTimer(HWND Wnd, UINT_PTR IDEvent, BOOL SystemTimer)
625 {
626 PTHREADINFO pti;
627 PWINDOW_OBJECT Window = NULL;
628
629 DPRINT("IntKillTimer wnd %x id %p systemtimer %s\n",
630 Wnd, IDEvent, SystemTimer ? "TRUE" : "FALSE");
631
632 pti = PsGetCurrentThreadWin32Thread();
633 if (Wnd)
634 {
635 Window = UserGetWindowObject(Wnd);
636
637 if (! MsqKillTimer(pti->MessageQueue, Wnd,
638 IDEvent, SystemTimer ? WM_SYSTIMER : WM_TIMER))
639 {
640 // Give it another chance to find the timer.
641 if (Window && !( MsqKillTimer(Window->pti->MessageQueue, Wnd,
642 IDEvent, SystemTimer ? WM_SYSTIMER : WM_TIMER)))
643 {
644 DPRINT1("Unable to locate timer in message queue for Window.\n");
645 SetLastWin32Error(ERROR_INVALID_PARAMETER);
646 return FALSE;
647 }
648 }
649 }
650
651 /* window-less timer? */
652 if ((Wnd == NULL) && ! SystemTimer)
653 {
654 if (! MsqKillTimer(pti->MessageQueue, Wnd,
655 IDEvent, SystemTimer ? WM_SYSTIMER : WM_TIMER))
656 {
657 DPRINT1("Unable to locate timer in message queue for Window-less timer.\n");
658 SetLastWin32Error(ERROR_INVALID_PARAMETER);
659 return FALSE;
660 }
661
662 /* Release the id */
663 IntLockWindowlessTimerBitmap();
664
665 ASSERT(RtlAreBitsSet(&WindowLessTimersBitMap, IDEvent - 1, 1));
666 RtlClearBits(&WindowLessTimersBitMap, IDEvent - 1, 1);
667
668 HintIndex = IDEvent - 1;
669
670 IntUnlockWindowlessTimerBitmap();
671 }
672
673 return TRUE;
674 }
675
676 NTSTATUS FASTCALL
677 InitTimerImpl(VOID)
678 {
679 ULONG BitmapBytes;
680
681 ExInitializeFastMutex(&Mutex);
682
683 BitmapBytes = ROUND_UP(NUM_WINDOW_LESS_TIMERS, sizeof(ULONG) * 8) / 8;
684 WindowLessTimersBitMapBuffer = ExAllocatePoolWithTag(PagedPool, BitmapBytes, TAG_TIMERBMP);
685 if (WindowLessTimersBitMapBuffer == NULL)
686 {
687 return STATUS_UNSUCCESSFUL;
688 }
689
690 RtlInitializeBitMap(&WindowLessTimersBitMap,
691 WindowLessTimersBitMapBuffer,
692 BitmapBytes * 8);
693
694 /* yes we need this, since ExAllocatePoolWithTag isn't supposed to zero out allocated memory */
695 RtlClearAllBits(&WindowLessTimersBitMap);
696
697 return STATUS_SUCCESS;
698 }
699
700 UINT_PTR
701 APIENTRY
702 NtUserSetTimer
703 (
704 HWND hWnd,
705 UINT_PTR nIDEvent,
706 UINT uElapse,
707 TIMERPROC lpTimerFunc
708 )
709 {
710 PWINDOW_OBJECT Window;
711 DECLARE_RETURN(UINT_PTR);
712
713 DPRINT("Enter NtUserSetTimer\n");
714 UserEnterExclusive();
715 Window = UserGetWindowObject(hWnd);
716 UserLeave();
717
718 RETURN(IntSetTimer(Window, nIDEvent, uElapse, lpTimerFunc, TMRF_TIFROMWND));
719
720 CLEANUP:
721 DPRINT("Leave NtUserSetTimer, ret=%i\n", _ret_);
722
723 END_CLEANUP;
724 }
725
726
727 BOOL
728 APIENTRY
729 NtUserKillTimer
730 (
731 HWND hWnd,
732 UINT_PTR uIDEvent
733 )
734 {
735 PWINDOW_OBJECT Window;
736 DECLARE_RETURN(BOOL);
737
738 DPRINT("Enter NtUserKillTimer\n");
739 UserEnterExclusive();
740 Window = UserGetWindowObject(hWnd);
741 UserLeave();
742
743 RETURN(IntKillTimer(Window, uIDEvent, FALSE));
744
745 CLEANUP:
746 DPRINT("Leave NtUserKillTimer, ret=%i\n", _ret_);
747 END_CLEANUP;
748 }
749
750
751 UINT_PTR
752 APIENTRY
753 NtUserSetSystemTimer(
754 HWND hWnd,
755 UINT_PTR nIDEvent,
756 UINT uElapse,
757 TIMERPROC lpTimerFunc
758 )
759 {
760 DECLARE_RETURN(UINT_PTR);
761
762 DPRINT("Enter NtUserSetSystemTimer\n");
763
764 // This is wrong, lpTimerFunc is NULL!
765 RETURN(IntSetTimer(UserGetWindowObject(hWnd), nIDEvent, uElapse, lpTimerFunc, TMRF_SYSTEM));
766
767 CLEANUP:
768 DPRINT("Leave NtUserSetSystemTimer, ret=%i\n", _ret_);
769 END_CLEANUP;
770 }
771
772
773 /* EOF */