使用 timeSetEvent
MMRESULT timeSetEvent( UINT uDelay,
UINT uResolution,
LPTIMECALLBACK lpTimeProc,
WORD dwUser,
UINT fuEvent )
uDelay:以毫秒指定事件的周期。
Uresolution:以毫秒指定延時的精度,數值越小定時器事件分辨率越高。缺省值為1ms。
LpTimeProc:指向一個回調函數。
DwUser:存放用戶提供的回調數據。
FuEvent:指定定時器事件類型:
TIME_ONESHOT:uDelay毫秒后只產生一次事件
TIME_PERIODIC :每隔uDelay毫秒周期性地產生事件。
c#包裝類:
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
/// <summary>
///
/// </summary>
public class MultimediaTimerWapper : IDisposable
{
private bool disposed = false;
private int interval;
private int resolution;
/// <summary>
/// 當前定時器實例ID
/// </summary>
private uint timerId;
// 保持定時器回調以防止垃圾收集。
private readonly MultimediaTimerCallback Callback;
/// <summary>
/// API使用的回調
/// </summary>
public event EventHandler Timer;
public MultimediaTimerWapper()
{
Callback = new MultimediaTimerCallback(TimerCallbackMethod);
Resolution = 0;
}
~MultimediaTimerWapper()
{
Dispose(false);
}
/// <summary>
/// 注意最小分辨率為 0,表示可能的最高分辨率。
/// </summary>
public int Resolution
{
get
{
return resolution;
}
set
{
CheckDisposed();
if (value < 0)
throw new ArgumentOutOfRangeException("value");
resolution = value;
}
}
/// <summary>
/// 是否在運行
/// </summary>
public bool IsRunning
{
get { return timerId != 0; }
}
/// <summary>
/// 啟動一個定時器實例
/// </summary>
/// <param name="ms">以毫秒為單位的計時器間隔</param>
/// <param name="repeat">如果為 true 設置重復事件,否則設置一次性</param>
public void Start(uint ms,bool repeat)
{
//殺死任何現有的計時器
CheckDisposed();
if (IsRunning)
throw new InvalidOperationException("Timer is already running");
//Set the timer type flags
fuEvent f = fuEvent.TIME_CALLBACK_FUNCTION | (repeat ? fuEvent.TIME_PERIODIC : fuEvent.TIME_ONESHOT);
// Event type = 0, one off event
// Event type = 1, periodic event
uint userCtx = 0;
lock (this)
{
timerId = NativeMethods.TimeSetEvent(ms, (uint)Resolution, Callback, ref userCtx, (uint)f);
if (timerId == 0)
{
int error = Marshal.GetLastWin32Error();
throw new Win32Exception(error);
}
}
}
/// <summary>
///
/// </summary>
public void Stop()
{
CheckDisposed();
if (!IsRunning)
throw new InvalidOperationException("Timer has not been started");
StopInternal();
}
private void StopInternal()
{
lock (this)
{
if(timerId != 0)
{
NativeMethods.TimeKillEvent(timerId);
timerId = 0;
}
}
}
public void Dispose()
{
Dispose(true);
}
private void TimerCallbackMethod(uint id, uint msg, ref uint userCtx, uint rsv1, uint rsv2)
{
Timer?.Invoke(this, EventArgs.Empty);
}
private void CheckDisposed()
{
if (disposed)
throw new ObjectDisposedException("MultimediaTimer");
}
private void Dispose(bool disposing)
{
if (disposed)
return;
disposed = true;
if (IsRunning)
{
StopInternal();
}
if (disposing)
{
Timer = null;
GC.SuppressFinalize(this);
}
}
}
internal delegate void MultimediaTimerCallback(uint id, uint msg, ref uint userCtx, uint rsv1, uint rsv2);
/// <summary>
/// 此結構包含有關計時器分辨率的信息。單位是ms
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct TIMECAPS
{
/// <summary>
/// 支持的最小期限。
/// </summary>
public uint wPeriodMin;
/// <summary>
/// 支持的最大期限。
/// </summary>
public uint wPeriodMax;
}
/// <summary>
/// 定時器類型定義
/// </summary>
[Flags]
public enum fuEvent : uint
{
TIME_ONESHOT = 0, //Event occurs once, after uDelay milliseconds.
TIME_PERIODIC = 1,
TIME_CALLBACK_FUNCTION = 0x0000, /* callback is function */
//TIME_CALLBACK_EVENT_SET = 0x0010, /* callback is event - use SetEvent */
//TIME_CALLBACK_EVENT_PULSE = 0x0020 /* callback is event - use PulseEvent */
}
internal static class NativeMethods
{
/// <summary>
/// 此函數啟動指定的計時器事件。
/// </summary>
/// <param name="msDelay">事件延遲,以毫秒為單位。如果該值不在計時器支持的最小和最大事件延遲范圍內,則該函數返回錯誤。</param>
/// <param name="msResolution">計時器事件的分辨率,以毫秒為單位。分辨率越高,分辨率越高;零分辨率表示周期性事件應該以最大可能的精度發生。但是,為減少系統開銷,應使用適合您的應用程序的最大值。</param>
/// <param name="callback">如果fuEvent指定TIME_CALLBACK_EVENT_SET或TIME_CALLBACK_EVENT_PULSE標志,則fptc參數將解釋為事件對象的句柄。事件將在單個事件完成時設置或發出脈沖,或者在周期性事件完成時定期設置或觸發。對於fuEvent的任何其他值,fptc參數將被解釋為函數指針。</param>
/// <param name="userCtx">用戶提供的回調數據。</param>
/// <param name="fuEvent">計時器事件類型。下表顯示了fuEvent參數可以包含的值。</param>
[DllImport("winmm.dll", SetLastError = true, EntryPoint = "timeSetEvent")]
internal static extern uint TimeSetEvent(uint msDelay, uint msResolution, MultimediaTimerCallback callback, ref uint userCtx, uint fuEvent);
/// <summary>
/// 此功能取消指定的計時器事件。
/// </summary>
/// <param name="uTimerId">要取消的計時器事件的標識符。此標識符由timeSetEvent函數返回,該函數啟動指定的計時器事件。</param>
/// <returns></returns>
[DllImport("winmm.dll", SetLastError = true, EntryPoint = "timeKillEvent")]
internal static extern void TimeKillEvent(uint uTimerId);
/// <summary>
/// 此函數查詢計時器設備以確定其分辨率。
/// </summary>
/// <param name="ptc">指向TIMECAPS結構的指針。該結構充滿了有關計時器設備分辨率的信息。</param>
/// <param name="cbtc">TIMECAPS結構的大小(以字節為單位)。</param>
/// <returns>如果成功,則返回TIMERR_NOERROR,如果未能返回計時器設備功能,則返回TIMERR_STRUCT。</returns>
[DllImport("winmm.dll")]
internal static extern uint timeGetDevCaps(ref TIMECAPS ptc, int cbtc);
[DllImport("Winmm.dll", CharSet = CharSet.Auto)]
internal static extern uint timeGetTime();
[DllImport("Winmm.dll", CharSet = CharSet.Auto)]
internal static extern uint timeBeginPeriod(uint uPeriod);
[DllImport("Winmm.dll", CharSet = CharSet.Auto)]
internal static extern uint timeEndPeriod(uint uPeriod);
}
調用方式:
static void Main(string[] args)
{
MultimediaTimerWapper multimediaTimer = new MultimediaTimerWapper();
multimediaTimer.Resolution = 1;
multimediaTimer.Timer += MultimediaTimer_Timer;
multimediaTimer.Start(1000,false);
Console.ReadKey();
multimediaTimer.Stop();
}
private static void MultimediaTimer_Timer(object sender, EventArgs e)
{
Console.WriteLine("執行完成");
}
使用 CreateTimerQueueTimer
定時器
定時器隊列(Timer Queue)可以使用CreateTimerQueue
函數創建。定時器隊列中的定時器是輕量級對象,可以在一定時間間隔之后調用指定的回調函數(可以只調用一次,也可以是周期性的)。這種等待操作由線程池中某個線程處理的(系統級別)。
向定時器隊列中添加定時器可以使用CreateTimerQueueTimer
函數。更新一個計時器隊列中的計時器可以使用 ChangeTimerQueueTimer
函數。這兩個函數中你可以指定一個回調函數,如果設定時間到達,線程池中某個線程會調用該回調函數。
使用 DeleteTimerQueueTimer
函數可以取消掛起的計時器。當不再使計時器隊列的時候,調用 DeleteTimerQueueEx
函數刪除計時器隊列,該函數調用是會取消並刪除任何在該隊列中等待的計時器。
1.HANDLE CreateTimerQueue()
:該函數創建一個定時器隊列,定時器隊列對一組定時器進行組織安排。一旦擁有一個定時器隊列,就可以在該隊列中創建下面的定時器:
2.BOOL CreateTimerQueueTimer(PHANDLE phNewTimer, HANDLE hTimerQueue,WAITORTIMERCALLBACK pfnCallback,PVOID pvContext,DWORD dwDueTimer,DWORD dwPeriod,ULONG dwFlags)
:如果只是創建少數幾個定時器,只需要為hTimerQueue
參數傳遞NULL
,並且完全避免調用CreateTimerQueue
函數。這會使用默認的定時器隊列,並且簡化你的代碼。工作回調函數必須采用下面的原型: VOID WINAPI WaitOrTimerCallback(PVOID pvContext,BOOL fTimerOrWatiFired)
;
dwFlags
參數的取值:WT_EXECUTEDEFAULT
(用非I/O組件處理工作項目),WT_EXECUTEINIOTHREAD
(用I/O組件),WT_EXECUTEPERSISTENTTHREAD
(用定時器),WT_EXECUTELONGFUNCTION
(工作項目需要花費較長時間時)
還有一個標志是WT_EXECUTEINTIMERTHREAD
,它使定時器組件的線程可以執行工作項目,雖然這可以使工作項目的運行效率更高,但是這非常危險,如果工作項目長時間中斷運行,那么等待定時器的線程就無法執行任何其他操作。如果打算這么做,那么該代碼應該迅速執行,不應該中斷。
回調函數的原型:VOID WINAPI WaitOrTimerCallbackFunc( PVOID pvContext, BOOLEAN fTimerOrWaitFired)
;
3.可以調用 BOOL ChangeTimerQueueTimer(HANDLE hTimerQueue ,HANDLE hTimer, ULONG dwDueTimer,ULONG dwPeriod)
函數來改變它的到期時間和到期周期。
4.當不再使用定時器時,必須通過調用下面的函數將它刪除: BOOL DeleteTimerQueueTimer(HANDLE hTimerQueue,HANDLE hTimer, HANDLE hCompletionEvent)
;
5. 當要刪除定時器隊列:BOOL DeleteTimerQueueEX(HANDLE hTimerQueue,HANDLE hCompletionEvent)
;
c#包裝類:
using System;
using System.Runtime.InteropServices;
/// <summary>
/// 高精度計時器
/// </summary>
public class TimerQueueTimerWapper : IDisposable
{
/// <summary>
/// Handle to the timer.(處理定時器。)
/// </summary>
private IntPtr phNewTimer;
#region Win32 TimerQueueTimer 函數
/// <summary>
///
/// </summary>
/// <param name="phNewTimer">Pointer to a handle; this is an out value(指向句柄的指針; 這是一個out值)</param>
/// <param name="TimerQueue">Timer queue handle. For the default timer queue, NULL (定時器隊列句柄。 對於默認計時器隊列,NULL)</param>
/// <param name="Callback">Pointer to the callback function (指向回調函數的指針)</param>
/// <param name="Parameter">Value passed to the callback function (傳遞給回調函數的值)</param>
/// <param name="DueTime">Time (milliseconds), before the timer is set to the signaled state for the first time
/// (時間(毫秒),在定時器第一次設置為信號狀態之前)</param>
/// <param name="Period">Time period (milliseconds). If zero, timer is signaled only once(計時器周期(毫秒)。如果為零,則計時器僅發出一次信號)</param>
/// <param name="Flags">One or more of the next values (table taken from MSDN):
/// WT_EXECUTEINTIMERTHREAD The callback function is invoked by the timer thread itself. This flag should be used only for short tasks or it could affect other timer operations.
/// WT_EXECUTEINIOTHREAD The callback function is queued to an I/O worker thread. This flag should be used if the function should be executed in a thread that waits in an alertable state.
/// </param>
/// <remarks>
/// The callback function is queued as an APC. Be sure to address reentrancy issues if the function performs an alertable wait operation.
/// WT_EXECUTEINPERSISTENTTHREAD The callback function is queued to a thread that never terminates. This flag should be used only for short tasks or it could affect other timer operations.
/// Note that currently no worker thread is persistent, although no worker thread will terminate if there are any pending I/O requests.
/// WT_EXECUTELONGFUNCTION Specifies that the callback function can perform a long wait. This flag helps the system to decide if it should create a new thread.
/// WT_EXECUTEONLYONCE The timer will be set to the signaled state only once.
/// </remarks>
/// <returns></returns>
[DllImport("kernel32.dll")]
private static extern bool CreateTimerQueueTimer(out IntPtr phNewTimer, IntPtr TimerQueue,WaitOrTimerDelegate Callback,
IntPtr Parameter,uint DueTime,uint Period, uint Flags);
/// <summary>
///
/// </summary>
/// <param name="timerQueue">A handle to the (default) timer queue</param>
/// <param name="timer">A handle to the timer</param>
/// <param name="completionEvent">A handle to an optional event to be signaled when the function is successful and all callback functions have completed. Can be NULL.</param>
/// <returns></returns>
[DllImport("kernel32.dll")]
private static extern bool DeleteTimerQueueTimer(IntPtr timerQueue,IntPtr timer, IntPtr completionEvent);
[DllImport("kernel32.dll")]
private static extern bool DeleteTimerQueue(IntPtr TimerQueue);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr hObject);
#endregion
public delegate void WaitOrTimerDelegate(IntPtr lpParameter, bool timerOrWaitFired);
public TimerQueueTimerWapper()
{
}
/// <summary>
/// 創建定時器
/// </summary>
/// <param name="dueTime">定時器第一次設置為信號狀態之前的時間(毫秒)。</param>
/// <param name="period">Period - 計時器周期(毫秒)。 如果為零,則計時器僅發出一次信號。</param>
/// <param name="callbackDelegate">回調函數參數</param>
public void Create(uint dueTime, uint period, WaitOrTimerDelegate callbackDelegate)
{
IntPtr pParameter = IntPtr.Zero;
bool success = CreateTimerQueueTimer(
// Timer handle
out phNewTimer,
// Default timer queue. IntPtr.Zero is just a constant value that represents a null pointer.
IntPtr.Zero,
// Timer callback function
callbackDelegate,
// Callback function parameter
pParameter,
// Time (milliseconds), before the timer is set to the signaled state for the first time.
// 定時器第一次設置為信號狀態之前的時間(毫秒)。
dueTime,
// Period - Timer period (milliseconds). If zero, timer is signaled only once.
//Period - 計時器周期(毫秒)。 如果為零,則計時器僅發出一次信號。
period,
(uint)Flag.WT_EXECUTEINIOTHREAD);
if (!success)
throw new QueueTimerException("Error creating QueueTimer");
}
/// <summary>
/// 刪除定時器
/// </summary>
public void Delete()
{
//bool success = DeleteTimerQueue(IntPtr.Zero);
bool success = DeleteTimerQueueTimer(
IntPtr.Zero, // TimerQueue - A handle to the (default) timer queue
phNewTimer, // Timer - A handle to the timer
IntPtr.Zero // CompletionEvent - A handle to an optional event to be signaled when the function is successful and all callback functions have completed. Can be NULL.
);
int error = Marshal.GetLastWin32Error();
//CloseHandle(phNewTimer);
}
private enum Flag
{
WT_EXECUTEDEFAULT = 0x00000000,
WT_EXECUTEINIOTHREAD = 0x00000001,
//WT_EXECUTEINWAITTHREAD = 0x00000004,
WT_EXECUTEONLYONCE = 0x00000008,
WT_EXECUTELONGFUNCTION = 0x00000010,
WT_EXECUTEINTIMERTHREAD = 0x00000020,
WT_EXECUTEINPERSISTENTTHREAD = 0x00000080,
//WT_TRANSFER_IMPERSONATION = 0x00000100
}
#region IDisposable Members
void IDisposable.Dispose()
{
Delete();
}
#endregion
}
public class QueueTimerException : Exception
{
public QueueTimerException(string message) : base(message)
{
}
public QueueTimerException(string message, Exception innerException)
: base(message, innerException)
{
}
}
調用方式:
static void Main(string[] args)
{
TimerQueueTimerWapper qt = new TimerQueueTimerWapper();
TimerQueueTimerWapper.WaitOrTimerDelegate callbackDelete = new TimerQueueTimerWapper.WaitOrTimerDelegate(QueueTimerCallback);
qt.Create(2000, 0, callbackDelete);
Console.ReadKey();
qt.Delete();
}
private static void QueueTimerCallback(IntPtr lpParameter, bool timerOrWaitFired)
{
Console.WriteLine("執行完成");
}