Set time programmatically using C#

前端 未结 5 2249
礼貌的吻别
礼貌的吻别 2020-12-06 18:41

What is the best way to set the time on a remote machine remotely? The machine is running Windows XP and is receiving the new time through a web service call. The goal is

相关标签:
5条回答
  • 2020-12-06 19:07

    This is the Win32 API call for setting system time:

    [StructLayout(LayoutKind.Sequential)] 
    public struct SYSTEMTIME { 
     public short wYear; 
     public short wMonth; 
     public short wDayOfWeek; 
     public short wDay; 
     public short wHour; 
     public short wMinute; 
     public short wSecond; 
     public short wMilliseconds; 
     } 
     [DllImport("kernel32.dll", SetLastError=true)] 
    public static extern bool SetSystemTime(ref SYSTEMTIME theDateTime ); 
    

    I'm not exactly sure how you would get the security worked out such that you could execute that function on the client, though.

    You can get a lot more detail on setting system time at PInvoke.

    0 讨论(0)
  • 2020-12-06 19:08

    Here's the routine I have been using for years to read the DateTime value off our our SQL Server (using file time), convert it to a SYSTEMTIME that is set on the PC.

    This works for PCs and Windows Mobile devices.

    It can be called anytime you happen to be calling your SQL Server.

    public class TimeTool {
    
      private static readonly DateTime NODATE = new DateTime(1900, 1, 1);
    
    #if PocketPC
      [DllImport("coredll.dll")]
    #else
      [DllImport("kernel32.dll")]
    #endif
      static extern bool SetLocalTime([In] ref SYSTEMTIME lpLocalTime);
    
      public struct SYSTEMTIME {
        public short Year, Month, DayOfWeek, Day, Hour, Minute, Second, Millisecond;
        /// <summary>
        /// Convert form System.DateTime
        /// </summary>
        /// <param name="time">Creates System Time from this variable</param>
        public void FromDateTime(DateTime time) {
          Year = (short)time.Year;
          Month = (short)time.Month;
          DayOfWeek = (short)time.DayOfWeek;
          Day = (short)time.Day;
          Hour = (short)time.Hour;
          Minute = (short)time.Minute;
          Second = (short)time.Second;
          Millisecond = (short)time.Millisecond;
        }
    
        public DateTime ToDateTime() {
          return new DateTime(Year, Month, Day, Hour, Minute, Second, Millisecond);
        }
    
        public static DateTime ToDateTime(SYSTEMTIME time) {
          return time.ToDateTime();
        }
      }
    
      // read SQL Time and set time on device
      public static int SyncWithSqlTime(System.Data.SqlClient.SqlConnection con) {
        SYSTEMTIME systemTime = new SYSTEMTIME();
        DateTime sqlTime = NODATE;
        string sql = "SELECT GETDATE() AS [CurrentDateTime]";
        using (System.Data.SqlClient.SqlCommand cmd = new System.Data.SqlClient.SqlCommand(sql, con)) {
          try {
            cmd.Connection.Open();
            System.Data.SqlClient.SqlDataReader r = cmd.ExecuteReader();
            while (r.Read()) {
              if (!r.IsDBNull(0)) {
                sqlTime = (DateTime)r[0];
              }
            }
          } catch (Exception) {
            return -1;
          }
        }
        if (sqlTime != NODATE) {
          systemTime.FromDateTime(sqlTime); // Convert to SYSTEMTIME
          if (SetLocalTime(ref systemTime)) { //Call Win32 API to set time
            return 1;
          }
        }
        return 0;
      }
    
    }
    
    0 讨论(0)
  • 2020-12-06 19:23

    The way to query a network machine for it's system time is NetRemoteTOD.

    Here's code to do it in Delphi (a usage example is posted below).

    Since it relies on Windows API calls, it shouldn't be too different in C#.

    unit TimeHandler;
    
    interface
    
    type
      TTimeHandler = class
      private
        FServerName : widestring;
      public
        constructor Create(servername : widestring);
        function RemoteSystemTime : TDateTime;
        procedure SetLocalSystemTime(settotime : TDateTime);
      end;
    
    implementation
    
    uses
      Windows, SysUtils, Messages;
    
    function NetRemoteTOD(ServerName :PWideChar; var buffer :pointer) : integer; stdcall; external 'netapi32.dll';
    function NetApiBufferFree(buffer : Pointer) : integer; stdcall; external 'netapi32.dll';
    
    type
      //See MSDN documentation on the TIME_OF_DAY_INFO structure.
      PTime_Of_Day_Info = ^TTime_Of_Day_Info;
      TTime_Of_Day_Info = record
        ElapsedDate : integer;
        Milliseconds : integer;
        Hours : integer;
        Minutes : integer;
        Seconds : integer;
        HundredthsOfSeconds : integer;
        TimeZone : LongInt;
        TimeInterval : integer;
        Day : integer;
        Month : integer;
        Year : integer;
        DayOfWeek : integer;
      end;
    
    constructor TTimeHandler.Create(servername: widestring);
    begin
      inherited Create;
      FServerName := servername;
    end;
    
    function TTimeHandler.RemoteSystemTime: TDateTime;
    var
      Buffer : pointer;
      Rek : PTime_Of_Day_Info;
      DateOnly, TimeOnly : TDateTime;
      timezone : integer;
    begin
      //if the call is successful...
      if 0 = NetRemoteTOD(PWideChar(FServerName),Buffer) then begin
        //store the time of day info in our special buffer structure
        Rek := PTime_Of_Day_Info(Buffer);
    
        //windows time is in GMT, so we adjust for our current time zone
        if Rek.TimeZone <> -1 then
          timezone := Rek.TimeZone div 60
        else
          timezone := 0;
    
        //decode the date from integers into TDateTimes
        //assume zero milliseconds
        try
          DateOnly := EncodeDate(Rek.Year,Rek.Month,Rek.Day);
          TimeOnly := EncodeTime(Rek.Hours,Rek.Minutes,Rek.Seconds,0);
        except on e : exception do
          raise Exception.Create(
                                 'Date retrieved from server, but it was invalid!' +
                                 #13#10 +
                                 e.Message
                                );
        end;
    
        //translate the time into a TDateTime
        //apply any time zone adjustment and return the result
        Result := DateOnly + TimeOnly - (timezone / 24);
      end  //if call was successful
      else begin
        raise Exception.Create('Time retrieval failed from "'+FServerName+'"');
      end;
    
      //free the data structure we created
      NetApiBufferFree(Buffer);
    end;
    
    procedure TTimeHandler.SetLocalSystemTime(settotime: TDateTime);
    var
      SystemTime : TSystemTime;
    begin
      DateTimeToSystemTime(settotime,SystemTime);
      SetLocalTime(SystemTime);
      //tell windows that the time changed
      PostMessage(HWND_BROADCAST,WM_TIMECHANGE,0,0);
    end;
    

    And here is the usage example:

    procedure TfrmMain.SynchLocalTimeWithServer;
    var
      tod : TTimeHandler;
    begin
      tod := TTimeHandler.Create(cboServerName.Text);
      try
        tod.SetLocalSystemTime(tod.RemoteSystemTime);
      finally
        FreeAndNil(tod);
      end;  //try-finally
    end;
    
    0 讨论(0)
  • 2020-12-06 19:31

    You could also probably do this in a batch file using some combination of

    TIME
    

    to set the time, and

    net time \\server_name
    

    to retrieve the time from a server.

    0 讨论(0)
  • 2020-12-06 19:34

    I would use Windows built-in internet time abilities. You can set up a time server on your server, have it get time from a 2nd-tier timeserver, and have all your client machines get time from it.

    I've been down the application-setting-system-time road before.

    0 讨论(0)
提交回复
热议问题