using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
using System.ComponentModel;
using System.Windows.Forms;
using System.Diagnostics;
class WakeUp
{
[DllImport("kernel32.dll")]
public static extern SafeWaitHandle CreateWaitableTimer(IntPtr lpTimerAttributes, bool bManualReset, string lpTimerName);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetWaitableTimer(SafeWaitHandle hTimer, [In] ref long pDueTime, int lPeriod, IntPtr pfnCompletionRoutine, IntPtr lpArgToCompletionRoutine, bool fResume);
//tmParam will be converted to the proper data type inside
//the class. Just send DateTime with the correct WakeUp time
//As the only param to the constructor of WakeUp
public WakeUp(DateTime tmParam)
{
//Create a new thread to set the waitable timer
//setWaitable(tmParam);
Debug.Print("Starting thread...");
Thread t = new Thread( () => setWaitable(tmParam) );
t.Start();
}
static void setWaitable(DateTime smParam)
{
long waketime = smParam.ToFileTime();
using (SafeWaitHandle handle = CreateWaitableTimer(IntPtr.Zero, true, DateTime.Now.ToString()))
{
//I need to try starting this block in a new thread since there is some blocking going on
//to set the waitable timer
if (SetWaitableTimer(handle, ref waketime, 0, IntPtr.Zero, IntPtr.Zero, true))
{
using (EventWaitHandle wh = new EventWaitHandle(false, EventResetMode.AutoReset))
{
wh.SafeWaitHandle = handle;
wh.WaitOne();
Debug.Print("TimerSet "+smParam.ToString());
}
}
else
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
}
}
然后创建新线程:
Debug.Print("TimerSet "+smParam.ToString());
永远不会打印到输出窗口。可以将构造函数更改为:
public WakeUp(DateTime tmParam)
{
//Create a new thread to set the waitable timer
//setWaitable(tmParam);
Debug.Print("Starting thread...");
setWaitable(tmParam);
}
然而,这将阻止当前线程大约需要两分钟或更短时间,但它每次都有效。我想把事情放到自己的线上。
出于某种原因,我在创建新线程EventWaitHandle时发布的第一种方式似乎永远不会回来。我认为它永远只是封锁。这可能是线程之间的安全问题吗?
只是为了澄清我的呼唤:
WakeUp test1 = new wakeUp(DateTime.Now.AddMinutes(2));
谢谢...
答案 0 :(得分:2)
new wakeUp(DateTime.Now.AddMinutes(2)
SetWaitableTimer()的 pDueTime 参数可以是增量或绝对的。您可以通过传递负值来指定增量值。但是你正在使用正值,所以你得到绝对的时间。显然,如果你使用增量值,你就会领先,因为那是你真正想要的。使用TimeSpan.Ticks属性。
使用绝对时间是可以的,但是当你在罗马时,你必须像罗马人那样行事,操作系统的时钟在UTC上运行。当地时间仅适用于人类。来自MSDN文章的SetWaitableTimer:
将定时器的状态设置为发信号的时间,以100纳秒的间隔。使用FILETIME结构描述的格式。正值表示绝对时间。 确保使用基于UTC的绝对时间,因为系统在内部使用基于UTC的时间。负值表示相对时间。实际的计时器精度取决于硬件的功能。有关基于UTC的时间的更多信息,请参阅系统时间。
您必须使用DateTime.UtcNow
答案 1 :(得分:2)
为什么要为计时器创建新线程?包含WaitableTimer
的整个计时器点是您创建计时器对象并以异步方式通知您(通过在单独的线程上调用回调函数)。当计时器周期结束时。在这方面,WaitableTimer
与.NET计时器对象System.Threading.Timer
没有什么不同。在WaitableTimer
的情况下,您可以让它发出事件信号,这是一件很方便的事情。
几年前,我写了一篇关于使用C#中的WaitableTimer
的文章。不幸的是,托管该文章的网站已不复存在。代码可以通过http://mischel.com/pubs/waitabletimer.zip。
您可能希望查看Thread Timer Example,其中显示了通常如何使用计时器。 WaitableTimer
的机制略有不同,但概念是相同的。