Plunging into .NET Development

Weblog Pieter Gheysens
Microsoft .NET Development - C# - Enterprise Library - Visual Studio 2005 Team System - Compuware DevPartner - ...
 


Wednesday, April 27

Controlling Windows SystemTime on local machine

For implementing multiple unit-test scenarios, I had to be able to adjust the local Windows SystemTime because some of the methods in our application behave differently according to the local SystemTime.

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 :
  1. Display current SystemTime
  2. Stop "Windows Time" Service
  3. Set new SystemTime (January 1, 2006, 00:00)
  4. Display current SystemTime
  5. Start "Windows Time" Service
  6. Force resync "Windows Time" Service
  7. Display current SystemTime
Here are some code-snippets (formatting of code is best viewed on my blog) of how these steps are performed :
  • 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.

Note that no error-handling has been added in the code.

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!

0 Comments:

Post a Comment

<< Home