Я пытаюсь установить Windows SystemTime в приложении aС# в Windows. Я реализовал пример, который присутствует на каждом форуме (включая здесь), но он не отвечает. У меня есть частный класс с двумя методами: GetSystemTime() и SetSystemTime(). GetSystemTime работает нормально, но SetSystemTime не устанавливает время, которое я пытаюсь передать из GetNetworkTime(). Я делаю что-то неправильно? Я исследовал, и я не знаю, является ли это проблемой с привилегиями (я зарегистрирован как администратор, это не должно) или, может быть, проблема с форматом даты.
Дело в том: должен ли я видеть изменение в системных часах, когда заканчивается SetSystemTime()? Как я знаю, что он работает хорошо? Потому что я не вижу никаких изменений. Здесь я копирую класс, а также метод GetNetworkTime, который получает фактический час с и NTP-сервер.
public static class SystemTime
{
[DllImport("kernel32.dll", SetLastError = true)]
private extern static void GetSystemTime(ref SYSTEMTIME systime);
[DllImport("kernel32.dll", SetLastError = true)]
private extern static uint SetSystemTime(ref SYSTEMTIME systime);
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;
}
private static void GetTime()
{
// Call the native GetSystemTime method
// with the defined structure.
SYSTEMTIME stime = new SYSTEMTIME();
GetSystemTime(ref stime);
}
public static void SetTime()
{
GetTime();
SYSTEMTIME systime = new SYSTEMTIME();
try
{
DateTime d = Intex.Core.Utils.Common.GetNetworkTime();
systime.wHour = (ushort)d.Year;
systime.wMonth = (ushort)d.Month;
systime.wDayOfWeek = (ushort)d.DayOfWeek;
systime.wDay = (ushort)d.Day;
systime.wHour = (ushort)d.Hour;
systime.wMinute = (ushort)d.Minute;
systime.wSecond = (ushort)d.Second;
systime.wMilliseconds = (ushort)d.Millisecond;
SetSystemTime(ref systime);
GetTime();
}
catch (Exception)
{
GetSystemTime(ref systime);
SetSystemTime(ref systime);
}
}
}
public static DateTime GetNetworkTime()
{
//default Windows time server
string ntpServer = ConfigurationManager.AppSettings["NTPServer"];
// NTP message size - 16 bytes of the digest (RFC 2030)
var ntpData = new byte[48];
//Setting the Leap Indicator, Version Number and Mode values
ntpData[0] = 0x1B; //LI = 0 (no warning), VN = 3 (IPv4 only), Mode = 3 (Client Mode)
var addresses = Dns.GetHostEntry(ntpServer).AddressList;
//The UDP port number assigned to NTP is 123
var ipEndPoint = new IPEndPoint(addresses[0], 123);
//NTP uses UDP
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
socket.Connect(ipEndPoint);
//Stops code hang if NTP is blocked
socket.ReceiveTimeout = 3000;
socket.Send(ntpData);
socket.Receive(ntpData);
socket.Close();
//Offset to get to the "Transmit Timestamp" field (time at which the reply
//departed the server for the client, in 64-bit timestamp format."
const byte serverReplyTime = 40;
//Get the seconds part
ulong intPart = BitConverter.ToUInt32(ntpData, serverReplyTime);
//Get the seconds fraction
ulong fractPart = BitConverter.ToUInt32(ntpData, serverReplyTime + 4);
//Convert From big-endian to little-endian
intPart = SwapEndianness(intPart);
fractPart = SwapEndianness(fractPart);
var milliseconds = (intPart * 1000) + ((fractPart * 1000) / 0x100000000L);
//**UTC** time
var networkDateTime = (new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc)).AddMilliseconds((long)milliseconds);
return networkDateTime.ToLocalTime();
}
static uint SwapEndianness(ulong x)
{
return (uint)(((x & 0x000000ff) << 24) +
((x & 0x0000ff00) << 8) +
((x & 0x00ff0000) >> 8) +
((x & 0xff000000) >> 24));
}
MSDN говорит
Если функция выполнена успешно, возвращаемое значение отличное от нуля. Если функция не работает, возвращаемое значение равно нулю. Чтобы получить расширенную информацию об ошибке, вызовите GetLastError.
См. Http://msdn.microsoft.com/en-us/library/windows/desktop/ms724942%28v=vs.85%29.aspx для всей страницы.
Вы пытались проверить возвращаемое значение и, следовательно, последнюю ошибку?
Вот пример кода, чтобы получить возвращаемое значение и последний код ошибки: (просто замените свой собственный вызов "SetSystemTime")
var ret = SetSystemTime(ref systime);
Console.WriteLine("SetSystemTime return : " + ret);
System.Console.WriteLine("Last error : " + GetLastError());
Не забудьте добавить импорт для GetLastError:
[DllImport("kernel32.dll")]
static extern uint GetLastError();
edit: Я только что попытался, я получаю сообщение об ошибке "ERROR_INVALID_PARAMETER 87 (0x57) Параметр неверен.".
Итак, следуя этому совету, я снова прочитал ваш код:
Вы написали:
systime.wHour = (ushort)d.Year;
вместо:
systime.wYear = (ushort)d.Year;
Исправлено, а затем больше нет ошибок.
Чтобы изменить системное время, вам необходимо иметь следующую привилегию: SeSystemTimePrivilege
Или вы можете запускать приложение как администратор. Тот факт, что вы зарегистрированы как администратор, не изменится, потому что он связан с UAC. Вы можете полностью отключить UAC, но я бы не рекомендовал этого.