在開發全螢幕遊戲、多媒體播放器或自動化測試工具時,常需要以程式碼動態調整顯示器的解析度與重新率。.NET Framework 本身未提供直接的 Managed API,必須透過 P/Invoke 呼叫 user32.dll 中的 ChangeDisplaySettings 函式。本文完整示範如何定義結構體、匯入非受控函式,並安全地套用新的顯示模式。
DEVMODE 結構體包含顯示裝置的詳細設定,欄位順序與大小必須與 Win32 API 完全一致,否則會導致呼叫失敗或系統不穩定。
using System;
using System.Runtime.InteropServices;
public class DisplaySettings
{
// 螢幕旋轉枚舉
public enum DMDO : short
{
DEFAULT = 0,
D90 = 1,
D180 = 2,
D270 = 3
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct DEVMODE
{
// 必要旗標常數
public const int DM_DISPLAYFREQUENCY = 0x400000;
public const int DM_PELSWIDTH = 0x80000;
public const int DM_PELSHEIGHT = 0x100000;
private const int CCHDEVICENAME = 32;
private const int CCHFORMNAME = 32;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHDEVICENAME)]
public string dmDeviceName;
public short dmSpecVersion;
public short dmDriverVersion;
public short dmSize;
public short dmDriverExtra;
public int dmFields;
public int dmPositionX;
public int dmPositionY;
public DMDO dmDisplayOrientation;
public int dmDisplayFixedOutput;
public short dmColor;
public short dmDuplex;
public short dmYResolution;
public short dmTTOption;
public short dmCollate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHFORMNAME)]
public string dmFormName;
public short dmLogPixels;
public int dmBitsPerPel;
public int dmPelsWidth;
public int dmPelsHeight;
public int dmDisplayFlags;
public int dmDisplayFrequency;
public int dmICMMethod;
public int dmICMIntent;
public int dmMediaType;
public int dmDitherType;
public int dmReserved1;
public int dmReserved2;
public int dmPanningWidth;
public int dmPanningHeight;
}
// 匯入 user32.dll
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int ChangeDisplaySettings(ref DEVMODE lpDevMode, int dwFlags);
// 回傳碼常數
private const int DISP_CHANGE_SUCCESSFUL = 0;
private const int DISP_CHANGE_RESTART = 1;
private const int DISP_CHANGE_FAILED = -1;
private const int DISP_CHANGE_BADMODE = -2;
// ... 其他錯誤碼略
}
以下方法將螢幕設定為 1024×768、85Hz。實務上建議先呼叫 EnumDisplaySettings 列舉支援的模式,確認目標模式存在後再套用。
public static bool SetResolution(int width, int height, int frequency)
{
DEVMODE dm = new DEVMODE();
dm.dmSize = (short)Marshal.SizeOf(typeof(DEVMODE));
dm.dmPelsWidth = width;
dm.dmPelsHeight = height;
dm.dmDisplayFrequency = frequency;
dm.dmFields = DEVMODE.DM_PELSWIDTH | DEVMODE.DM_PELSHEIGHT | DEVMODE.DM_DISPLAYFREQUENCY;
int result = ChangeDisplaySettings(ref dm, 0); // 0 = CDS_UPDATEREGISTRY | CDS_NORESET
switch (result)
{
case DISP_CHANGE_SUCCESSFUL:
Console.WriteLine("顯示模式變更成功。");
return true;
case DISP_CHANGE_RESTART:
Console.WriteLine("需重新啟動電腦才能生效。");
return true;
default:
Console.WriteLine($"變更失敗,錯誤碼:{result}");
return false;
}
}
CDS_TEST (0x00000002) 可先驗證模式是否支援,不實際套用。CDS_UPDATEREGISTRY (0x00000001) 將設定寫入登錄檔,重開機後保持。ChangeDisplaySettingsEx 指定裝置名稱(dmDeviceName)。requireAdministrator。Marshal.GetLastWin32Error() 取得詳細錯誤資訊。class Program
{
static void Main()
{
// 設定為 1920x1080 @ 60Hz
bool ok = DisplaySettings.SetResolution(1920, 1080, 60);
if (!ok) Console.WriteLine("請確認目標解析度/重新率為顯示器支援模式。");
Console.ReadKey();
}
}
透過 P/Invoke 呼叫 ChangeDisplaySettings 是 C# 控制顯示模式的標準做法。關鍵在於正確定義 DEVMODE 結構體、妥善處理旗標與錯誤碼,並在部署前於目標硬體充分測試,確保使用者體驗不受干擾。