在 C# 中直接修改系統時間需要呼叫 Windows 底層 API,因為 .NET Framework 沒有直接提供設定系統時間的 managed 方法。本文完整示範如何使用 P/Invoke 呼叫 kernel32.dll 的 SetSystemTime 函數,並處理時區轉換(系統時間為 UTC)的關鍵細節。
SYSTEMTIME 結構對應 Windows API 的時間格式,必須使用 StructLayout(LayoutKind.Sequential) 確保記憶體配置正確:
[StructLayout(LayoutKind.Sequential)]
public 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;
}
public static class Win32Api
{
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool SetSystemTime(ref SYSTEMTIME st);
[DllImport("kernel32.dll")]
public static extern void GetSystemTime(ref SYSTEMTIME st);
}
SetSystemTime 接收的是 UTC 時間,若直接傳入 DateTime.Now 會導致時間偏移。必須手動轉換:
DateTime localNow = DateTime.Now;
DateTime utcNow = localNow.ToUniversalTime(); // 正確轉換
SYSTEMTIME st = new SYSTEMTIME
{
wYear = (ushort)utcNow.Year,
wMonth = (ushort)utcNow.Month,
wDay = (ushort)utcNow.Day,
wDayOfWeek = (ushort)utcNow.DayOfWeek,
wHour = (ushort)utcNow.Hour,
wMinute = (ushort)utcNow.Minute,
wSecond = (ushort)utcNow.Second,
wMilliseconds = (ushort)utcNow.Millisecond
};
bool success = Win32Api.SetSystemTime(ref st);
if (!success)
{
int error = Marshal.GetLastWin32Error();
throw new System.ComponentModel.Win32Exception(error);
}
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class TimeSyncForm : Form
{
private Button btnSync;
private TextBox txtCurrentTime;
private TextBox txtNewTime;
public TimeSyncForm()
{
InitializeComponent();
}
private void InitializeComponent()
{
this.btnSync = new Button { Text = "同步系統時間", Location = new Point(20, 50), Width = 150 };
this.txtCurrentTime = new TextBox { Location = new Point(20, 20), Width = 300, ReadOnly = true };
this.txtNewTime = new TextBox { Location = new Point(20, 90), Width = 300, ReadOnly = true };
this.btnSync.Click += BtnSync_Click;
this.Controls.AddRange(new Control[] { btnSync, txtCurrentTime, txtNewTime });
this.Text = "系統時間設定工具";
this.Size = new Size(360, 180);
}
private void BtnSync_Click(object sender, EventArgs e)
{
try
{
txtCurrentTime.Text = $"目前系統時間: {DateTime.Now:yyyy/MM/dd HH:mm:ss.fff}";
// 取得網路時間或指定時間(此處示範用目前時間)
DateTime targetTime = DateTime.Now; // 實務上應從 NTP 伺服器取得
DateTime utcTime = targetTime.ToUniversalTime();
SYSTEMTIME st = new SYSTEMTIME
{
wYear = (ushort)utcTime.Year,
wMonth = (ushort)utcTime.Month,
wDay = (ushort)utcTime.Day,
wDayOfWeek = (ushort)utcTime.DayOfWeek,
wHour = (ushort)utcTime.Hour,
wMinute = (ushort)utcTime.Minute,
wSecond = (ushort)utcTime.Second,
wMilliseconds = (ushort)utcTime.Millisecond
};
if (Win32Api.SetSystemTime(ref st))
{
txtNewTime.Text = $"設定成功,新時間: {DateTime.Now:yyyy/MM/dd HH:mm:ss.fff}";
MessageBox.Show("系統時間已更新", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else
{
throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
}
}
catch (Exception ex)
{
MessageBox.Show($"設定失敗: {ex.Message}", "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new TimeSyncForm());
}
}
| 錯誤碼 | 原因 | 解決方式 |
|---|---|---|
| 1314 (ERROR_PRIVILEGE_NOT_HELD) | 權限不足 | 以管理員身分執行、檢查群組原則 |
| 87 (ERROR_INVALID_PARAMETER) | 參數錯誤 | 檢查 SYSTEMTIME 結構欄位是否在有效範圍 |
| 時間偏移 8 小時 | 未轉 UTC | 使用 ToUniversalTime() 轉換 |
透過 P/Invoke 呼叫 `SetSystemTime` 是 C# 修改系統時間的標準做法。核心關鍵在於:正確定義 `SYSTEMTIME` 結構、將本地時間轉為 UTC、以管理員權限執行。實務應用中建議整合 NTP 客戶端功能,打造完整的時間同步解決方案。