timesync_windows.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. //go:build windows
  2. // ============================================================================
  3. // timesync_windows.go — 系统时间同步(Windows 平台)
  4. //
  5. // 启用 SeSystemtimePrivilege 特权后用 SetLocalTime 校准系统时间;
  6. // 直接调用 syscall 声明 user32/kernel32 函数,不引入额外依赖。
  7. // ============================================================================
  8. package main
  9. import (
  10. "fmt"
  11. "syscall"
  12. "time"
  13. "unsafe"
  14. )
  15. // systemTime Windows SYSTEMTIME 结构
  16. type systemTime struct {
  17. Year, Month, DayOfWeek, Day, Hour, Minute, Second, Milliseconds uint16
  18. }
  19. type luid struct {
  20. LowPart uint32
  21. HighPart int32
  22. }
  23. type luidAndAttributes struct {
  24. Luid luid
  25. Attributes uint32
  26. }
  27. type tokenPrivileges struct {
  28. PrivilegeCount uint32
  29. Privileges [1]luidAndAttributes
  30. }
  31. // enablePrivilege 启用当前进程的指定特权(SeSystemtimePrivilege 默认禁用,
  32. // 必须先 AdjustTokenPrivileges 启用,否则 SetLocalTime 会失败)
  33. func enablePrivilege(name string) error {
  34. const (
  35. tokenQuery = 0x0008
  36. tokenAdjustPrivileges = 0x0020
  37. sePrivilegeEnabled = 0x00000002
  38. errNotAllAssigned = syscall.Errno(1300) // ERROR_NOT_ALL_ASSIGNED
  39. )
  40. advapi32 := syscall.NewLazyDLL("advapi32.dll")
  41. procOpenProcessToken := advapi32.NewProc("OpenProcessToken")
  42. procLookupPrivilegeValue := advapi32.NewProc("LookupPrivilegeValueW")
  43. procAdjustTokenPrivileges := advapi32.NewProc("AdjustTokenPrivileges")
  44. currentProc, _ := syscall.GetCurrentProcess()
  45. var token syscall.Handle
  46. ret, _, err := procOpenProcessToken.Call(
  47. uintptr(currentProc),
  48. tokenQuery|tokenAdjustPrivileges,
  49. uintptr(unsafe.Pointer(&token)),
  50. )
  51. if ret == 0 {
  52. return fmt.Errorf("OpenProcessToken: %v", err)
  53. }
  54. defer syscall.CloseHandle(token)
  55. var luidVal luid
  56. namePtr, _ := syscall.UTF16PtrFromString(name)
  57. ret, _, err = procLookupPrivilegeValue.Call(
  58. 0,
  59. uintptr(unsafe.Pointer(namePtr)),
  60. uintptr(unsafe.Pointer(&luidVal)),
  61. )
  62. if ret == 0 {
  63. return fmt.Errorf("LookupPrivilegeValue(%s): %v", name, err)
  64. }
  65. tp := tokenPrivileges{
  66. PrivilegeCount: 1,
  67. Privileges: [1]luidAndAttributes{
  68. {Luid: luidVal, Attributes: sePrivilegeEnabled},
  69. },
  70. }
  71. ret, _, err = procAdjustTokenPrivileges.Call(
  72. uintptr(token), 0,
  73. uintptr(unsafe.Pointer(&tp)), 0, 0, 0,
  74. )
  75. if ret == 0 {
  76. return fmt.Errorf("AdjustTokenPrivileges: %v", err)
  77. }
  78. if err == errNotAllAssigned {
  79. return fmt.Errorf("进程不具备特权 %s(需以管理员身份运行)", name)
  80. }
  81. return nil
  82. }
  83. // setLocalTime 按系统当前时区设置本地时间(需管理员权限)
  84. func setLocalTime(t time.Time) error {
  85. if err := enablePrivilege("SeSystemtimePrivilege"); err != nil {
  86. return err
  87. }
  88. st := systemTime{
  89. Year: uint16(t.Year()),
  90. Month: uint16(t.Month()),
  91. DayOfWeek: uint16(t.Weekday()),
  92. Day: uint16(t.Day()),
  93. Hour: uint16(t.Hour()),
  94. Minute: uint16(t.Minute()),
  95. Second: uint16(t.Second()),
  96. Milliseconds: uint16(t.Nanosecond() / 1e6),
  97. }
  98. ret, _, err := syscall.NewLazyDLL("kernel32.dll").NewProc("SetLocalTime").
  99. Call(uintptr(unsafe.Pointer(&st)))
  100. if ret == 0 {
  101. return fmt.Errorf("SetLocalTime: %v", err)
  102. }
  103. return nil
  104. }