views:

390

answers:

3

Hi, I'm having a hard time getting SetSystemTime working in my C# code. SetSystemtime is a kernel32.dll function. I'm using P/invoke (interop) to call it. SetSystemtime returns false and the error is "Invalid Parameter". I've posted the code below. I stress that GetSystemTime works just fine. I've tested this on Vista and Windows 7. Based on some newsgroup postings I've seen I have turned off UAC. No difference. I have done some searching for this problem. I found this link: http://groups.google.com.tw/group/microsoft.public.dotnet.framework.interop/browse_thread/thread/805fa8603b00c267

where the problem is reported but no resolution seems to be found. Notice that UAC is also mentioned but I'm not sure this is the problem. Also notice that this gentleman gets no actual Win32Error.

  1. Can someone try my code on XP?
  2. Can someone tell me what I'm doing wrong and how to fix it. If the answer is to somehow change permission settings programatically, I'd need an example. I would have thought turning off UAC should cover that though.
  3. I'm not required to use this particular way (SetSystemTime). I'm just trying to introduce some "clock drift" to stress test something. If there's another way to do it, please tell me. Frankly, I'm surprised I need to use Interop to change the system time. I would have thought there is a .NET method.

Thank you very much for any help or ideas. Andrew

Code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace SystemTimeInteropTest
{
    class Program
    {
        #region ClockDriftSetup
        [StructLayout(LayoutKind.Sequential)]
        public struct SystemTime
        {
            [MarshalAs(UnmanagedType.U2)]
            public short Year;
            [MarshalAs(UnmanagedType.U2)]
            public short Month;
            [MarshalAs(UnmanagedType.U2)]
            public short DayOfWeek;
            [MarshalAs(UnmanagedType.U2)]
            public short Day;
            [MarshalAs(UnmanagedType.U2)]
            public short Hour;
            [MarshalAs(UnmanagedType.U2)]
            public short Minute;
            [MarshalAs(UnmanagedType.U2)]
            public short Second;
            [MarshalAs(UnmanagedType.U2)]
            public short Milliseconds;
        }

        [DllImport("kernel32.dll")]
        public static extern void GetLocalTime(
        out SystemTime systemTime);

        [DllImport("kernel32.dll")]
        public static extern void GetSystemTime(
        out SystemTime systemTime);

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool SetSystemTime(
        ref SystemTime systemTime);

        //[DllImport("kernel32.dll", SetLastError = true)]
        //public static extern bool SetLocalTime(
        //ref SystemTime systemTime);
        [System.Runtime.InteropServices.DllImportAttribute("kernel32.dll", EntryPoint = "SetLocalTime")]
        [return: System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)]
        public static extern bool SetLocalTime([InAttribute()] ref SystemTime lpSystemTime);



        #endregion ClockDriftSetup
        static void Main(string[] args)
        {
            try
            {
            SystemTime sysTime;
             GetSystemTime(out sysTime);
                sysTime.Milliseconds += (short)80;
                sysTime.Second += (short)3000;
                bool bResult = SetSystemTime(ref sysTime);

                if (bResult == false)
                    throw new System.ComponentModel.Win32Exception();
            }
            catch (Exception ex)
            {
                Console.WriteLine("Drift Error: " + ex.Message);
            }
        }
    }
}
A: 

You can't just add 3000 to the seconds field. You need to specify a number of seconds between 0 and 60. You need to adjust the seconds, minutes, hours, etc fields so that they are all within a valid range.

Edit The simplest way would actually be to call SystemTimeToFileTime then add numSeconds * 10000 to the value it gives back, and then call FileTimeToSystemTime to convert back to a SystemTime.

Dean Harding
codeka, Thanks for the response. I did not find it necessary to modify the code above (see Gabe's answer). That is, I added 3600 seconds and the clock seems to advance by 1 hour. I'm not sure if I misunderstood you...
Dave
+2  A: 

There is a .Net method to set the time; it's just not available in C# by default. If you add a reference to Microsoft.VisualBasic to your project then you can just write:

Microsoft.VisualBasic.DateAndTime.TimeOfDay = DateTime.Now.AddSeconds(3000).AddMilliseconds(80);
Gabe
gabe, Thank you very much for your answer. That worked for me!
Dave
One more newbie follow up question. At some point after I've successfully advanced the clock (see Gabe's response for how to do it), the clock reverts itself back to the correct time. I'm still gathering data on how long it waits to do this, but my guess is that it's somehow getting updated through the network or something. Anyone know if there is a Windows program/service that is doing this? Is there a way to turn it off for purposes of testing? Or is disconnecting from the network the preferred manner? Many Thanks, Andrew
Dave
Odds are you need to stop the `w32time` service.
Gabe
A: 

Itried your code using XP Pro sp3 it returned an error " invalid parameter" as well

spookytjd