Skip to content

Instantly share code, notes, and snippets.

@rbmm
Created May 19, 2023 21:35
Show Gist options
  • Save rbmm/154da2597da2e2464c7e5e1c6126f788 to your computer and use it in GitHub Desktop.
Save rbmm/154da2597da2e2464c7e5e1c6126f788 to your computer and use it in GitHub Desktop.
BOOL timer_create(
_Outptr_ PHANDLE phTimer,
_In_ WAITORTIMERCALLBACK Callback,
_In_opt_ PVOID Parameter,
_In_ DWORD DueTime,
_In_ DWORD Period
);
void timer_release(HANDLE /*hTimer*/);
BOOL timer_cancel(HANDLE hTimer, void (NTAPI *timer_canceled_callback)(_In_opt_ PVOID Parameter), _In_opt_ PVOID Parameter );
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
#ifndef IDC_STATIC
#define IDC_STATIC MAXUSHORT
#endif
struct DTimer
{
HANDLE _M_hTimer = 0;
HWND _M_hwndDlg = 0;
LONG _M_dwRefCount = 1;
ULONG _M_n = 0;
ULONG _M_dwThreadId = GetCurrentThreadId();
LONG _M_stopping = FALSE;
void AddRef()
{
InterlockedIncrementNoFence(&_M_dwRefCount);
}
void Release()
{
if (!InterlockedDecrement(&_M_dwRefCount))
{
delete this;
}
}
static BOOL CALLBACK EnumThreadWndProc( HWND hwnd, LPARAM lParam)
{
if (GetClassLong(hwnd, GCW_ATOM) == (ULONG)(ULONG_PTR)WC_DIALOG)
{
reinterpret_cast<DTimer*>(lParam)->_M_hwndDlg = hwnd;
return FALSE;
}
return TRUE;
}
void OnTimer()
{
DbgPrint("%x: %s<%p>(%x)\n", GetCurrentThreadId(), __FUNCTION__, this, _M_n);
if (!_M_hwndDlg)
{
EnumThreadWindows(_M_dwThreadId, EnumThreadWndProc, (LPARAM)this);
}
if (--_M_n)
{
if (_M_hwndDlg)
{
WCHAR txt[32];
if (0 < swprintf_s(txt, _countof(txt), L"...%02u", _M_n))
{
SetDlgItemTextW(_M_hwndDlg, IDC_STATIC, txt);
}
}
}
else
{
if (_M_hwndDlg)
{
PostMessageW(_M_hwndDlg, WM_CLOSE, 0, 0);
}
Stop();
}
}
static VOID NTAPI WaitOrTimerCallback(PVOID This, BOOLEAN )
{
reinterpret_cast<DTimer*>(This)->OnTimer();
}
static void NTAPI timer_canceled_callback(PVOID This)
{
DbgPrint("%x: %s<%p>\n", GetCurrentThreadId(), __FUNCTION__, This);
reinterpret_cast<DTimer*>(This)->Release();
}
DTimer(ULONG n) : _M_n(n)
{
DbgPrint("%x: %s<%p>\n", GetCurrentThreadId(), __FUNCTION__, this);
}
~DTimer()
{
DbgPrint("%x: %s<%p>\n", GetCurrentThreadId(), __FUNCTION__, this);
if (_M_hTimer)
{
timer_release(_M_hTimer);
}
PostThreadMessageW(_M_dwThreadId, WM_QUIT, 0, 0);
}
BOOL Start(_In_ DWORD DueTime, _In_ DWORD Period)
{
AddRef();
if (timer_create(&_M_hTimer, WaitOrTimerCallback, this, DueTime, Period))
{
return TRUE;
}
Release();
return FALSE;
}
void Stop()
{
if (!InterlockedExchangeNoFence(&_M_stopping, TRUE))
{
timer_cancel(_M_hTimer, timer_canceled_callback, this);
}
}
};
void t_test(ULONG n)
{
if (DTimer* timer = new DTimer(n))
{
if (timer->Start(0, 1000))
{
WCHAR txt[32];
if (0 < swprintf_s(txt, _countof(txt), L"...%02u", n))
{
MessageBox(0, txt, L"timer run", MB_ICONINFORMATION);
}
timer->Stop();
}
timer->Release();
}
MessageBox(0, 0, L"demo delay", MB_ICONWARNING);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BOOL timer_create(
_Outptr_ PHANDLE phTimer,
_In_ WAITORTIMERCALLBACK Callback,
_In_opt_ PVOID Parameter,
_In_ DWORD DueTime,
_In_ DWORD Period
)
{
return CreateTimerQueueTimer(phTimer, 0, Callback, Parameter, DueTime, Period, 0);
}
void timer_release(HANDLE /*hTimer*/)
{
}
BOOL timer_cancel(HANDLE hTimer, void (NTAPI *timer_canceled_callback)(_In_opt_ PVOID Parameter), _In_opt_ PVOID Parameter )
{
if (HANDLE hEvent = CreateEvent(0, 0, 0, 0))
{
if (!DeleteTimerQueueTimer(0, hTimer, hEvent) && GetLastError() != ERROR_IO_PENDING)
{
__debugbreak();
}
struct WO
{
HANDLE hEvent;
HANDLE hWaitObj = 0;
void (NTAPI *timer_canceled_callback)(PVOID Parameter);
PVOID Parameter;
WO(HANDLE hEvent, void (NTAPI *timer_canceled_callback)(PVOID Parameter), PVOID Parameter) : hEvent(hEvent),
timer_canceled_callback(timer_canceled_callback), Parameter(Parameter)
{
}
void OnWait()
{
DbgPrint("%x: %s<%p>\n", GetCurrentThreadId(), __FUNCTION__, this);
if (!UnregisterWaitEx(hWaitObj, 0) && GetLastError() != ERROR_IO_PENDING)
{
__debugbreak();
}
CloseHandle(hEvent);
timer_canceled_callback(Parameter);
delete this;
}
static VOID NTAPI WaitOrTimerCallback(PVOID This, BOOLEAN TimerOrWaitFired )
{
if (TimerOrWaitFired)
{
__debugbreak();
}
reinterpret_cast<WO*>(This)->OnWait();
}
};
if (WO* pwo = new WO(hEvent, timer_canceled_callback, Parameter))
{
if (RegisterWaitForSingleObject(&pwo->hWaitObj, hEvent,
WO::WaitOrTimerCallback, pwo, INFINITE, WT_EXECUTEINWAITTHREAD|WT_EXECUTEONLYONCE))
{
return TRUE;
}
delete pwo;
}
CloseHandle(hEvent);
}
return FALSE;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment