Contents

有些时候我们不希望用户在使用我们的软件时修改系统时间,那么怎么检测用户是否修改系统时间呢?Windows会在系统时间修改时发送WM_TIMECHANGE消息,所以可以在C++的WindowProc函数中处理这个消息。如果是C#,可以直接用SystemEvents.TimeChanged事件。这个事件有个bug,就是每次都会被触发两次。

如果还想做的更智能一点,想把时间再改回去,就需要知道用户修改时间前的时间是什么。一个可行的办法是在程序开始运行时记一个开始时间,然后用Stopwatch这个类来掐表算算过去了多长时间。给Windows设置时间需要用到SetSystemTime这个API。

下面是示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
public class Program
{
private static Stopwatch s_Stopwatch;
private static DateTime s_Start;
private static int s_Count = 0;

[DllImport("kernel32.dll")]
private extern static uint SetSystemTime(ref SYSTEMTIME lpSystemTime);


private struct SYSTEMTIME
{
public ushort wYear;
public ushort wMonth;
public ushort wDayOfWeek;
public ushort wDay;
public ushort wHour;
public ushort wMinute;
public ushort wSecond;
public ushort wMilliseconds;
}


static void Main(string[] args)
{
SystemEvents.TimeChanged += (sender, eventArgs) =>
{
if (s_Count == 1)
{
s_Count = 0;
return;
}
var realTime = s_Start + s_Stopwatch.Elapsed;
var newTime = DateTime.Now;


Console.WriteLine(newTime);
Console.WriteLine("Should be:");
Console.WriteLine(realTime);

var utcTime = realTime.ToUniversalTime();

SYSTEMTIME systemtime = new SYSTEMTIME();

systemtime.wYear = (ushort)utcTime.Year;
systemtime.wMonth = (ushort)utcTime.Month;
systemtime.wDay = (ushort)utcTime.Day;
systemtime.wHour = (ushort)utcTime.Hour;
systemtime.wMinute = (ushort)utcTime.Minute;
systemtime.wSecond = (ushort)utcTime.Second;
systemtime.wMilliseconds = (ushort)utcTime.Millisecond;

SetSystemTime(ref systemtime);
Console.WriteLine("Change back!");
Console.WriteLine();

s_Count++;
};


s_Stopwatch = new Stopwatch();
s_Start = DateTime.Now;
s_Stopwatch.Start();

Console.ReadLine();
s_Stopwatch.Stop();
}
}

下面是一个运行结果,可以看到时间改了两次,因为我们的程序改了一次。注意上面代码中的s_Count用来绕过上面提到的触发两次event的bug,如果没有这个话,这段代码会死循环下去。。。因为我们一直再改时间。当然也可以做一个时间的比较,小于某个值我们就认为一样,就不修改了。

1
2
3
4
5
6
7
8
9
9/15/2015 8:01:32 PM
Should be:
9/16/2015 8:01:32 PM
Change back!

9/16/2015 8:01:32 PM
Should be:
9/16/2015 8:01:32 PM
Change back!

两外一个要注意的是如果程序是Windows服务的话,需要起一个隐藏的窗口来处理消息,不然TimeChanged收不到。

如果想直接禁止用户修改时间,可以参见这篇文章How to Allow or Prevent Specific Users and Groups from Changing the Date and Time in Windows。里面提供了两个方法:

  1. 修改Local Security Policy中的Change the system time。
  2. 通过ntrights运行ntrights -U "Administrators" -R SeSystemtimePrivilege来禁止修改时间,运行ntrights -U "Administrators" -R SeSystemtimePrivilege来允许修改时间。
Contents