Controlling Windows SystemTime on local machine
A colleague of me find out that we had to make use of the API-call "SetSystemTime" (library kernel32.dll) to programmatically set the local SystemTime. Because all computers on the network are synchronized automatically by a network time server, we had also to be able to stop (and start) the local "Windows Time" Service in our code. Otherwise it would be possible that during the unit-tests the client might have been resynchronized with the network time server. The purpose of the Windows Time Service is to ensure that all computers in an organization use a common time.
I created a little test-project to work this out ...
Basically, the following steps are executed :
- Display current SystemTime
- Stop "Windows Time" Service
- Set new SystemTime (January 1, 2006, 00:00)
- Display current SystemTime
- Start "Windows Time" Service
- Force resync "Windows Time" Service
- Display current SystemTime
- Stop "Windows Time" Service
private void stopWindowsTimeService()
{
//Stop Windows Time
localProcess.StartInfo.FileName = "net";
//Service to stop is "W32Time"
localProcess.StartInfo.Arguments = "stop W32Time";
localProcess.Start();
//Wait for process to exit
localProcess.WaitForExit();
//Capture output in streamreader and sent it to screen
streamReader = localProcess.StandardOutput;
this.txtOutput.Text += streamReader.ReadToEnd();
this.Refresh();
}
In the constructor of my Form I created a new process and I've set some properties for this process.
//create new System.Diagnostics.Process
localProcess = new Process();
//do not show command-window
localProcess.StartInfo.CreateNoWindow = true;
//Redirect output to capture output with streamreader
localProcess.StartInfo.UseShellExecute = false;
localProcess.StartInfo.RedirectStandardOutput = true;
- Set new SystemTime
[DllImport("kernel32.dll")]
private static extern bool SetSystemTime(ref SystemTime systemTime);
Use namespace System.RunTime.InteropServices for DllImport-attribute. This will indicate that the method is exposed by an unmanaged dynamic-link library (kernel32.dll) as a static entry point. You will also need the SystemTime structure to pass the new SystemTime by reference. The new SystemTime will be set to January 1, 2006 00:00.
private struct SystemTime
{
public short Year;
public short Month;
public short DayOfWeek;
public short Day;
public short Hour;
public short Minute;
public short Second;
public short Milliseconds;
}
//set new SystemDateTime
DateTime tempDateTime = new DateTime(2006, 1, 1, 0, 0, 0).ToUniversalTime();
SystemTime systemTime = new SystemTime();
systemTime.Year = Convert.ToInt16(tempDateTime.Year);
systemTime.Month = Convert.ToInt16(tempDateTime.Month);
systemTime.Day = Convert.ToInt16(tempDateTime.Day);
systemTime.Hour = Convert.ToInt16(tempDateTime.Hour);
systemTime.Minute = Convert.ToInt16(tempDateTime.Minute);
systemTime.Second = Convert.ToInt16(tempDateTime.Second);
//set new dateTime
SetSystemTime(ref systemTime);
Note that the method-call expects a DateTime in UTC (coordinated universal time).
- Start "Windows Time" Service and force resync
private void startWindowsTimeService()
{
//start Windows Time Process
localProcess.StartInfo.FileName = "net";
//Service to start is "W32Time"
localProcess.StartInfo.Arguments = "start W32Time";
localProcess.Start();
//Wait for process to exit
localProcess.WaitForExit();
//Capture output in streamreader and sent it to screen
streamReader = localProcess.StandardOutput;
this.txtOutput.Text += streamReader.ReadToEnd();
this.Refresh();
//resync Windows Time
localProcess.StartInfo.FileName = "w32tm";
localProcess.StartInfo.Arguments = @" /resync";
localProcess.Start();
localProcess.WaitForExit();
//Capture output in streamreader and sent it to screen
streamReader = localProcess.StandardOutput;
this.txtOutput.Text += streamReader.ReadToEnd();
this.Refresh();
}
The resync-option tells the (local) computer that it should resynchronize its clock as soon as possible. If no computer is specified, the local computer will resynchronize. w32tm.exe is the executable to display/manage settings of the Windows Time Service.
I noticed another thing while working on this project. If you have set the local SystemTime to a date in the future and you've build a solution/project on this "future" date, then you should force a rebuild of your solution/project when returning to the current SystemTime because otherwise Visual studio .NET 2003 will make use of the latest dlls (those that have been built in the "future"). I took me a while to see this : I was sure that I've changed the source code, but nothing was changed while running the app ... Grrr. Working on a future date can also make your Outlook "reminders" pop-up or warn you that your password will expire and so on. Be sure to resync your local SystemTime if you want to stay out of trouble!