首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC 教學

VC 程式設計修改螢幕解析度 (Win32 API 實作)

     編輯:VC 教學

前言

自動化測試或特定應用情境下,需以程式方式切換螢幕解析度。Windows 提供 ChangeDisplaySettings API 為標準做法,但本文展示一種替代方案:自動化「顯示屬性」控制面板小程式,模擬使用者操作達成解析度切換。此法可繞過某些驅動程式對 ChangeDisplaySettings 的限制,但相依於 UI 結構,穩定性較低。

核心策略:啟動 desk.cpl 並操作子視窗

  1. 使用 CreateProcess 啟動 rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,3 直接開啟「設定」頁籤。
  2. 尋找主視窗句柄 (依進程 ID 比對)。
  3. 列舉子視窗定位解析度滑桿 (Trackbar) 與顯示目前解析度的文字標籤。
  4. 發送 WM_KEYDOWN (VK_DOWN/UP) 控制滑桿,讀取文字標籤判斷是否達目標解析度。
  5. 點擊「確定」/「套用」按鈕確認變更。

關鍵函式實作

1. 尋找主視窗

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;
}

2. 定位子視窗 (依 Z-order 索引)

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) 識別控制項。

3. 調整滑桿至目標解析度

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;
}

4. 確認變更

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;
}

5. 主流程串聯

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); // 關閉屬性視窗
}

標準做法對比:ChangeDisplaySettings API

上述 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,確保相容性、穩定性與多語系/多顯示器支援。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved