自動化測試或特定應用情境下,需以程式方式切換螢幕解析度。Windows 提供 ChangeDisplaySettings API 為標準做法,但本文展示一種替代方案:自動化「顯示屬性」控制面板小程式,模擬使用者操作達成解析度切換。此法可繞過某些驅動程式對 ChangeDisplaySettings 的限制,但相依於 UI 結構,穩定性較低。
CreateProcess 啟動 rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,3 直接開啟「設定」頁籤。WM_KEYDOWN (VK_DOWN/UP) 控制滑桿,讀取文字標籤判斷是否達目標解析度。bool FindMainWindow(HWND& hwnd, PROCESS_INFORMATION& pi)
{
STARTUPINFO si = { sizeof(si) };
if (!CreateProcess(NULL,
_T("rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,3"),
NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
return false;
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
DWORD dwPid = pi.dwProcessId;
for (int retry = 0; retry < 20; retry++)
{
Sleep(1000);
for (hwnd = GetWindow(GetDesktopWindow(), GW_CHILD); hwnd; hwnd = GetWindow(hwnd, GW_HWNDNEXT))
{
DWORD pid; GetWindowThreadProcessId(hwnd, &pid);
if (pid == dwPid && GetWindow(hwnd, GW_CHILD))
return true;
}
}
return false;
}
bool FindChildWindow(HWND hwndParent, int index, HWND& hwndChild)
{
if (!hwndParent || index < 1) return false;
int count = 0;
for (hwndChild = GetWindow(hwndParent, GW_CHILD); hwndChild; hwndChild = GetWindow(hwndChild, GW_HWNDNEXT))
if (++count == index) return true;
return false;
}
注意:索引值隨 OS 版本、主題、語系不同而變化。原文 Win2K 使用 11 (滑桿) 和 14 (文字),XP 需改為 8 和 11。強烈不建議依賴固定索引,應改用 GetClassName (如 msctls_trackbar32, Static) 識別控制項。
bool SetSolution(HWND hwndTrackbar, HWND hwndText, const char* targetW, const char* targetH)
{
// 先全部按 DOWN 到最低
for (int i = 0; i < 20; i++) PostMessage(hwndTrackbar, WM_KEYDOWN, VK_DOWN, 0);
Sleep(1000);
char buf[21];
for (int retry = 0; retry < 20; retry++)
{
GetWindowText(hwndText, buf, 20);
char* pW = strstr(buf, targetW);
char* pH = strstr(buf, targetH);
if (pW && pH && pW < pH) return true; // 找到目標解析度字串
PostMessage(hwndTrackbar, WM_KEYDOWN, VK_UP, 0);
Sleep(1000);
}
return false;
}
bool GetOkWindow(HWND hwndMain, PROCESS_INFORMATION pi, HWND& hwndOk)
{
DWORD dwPid = pi.dwProcessId;
for (int retry = 0; retry < 20; retry++)
{
Sleep(1000);
for (hwndOk = GetWindow(GetDesktopWindow(), GW_CHILD); hwndOk; hwndOk = GetWindow(hwndOk, GW_HWNDNEXT))
{
DWORD pid; GetWindowThreadProcessId(hwndOk, &pid);
if (pid == dwPid && GetWindow(hwndOk, GW_CHILD) && hwndOk != hwndMain)
return true; // 找到確認對話框
}
}
return false;
}
void SetScreen(const char* w, const char* h)
{
HWND hwndMain, hwndTrack, hwndText, hwndOk;
PROCESS_INFORMATION pi;
if (!FindMainWindow(hwndMain, pi)) return;
if (!FindChildWindow(GetWindow(hwndMain, GW_CHILD), 11, hwndTrack)) return; // 索引需調整
if (!FindChildWindow(GetWindow(hwndMain, GW_CHILD), 14, hwndText)) return;
if (!SetSolution(hwndTrack, hwndText, w, h)) return;
PostMessage(hwndMain, WM_COMMAND, IDOK, 0); // 點擊確定
if (!GetOkWindow(hwndMain, pi, hwndOk)) return;
PostMessage(hwndOk, WM_COMMAND, IDOK, 0); // 確認套用
if (!GetOkWindow(hwndMain, pi, hwndOk)) return;
PostMessage(hwndOk, WM_COMMAND, IDCANCEL, 0); // 關閉屬性視窗
}
上述 UI 自動化法脆弱且難維護。正規做法為使用 ChangeDisplaySettingsEx:
DEVMODE dm = {0};
dm.dmSize = sizeof(dm);
dm.dmPelsWidth = 1280;
dm.dmPelsHeight = 720;
dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
LONG result = ChangeDisplaySettingsEx(NULL, &dm, NULL, CDS_UPDATEREGISTRY | CDS_TEST, NULL);
if (result == DISP_CHANGE_SUCCESSFUL)
ChangeDisplaySettingsEx(NULL, &dm, NULL, CDS_UPDATEREGISTRY, NULL);
優點:標準、穩定、無視窗依賴、支援多顯示器、可測試模式 (CDS_TEST)。
本文展示的 UI 自動化修改解析度方法僅供教學參考,理解 CreateProcess、視窗列舉、訊息發送等技術。實際專案務必使用 ChangeDisplaySettingsEx API,確保相容性、穩定性與多語系/多顯示器支援。