/* * KernelEx * Copyright (C) 2009-2010, Tihiy * * This file is part of KernelEx source code. * * KernelEx is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation; version 2 of the License. * * KernelEx is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include #include #include "shlord.h" /* * The following functions are implemented: * QueueUserWorkItem (with SHLWAPI.SHQueueUserWorkItem) * RegisterWaitForSingleObject * RegisterWaitForSingleObjectEx * UnregisterWait * UnregisterWaitEx * CreateTimerQueue * CreateTimerQueueTimer * DeleteTimerQueue * DeleteTimerQueueEx * DeleteTimerQueueTimer * all functions could be implemented with shlwapi, * but they don't support async de-registration and most flags. * Wine code was completely purged outta here - it's no good. */ /* RegisterWait/TimerQueues are implemented using waitable timers, * unlike wine/shlwapi/whatever implementations exist. */ #define TPS_EXECUTEIO 0x00000001 #define TPS_LONGEXECTIME 0x00000008 typedef BOOL (WINAPI* SHQueueUserWorkItem_API) (LPTHREAD_START_ROUTINE pfnCallback, LPVOID pContext, LONG lPriority, PDWORD dwTag, PDWORD * pdwId, LPCSTR pszModule, DWORD dwFlags); static SHQueueUserWorkItem_API SHQueueUserWorkItem; /* MAKE_EXPORT QueueUserWorkItem_new=QueueUserWorkItem */ BOOL WINAPI QueueUserWorkItem_new( LPTHREAD_START_ROUTINE Function, PVOID Context, ULONG Flags) { if (!SHQueueUserWorkItem) SHQueueUserWorkItem = (SHQueueUserWorkItem_API)GetShlwapiProc(260); DWORD dwFlags = 0; if (Flags & WT_EXECUTEINIOTHREAD) dwFlags |= TPS_EXECUTEIO; if (Flags & WT_EXECUTELONGFUNCTION) dwFlags |= TPS_LONGEXECTIME; return SHQueueUserWorkItem( Function, Context, 0, NULL, NULL, NULL, dwFlags ); } //256h should be enough for everyone #define MAX_WAIT_ITEMS (MAXIMUM_WAIT_OBJECTS / 2) #define MAX_WAIT_THREADS 8 #define WAIT_STATE_ACTIVE 0 //item is in wait thread #define WAIT_STATE_UNREGISTERED 1 //item is unregistered and queued to be removed from wait thread #define WAIT_STATE_REMOVED 2 //item is removed from wait thread #define WAIT_STATE_DELETESYNC 3 //item will be destroyed with removal #define WAIT_STATE_GAMEOVER 4 //item is destroyed #define WAIT_STATE_ABANDONED 5 //item expired and removed from wait thread but not yet unregistered #define NONSENSE 10000 // RegisterWaitForSingleObject routines. typedef struct { HANDLE Object; HANDLE TimerObject; WAITORTIMERCALLBACK Callback; PVOID Context; ULONG Milliseconds; ULONG Flags; PVOID waitThread; LONG State; LONG CallbacksPending; HANDLE CompletionEvent; HANDLE CallbackEvent; } WAIT_WORK_ITEM, *PWAIT_WORK_ITEM; typedef struct { HANDLE hThread; HANDLE WaitEvent; BOOL InSyncCallback; LONG nItems; PWAIT_WORK_ITEM waitItems[MAX_WAIT_ITEMS]; } WAIT_THREAD, *PWAIT_THREAD; typedef struct { PWAIT_WORK_ITEM item; BOOLEAN TimerOrWaitFired; } ASYNC_CALL, *PASYNC_CALL; static PWAIT_THREAD waitthreads[MAX_WAIT_THREADS] = {0}; static CRITICAL_SECTION wait_cs; BOOL init_threadpool() { InitializeCriticalSection(&wait_cs); return TRUE; } static VOID ActivateTimer(PWAIT_WORK_ITEM workItem) { if (workItem->Milliseconds != INFINITE) { LARGE_INTEGER timeout; timeout.QuadPart = workItem->Milliseconds*(LONGLONG)-10000; SetWaitableTimer(workItem->TimerObject,&timeout,0,NULL,NULL,FALSE); } } static VOID DestroyWaitItem(PWAIT_WORK_ITEM workItem) { CloseHandle(workItem->CallbackEvent); CloseHandle(workItem->TimerObject); HeapFree(GetProcessHeap(),0,workItem); } static VOID CALLBACK AddWaitItem(ULONG_PTR Arg) { PWAIT_WORK_ITEM workItem = (PWAIT_WORK_ITEM)Arg; PWAIT_THREAD waitThread = (PWAIT_THREAD)workItem->waitThread; int i; for (i=0;inItems;i++) { //we can't have one handle several times if (waitThread->waitItems[i]->Object == workItem->Object) { workItem->waitThread = NULL; SetEvent(workItem->CompletionEvent); return; } } waitThread->waitItems[waitThread->nItems] = workItem; waitThread->nItems++; SetEvent(workItem->CompletionEvent); //what if callback will fire before RegisterWait... returns? ActivateTimer(workItem); } static VOID CALLBACK RemoveWaitItem(ULONG_PTR Arg) { PWAIT_WORK_ITEM workItem = (PWAIT_WORK_ITEM)Arg; PWAIT_THREAD waitThread = (PWAIT_THREAD)workItem->waitThread; int i, j; for (i=0;iwaitItems[i] == workItem) break; if (inItems-1;j++) { waitThread->waitItems[j]=waitThread->waitItems[j+1]; waitThread->waitItems[j+1]=NULL; } waitThread->nItems--; } //if unregister marked it for delete, kill it here if (InterlockedCompareExchange(&workItem->State,WAIT_STATE_GAMEOVER,WAIT_STATE_DELETESYNC) == WAIT_STATE_DELETESYNC) { if (workItem->CompletionEvent) SetEvent(workItem->CompletionEvent); DestroyWaitItem(workItem); } else { InterlockedCompareExchange(&workItem->State,WAIT_STATE_ABANDONED,WAIT_STATE_ACTIVE); InterlockedCompareExchange(&workItem->State,WAIT_STATE_REMOVED,WAIT_STATE_UNREGISTERED); //mark it removed } } static DWORD CALLBACK AsyncWaiterCall(LPVOID Arg) { PASYNC_CALL waiter = (PASYNC_CALL)Arg; PWAIT_WORK_ITEM workItem = waiter->item; if (workItem->State == WAIT_STATE_ACTIVE || workItem->State == WAIT_STATE_ABANDONED) workItem->Callback(workItem->Context,waiter->TimerOrWaitFired); LONG CallbacksPending = InterlockedDecrement(&workItem->CallbacksPending); SetEvent(workItem->CallbackEvent); if (CallbacksPending == 0 && workItem->State != WAIT_STATE_ACTIVE) { //if it's removed, all fine, destroy it here if (InterlockedCompareExchange(&workItem->State,WAIT_STATE_GAMEOVER,WAIT_STATE_REMOVED) == WAIT_STATE_REMOVED) { if (workItem->CompletionEvent) SetEvent(workItem->CompletionEvent); DestroyWaitItem(workItem); } else //destroy it with removal InterlockedCompareExchange(&workItem->State,WAIT_STATE_DELETESYNC,WAIT_STATE_UNREGISTERED); } HeapFree(GetProcessHeap(),0,Arg); return 0; } static VOID TerminateWaitThread(PWAIT_THREAD threadinfo) { int i; EnterCriticalSection(&wait_cs); for (i=0;inItems;i++) InterlockedCompareExchange(&threadinfo->waitItems[i]->State,WAIT_STATE_ABANDONED,WAIT_STATE_ACTIVE); //make sure there aren't any outstanding APCs SleepEx(0,TRUE); CloseHandle(threadinfo->hThread); CloseHandle(threadinfo->WaitEvent); HeapFree(GetProcessHeap(),0,threadinfo); } static DWORD WaitThreadWait(PWAIT_THREAD threadinfo) { int i; int nCount = threadinfo->nItems * 2; HANDLE* handles = (HANDLE*)alloca(sizeof(HANDLE)*nCount); for (i=0;inItems;i++) { handles[i*2] = threadinfo->waitItems[i]->Object; handles[(i*2)+1] = threadinfo->waitItems[i]->TimerObject; } return WaitForMultipleObjectsEx(nCount,handles,FALSE,INFINITE,TRUE); } static DWORD CALLBACK WaitThreadProc(LPVOID Arg) { PWAIT_THREAD threadinfo = (PWAIT_THREAD)Arg; //wait for first APC to come SleepEx(INFINITE,TRUE); while (TRUE) { SetEvent(threadinfo->WaitEvent); if (!threadinfo->nItems) //no things to do, wait 3 seconds and leave { if (SleepEx(3000,TRUE)!=WAIT_IO_COMPLETION) break; else continue; } DWORD status = WaitThreadWait(threadinfo); ResetEvent(threadinfo->WaitEvent); if (status == WAIT_FAILED) break; //FFFFFUUUU if (status >= WAIT_OBJECT_0 && status <= WAIT_OBJECT_0+MAXIMUM_WAIT_OBJECTS ) { status -= WAIT_OBJECT_0; PWAIT_WORK_ITEM workItem = threadinfo->waitItems[status/2]; BOOL TimerFired = (status & 1); if (workItem->State != WAIT_STATE_ACTIVE) continue; //don't run if removed if (workItem->CallbackEvent == NULL) //sync callback { SetEvent(threadinfo->WaitEvent); threadinfo->InSyncCallback = TRUE; workItem->Callback(workItem->Context,TimerFired); threadinfo->InSyncCallback = FALSE; } else { PASYNC_CALL waiter = (PASYNC_CALL)HeapAlloc(GetProcessHeap(),0,sizeof(*waiter)); if (waiter) { waiter->item = workItem; waiter->TimerOrWaitFired = TimerFired; InterlockedIncrement(&workItem->CallbacksPending); QueueUserWorkItem_new(AsyncWaiterCall,waiter,workItem->Flags); } } if (workItem->Flags & WT_EXECUTEONLYONCE || workItem->Milliseconds == 0) RemoveWaitItem((ULONG_PTR)workItem); else ActivateTimer(workItem); } } TerminateWaitThread(threadinfo); return 0; } static BOOL RegisterWaitItem(PWAIT_WORK_ITEM workItem) { EnterCriticalSection(&wait_cs); BOOL success = FALSE; int i; for (i=0;ihThread = CreateThread(NULL, 0, WaitThreadProc, waitthreads[i], 0, &dummy); if (waitthreads[i]->hThread == NULL) { HeapFree(GetProcessHeap(),0,waitthreads[i]); waitthreads[i]=NULL; break; } waitthreads[i]->WaitEvent = CreateEvent(NULL,TRUE,TRUE,NULL); } if (waitthreads[i]->nItems == MAX_WAIT_ITEMS || waitthreads[i]->InSyncCallback) //full or busy, gtfo continue; else { //try adding it workItem->waitThread = waitthreads[i]; workItem->CompletionEvent = CreateEvent(NULL,FALSE,FALSE,NULL); if ( QueueUserAPC(AddWaitItem,waitthreads[i]->hThread,(ULONG_PTR)workItem) ) { //wait until item is added WaitForSingleObject(workItem->CompletionEvent,INFINITE); success = (workItem->waitThread != NULL); } CloseHandle(workItem->CompletionEvent); workItem->CompletionEvent = NULL; if (success) break; } } LeaveCriticalSection(&wait_cs); return success; } static BOOL IsValidHandle( HANDLE hObject ) { HANDLE newobject = NULL; BOOL success = DuplicateHandle(GetCurrentProcess(),hObject,GetCurrentProcess(),&newobject,0,0,DUPLICATE_SAME_ACCESS); if (success) CloseHandle(newobject); return success; } /* MAKE_EXPORT RegisterWaitForSingleObject_new=RegisterWaitForSingleObject */ BOOL WINAPI RegisterWaitForSingleObject_new( PHANDLE phNewWaitObject, HANDLE hObject, WAITORTIMERCALLBACK Callback, PVOID Context, ULONG dwMilliseconds, ULONG dwFlags) { if (!phNewWaitObject || IsBadCodePtr((FARPROC)Callback) || !IsValidHandle(hObject)) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } PWAIT_WORK_ITEM newWorkItem = (PWAIT_WORK_ITEM)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(*newWorkItem)); if (!newWorkItem) return FALSE; newWorkItem->Object = hObject; newWorkItem->Callback = Callback; newWorkItem->Context = Context; newWorkItem->Milliseconds = dwMilliseconds; newWorkItem->Flags = dwFlags; if (!(dwFlags & WT_EXECUTEINWAITTHREAD)) newWorkItem->CallbackEvent = CreateEvent(NULL,FALSE,FALSE,NULL); newWorkItem->State = WAIT_STATE_ACTIVE; if (dwMilliseconds != INFINITE) newWorkItem->TimerObject = CreateWaitableTimer(NULL,FALSE,NULL); else newWorkItem->TimerObject = CreateEvent(NULL,FALSE,FALSE,NULL); //dummy event which never fires if (!RegisterWaitItem(newWorkItem)) { DestroyWaitItem(newWorkItem); SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } *phNewWaitObject = (HANDLE)newWorkItem; return TRUE; } /* MAKE_EXPORT UnregisterWaitEx_new=UnregisterWaitEx */ BOOL WINAPI UnregisterWaitEx_new( HANDLE WaitHandle, HANDLE CompletionEvent) { PWAIT_WORK_ITEM workItem = (PWAIT_WORK_ITEM)WaitHandle; if (!workItem) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } //9x dies if we queue an apc into dead thread so avoid it at all costs if ( InterlockedCompareExchange(&workItem->State,WAIT_STATE_UNREGISTERED,WAIT_STATE_ACTIVE)==WAIT_STATE_ACTIVE ) { PWAIT_THREAD waitthread = (PWAIT_THREAD)workItem->waitThread; if (!QueueUserAPC(RemoveWaitItem,waitthread->hThread,(ULONG_PTR)workItem)) return FALSE; //make sure thread is in wait state and isn't doing sth with item we delete WaitForSingleObject(waitthread->WaitEvent,NONSENSE); } InterlockedCompareExchange(&workItem->State,WAIT_STATE_REMOVED,WAIT_STATE_ABANDONED); BOOL pending = FALSE; if (workItem->CallbacksPending) { pending = TRUE; if (CompletionEvent != NULL) { if (CompletionEvent == INVALID_HANDLE_VALUE) { //wait for callbacks to finish while (workItem->CallbacksPending) WaitForSingleObject(workItem->CallbackEvent,INFINITE); pending = FALSE; } else InterlockedExchangePointer(&workItem->CompletionEvent, CompletionEvent); } } //all async callbacks are finished for sure? if (!pending) { //yes? and the item is removed from thread? if (InterlockedCompareExchange(&workItem->State,WAIT_STATE_GAMEOVER,WAIT_STATE_REMOVED) == WAIT_STATE_REMOVED) DestroyWaitItem(workItem); else //not removed? okay, it will be destroyed with removal InterlockedCompareExchange(&workItem->State,WAIT_STATE_DELETESYNC,WAIT_STATE_UNREGISTERED); } //otherwise, there are still pending callbacks and item will be destroyed as soon as all callbacks end if (pending) { SetLastError(ERROR_IO_PENDING); return FALSE; } return TRUE; } /* MAKE_EXPORT RegisterWaitForSingleObjectEx_new=RegisterWaitForSingleObjectEx */ HANDLE WINAPI RegisterWaitForSingleObjectEx_new(HANDLE hObject, WAITORTIMERCALLBACK Callback, PVOID Context, ULONG dwMilliseconds, ULONG dwFlags) { HANDLE retHandle; if ( RegisterWaitForSingleObject_new(&retHandle,hObject,Callback,Context,dwMilliseconds,dwFlags) ) return retHandle; else return NULL; } /* MAKE_EXPORT UnregisterWait_new=UnregisterWait */ BOOL WINAPI UnregisterWait_new(HANDLE WaitHandle) { return UnregisterWaitEx_new(WaitHandle,NULL); } // Timer Queue routines #define MAX_TIMERS MAXIMUM_WAIT_OBJECTS typedef struct { HANDLE TimerObject; WAITORTIMERCALLBACK Callback; PVOID Parameter; ULONG DueTime; DWORD Period; ULONG Flags; PVOID TimerQueue; LONG State; LONG CallbacksPending; HANDLE CompletionEvent; HANDLE CallbackEvent; } TIMER_ITEM, *PTIMER_ITEM; typedef struct { HANDLE hThread; HANDLE WaitEvent; HANDLE CompletionEvent; LONG nItems; PTIMER_ITEM timers[MAX_TIMERS]; } TIMER_QUEUE, *PTIMER_QUEUE; static PTIMER_QUEUE defaultTimerQueue = NULL; static VOID DestroyTimerItem(PTIMER_ITEM timer) { CloseHandle(timer->CallbackEvent); CloseHandle(timer->TimerObject); HeapFree(GetProcessHeap(),0,timer); } static VOID CALLBACK ChangeTimerItem(PTIMER_ITEM timer) { //(re)activate waitable timer LARGE_INTEGER timeout; timeout.QuadPart = timer->DueTime * (LONGLONG)-10000; SetWaitableTimer(timer->TimerObject,&timeout,(timer->Flags & WT_EXECUTEONLYONCE) ? 0 : timer->Period,NULL,NULL,FALSE); } static VOID CALLBACK AddTimerItem(ULONG_PTR Arg) { PTIMER_ITEM timer = (PTIMER_ITEM)Arg; PTIMER_QUEUE timerqueue = (PTIMER_QUEUE)timer->TimerQueue; if (timerqueue->nItems == MAX_TIMERS) //too much timers { timer->TimerQueue = NULL; SetEvent(timer->CompletionEvent); return; } timerqueue->timers[timerqueue->nItems] = timer; timerqueue->nItems++; SetEvent(timer->CompletionEvent); ChangeTimerItem(timer); } static VOID CALLBACK RemoveTimerItem(ULONG_PTR Arg) { PTIMER_ITEM timer = (PTIMER_ITEM)Arg; PTIMER_QUEUE timerqueue = (PTIMER_QUEUE)timer->TimerQueue; int i, j; for (i=0;itimers[i] == timer) break; if (inItems-1;j++) { timerqueue->timers[j]=timerqueue->timers[j+1]; timerqueue->timers[j+1]=NULL; } timerqueue->nItems--; } //if unregister marked it for delete, kill it here if (InterlockedCompareExchange(&timer->State,WAIT_STATE_GAMEOVER,WAIT_STATE_DELETESYNC) == WAIT_STATE_DELETESYNC) { if (timer->CompletionEvent) SetEvent(timer->CompletionEvent); DestroyTimerItem(timer); } else InterlockedCompareExchange(&timer->State,WAIT_STATE_REMOVED,WAIT_STATE_UNREGISTERED); //mark it removed } static DWORD CALLBACK AsyncTimerCall(LPVOID Arg) { PTIMER_ITEM timer = PTIMER_ITEM(Arg); if (timer->State == WAIT_STATE_ACTIVE) timer->Callback(timer->Parameter,TRUE); LONG CallbacksPending = InterlockedDecrement(&timer->CallbacksPending); SetEvent(timer->CallbackEvent); if (CallbacksPending == 0 && timer->State != WAIT_STATE_ACTIVE) { //if it's removed, all fine, destroy it here if (InterlockedCompareExchange(&timer->State,WAIT_STATE_GAMEOVER,WAIT_STATE_REMOVED) == WAIT_STATE_REMOVED) { if (timer->CompletionEvent) SetEvent(timer->CompletionEvent); DestroyTimerItem(timer); } else //destroy it with removal InterlockedCompareExchange(&timer->State,WAIT_STATE_DELETESYNC,WAIT_STATE_UNREGISTERED); } return 0; } /* MAKE_EXPORT DeleteTimerQueueTimer_new=DeleteTimerQueueTimer */ BOOL WINAPI DeleteTimerQueueTimer_new( HANDLE TimerQueue, HANDLE Timer, HANDLE CompletionEvent) { //absolutely equal to UnregisterWaitEx if (!Timer) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } PTIMER_ITEM timer = (PTIMER_ITEM)Timer; PTIMER_QUEUE timerqueue = (PTIMER_QUEUE)timer->TimerQueue; if ( InterlockedCompareExchange(&timer->State,WAIT_STATE_UNREGISTERED,WAIT_STATE_ACTIVE)==WAIT_STATE_ACTIVE ) { if (!QueueUserAPC(RemoveTimerItem,timerqueue->hThread,(ULONG_PTR)timer)) return FALSE; //make sure thread is in wait state and isn't doing sth with item we delete WaitForSingleObject(timerqueue->WaitEvent,NONSENSE); } BOOL pending = FALSE; if (timer->CallbacksPending) { pending = TRUE; if (CompletionEvent != NULL) { if (CompletionEvent == INVALID_HANDLE_VALUE) { while (timer->CallbacksPending) WaitForSingleObject(timer->CallbackEvent,INFINITE); pending = FALSE; } else InterlockedExchangePointer(&timer->CompletionEvent, CompletionEvent); } } if (!pending) { if (InterlockedCompareExchange(&timer->State,WAIT_STATE_GAMEOVER,WAIT_STATE_REMOVED) == WAIT_STATE_REMOVED) DestroyTimerItem(timer); else InterlockedCompareExchange(&timer->State,WAIT_STATE_DELETESYNC,WAIT_STATE_UNREGISTERED); } if (pending) { SetLastError(ERROR_IO_PENDING); return FALSE; } return TRUE; } static VOID CALLBACK TerminateTimerQueue(ULONG_PTR Arg) { PTIMER_QUEUE timerqueue = (PTIMER_QUEUE)Arg; CloseHandle(timerqueue->hThread); CloseHandle(timerqueue->WaitEvent); HeapFree(GetProcessHeap(),0,timerqueue); ExitThread(0); } static VOID CALLBACK FinishTimerQueue(ULONG_PTR Arg) { int i; PTIMER_QUEUE timerqueue = (PTIMER_QUEUE)Arg; EnterCriticalSection(&wait_cs); if (defaultTimerQueue == timerqueue) defaultTimerQueue = NULL; LeaveCriticalSection(&wait_cs); //delete all timers, waiting for (i=timerqueue->nItems-1;i>=0;i--) DeleteTimerQueueTimer_new( timerqueue, timerqueue->timers[i], INVALID_HANDLE_VALUE ); if (timerqueue->CompletionEvent) SetEvent(timerqueue->CompletionEvent); //queue object termination QueueUserAPC(TerminateTimerQueue,GetCurrentThread(),Arg); } static DWORD TimerQueueWait(PTIMER_QUEUE timerqueue) { int i; HANDLE* handles = (HANDLE*)alloca(sizeof(HANDLE)*timerqueue->nItems); for (i=0;inItems;i++) handles[i] = timerqueue->timers[i]->TimerObject; return WaitForMultipleObjectsEx(timerqueue->nItems,handles,FALSE,INFINITE,TRUE); } static DWORD CALLBACK TimerQueueProc(LPVOID Arg) { PTIMER_QUEUE timerqueue = (PTIMER_QUEUE)Arg; while (TRUE) { SetEvent(timerqueue->WaitEvent); if (!timerqueue->nItems) //wait for items { SleepEx(INFINITE,TRUE); continue; } DWORD status = TimerQueueWait(timerqueue); ResetEvent(timerqueue->WaitEvent); if (status == WAIT_FAILED) break; //uberfail if (status >= WAIT_OBJECT_0 && status <= WAIT_OBJECT_0+MAXIMUM_WAIT_OBJECTS ) { PTIMER_ITEM timer = timerqueue->timers[status-WAIT_OBJECT_0]; if (timer->State != WAIT_STATE_ACTIVE) continue; if (timer->CallbackEvent == NULL) //sync callback { SetEvent(timerqueue->WaitEvent); timer->Callback(timer->Parameter,TRUE); } else { InterlockedIncrement(&timer->CallbacksPending); QueueUserWorkItem_new(AsyncTimerCall,timer,timer->Flags); } } } FinishTimerQueue((ULONG_PTR)timerqueue); SleepEx(0,TRUE); return 0; } /* MAKE_EXPORT CreateTimerQueue_new=CreateTimerQueue */ HANDLE WINAPI CreateTimerQueue_new(VOID) { PTIMER_QUEUE timerqueue = (PTIMER_QUEUE)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(*timerqueue)); if (timerqueue != NULL) { DWORD dummy; timerqueue->WaitEvent = CreateEvent(NULL,TRUE,TRUE,NULL); timerqueue->hThread = CreateThread(NULL,0,TimerQueueProc,timerqueue,0,&dummy); if (!timerqueue->hThread) { HeapFree(GetProcessHeap(),HEAP_ZERO_MEMORY,timerqueue); timerqueue = NULL; } } return (HANDLE)timerqueue; } /* MAKE_EXPORT CreateTimerQueueTimer_new=CreateTimerQueueTimer */ BOOL WINAPI CreateTimerQueueTimer_new( PHANDLE phNewTimer, HANDLE TimerQueue, WAITORTIMERCALLBACK Callback, PVOID Parameter, DWORD DueTime, DWORD Period, ULONG Flags) { if (!phNewTimer || IsBadCodePtr((FARPROC)Callback)) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } *phNewTimer = NULL; if (TimerQueue == NULL) { EnterCriticalSection(&wait_cs); if (defaultTimerQueue == NULL) defaultTimerQueue = (PTIMER_QUEUE)CreateTimerQueue_new(); TimerQueue = defaultTimerQueue; LeaveCriticalSection(&wait_cs); if (TimerQueue == NULL) return FALSE; } PTIMER_ITEM timer = (PTIMER_ITEM)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(*timer)); timer->TimerQueue = TimerQueue; timer->Callback = Callback; timer->Parameter = Parameter; timer->DueTime = DueTime; timer->Period = Period; timer->Flags = Flags; timer->State = WAIT_STATE_ACTIVE; timer->TimerObject = CreateWaitableTimer(NULL,FALSE,NULL); if (!(Flags & WT_EXECUTEINTIMERTHREAD)) timer->CallbackEvent = CreateEvent(NULL,FALSE,FALSE,NULL); timer->CompletionEvent = CreateEvent(NULL,FALSE,FALSE,NULL); BOOL success = (BOOL)QueueUserAPC(AddTimerItem,((PTIMER_QUEUE)TimerQueue)->hThread,(ULONG_PTR)timer); if (success) { WaitForSingleObject(timer->CompletionEvent,INFINITE); CloseHandle(timer->CompletionEvent); timer->CompletionEvent = NULL; success = (timer->TimerObject != NULL); if (success) *phNewTimer = timer; } return success; } /* MAKE_EXPORT DeleteTimerQueueEx_new=DeleteTimerQueueEx */ BOOL WINAPI DeleteTimerQueueEx_new( HANDLE TimerQueue, HANDLE CompletionEvent) { PTIMER_QUEUE timerqueue = (PTIMER_QUEUE)TimerQueue; if (!timerqueue) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } if (CompletionEvent != NULL) { if (CompletionEvent == INVALID_HANDLE_VALUE) timerqueue->CompletionEvent = CreateEvent(NULL,FALSE,FALSE,NULL); else timerqueue->CompletionEvent = CompletionEvent; } QueueUserAPC(FinishTimerQueue,timerqueue->hThread,(ULONG_PTR)timerqueue); if (CompletionEvent == INVALID_HANDLE_VALUE) { WaitForSingleObject(timerqueue->CompletionEvent,INFINITE); CloseHandle(timerqueue->CompletionEvent); } return TRUE; } /* MAKE_EXPORT DeleteTimerQueue_new=DeleteTimerQueue */ BOOL WINAPI DeleteTimerQueue_new( HANDLE TimerQueue) { return DeleteTimerQueueEx_new(TimerQueue,NULL); } /* MAKE_EXPORT ChangeTimerQueueTimer_new=ChangeTimerQueueTimer */ BOOL WINAPI ChangeTimerQueueTimer_new( HANDLE TimerQueue, HANDLE Timer, ULONG DueTime, ULONG Period) { PTIMER_ITEM timer = (PTIMER_ITEM)Timer; if (!timer || timer->State != WAIT_STATE_ACTIVE) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } timer->DueTime = DueTime; timer->Period = Period; return (BOOL)QueueUserAPC((PAPCFUNC)ChangeTimerItem,((PTIMER_QUEUE)timer->TimerQueue)->hThread,(ULONG_PTR)timer); } //some undocumented APIs too... /* MAKE_EXPORT CancelTimerQueueTimer_new=CancelTimerQueueTimer */ BOOL WINAPI CancelTimerQueueTimer_new( HANDLE TimerQueue, HANDLE Timer) { PTIMER_ITEM timer = (PTIMER_ITEM)Timer; if (!timer || timer->State != WAIT_STATE_ACTIVE) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } return CancelWaitableTimer(timer->TimerObject); } /* MAKE_EXPORT SetTimerQueueTimer_new=SetTimerQueueTimer */ HANDLE WINAPI SetTimerQueueTimer_new( HANDLE TimerQueue, WAITORTIMERCALLBACK Callback, PVOID Parameter, DWORD DueTime, DWORD Period, BOOL PreferIo ) { HANDLE newHandle = NULL; CreateTimerQueueTimer_new(&newHandle,TimerQueue,Callback,Parameter,DueTime,Period,PreferIo ? WT_EXECUTEINIOTHREAD : WT_EXECUTEDEFAULT); return newHandle; }