Windows多屏開發小記

吳尼瑪發表於2017-12-19

這些天研究了下Windows系統下的多屏開發,這裡做一個小分享。

Windows系統下多屏模式原理

  • 微軟從Windows98後的作業系統就提供了多屏模式,並且在Windows7版本後微軟對多屏模式從效能和顯示解析度上都做了很大的改善。不考慮硬體的限制,Windows7最多可以支援12個輔助顯示卡,而有些顯示卡是可以支援多個顯示器的。多屏模式簡單理解就是一臺PC上可以安裝多個顯示卡並且接上多個顯示器,然後將這些顯示器的顯示區域形成一個大的虛擬桌面。所有顯示器中可以設定一個主顯示器,那麼Windows的系統工作列就會顯示在主顯示器的底部。

  • Windows系統下多顯示器有提供了3個模式

    • 擴充套件顯示:這種模式是最常見的。就是將多個顯示器的顯示區域組合一個虛擬桌面。在這個虛擬桌面中所有程式視窗都可以任意拖動,可以將程式視窗從一個顯示區域拖到另一個顯示區域。
    • 複製顯示:這種模式下所有連線上的顯示器都會顯示住顯示器的桌面。一般會在像多人演示培訓或者是監視遠端螢幕這樣的場景下使用。
    • 多重獨立顯示:這種模式必須要多塊獨立顯示卡,一塊顯示卡多個介面是不行的。在這種模式下,應用程式訪問的顯示器並不屬於Windows的虛擬桌面。這種模式不支援GDI+或者OpenGL,而且電腦重啟後可能會被改為桌面擴充套件模式。
    // 建立方式是通過在應用程式中呼叫CreateDC。
    hdc = CreateDC(lpszDisplayName, NULL, NULL, lpDevMode);
    複製程式碼

    假設系統的第二個顯示器是一個高解析度的大尺寸顯示器,我們可以把它用CAD應用程式的專用顯示。通過在CAD應用程式中呼叫新的Windows API,我們可以藉助GDI在上面畫圖。獨立顯示器的顯示區域沒有桌面上的任何物件(工作列和快捷方式),它與Windows桌面是獨立的。這可以避免Windows桌面對應用程式輸出的任何干擾,我們也不用擔心會在無意中把其它的視窗拽到獨立顯示的顯示區域中,這種方式就好像為應用程式提供了一個專用的顯示器。

Windows虛擬桌面及其座標

  • 要進行Windows系統下的多屏開發首先需要理解的就是Windows的虛擬桌面以及它的座標系統。在單顯示器系統中,實際Windows桌面的形狀和大小與顯示器是相同的。在多顯示器模式下,每一個顯示器實際上是一個大虛擬桌面的一個“子視窗”。我們可以通過控制皮膚中的顯示器屬性對每一個顯示器的顯示區域的大小(解析度)和相對位置進行調整,所有這些顯示區域互相連線但並不重疊。
  • 主顯示器的作用是確定虛擬桌面的座標。不管主顯示器的位置如何,它的顯示區域的左上角的座標定為虛擬座標的零點(0,0),右下角的座標是(X-1,Y-1)(假設主顯示器的解析度為X×Y),其餘顯示區域的座標由它和主顯示器的相對位置決定。
  • 因為虛擬桌面中的座標系統必須是連續的,因此第二個顯示區域的座標是主顯示器的顯示區域的繼續。假設兩個顯示器都使用1920×1080的解析度,並且第二個顯示器位於第一個顯示器(主顯示器)的正右方,則第二個顯示區域的座標是從(1920,0)到(3839,1079)。

主顯示器座標

副顯示器座標

Windows多屏系統API

  • 微軟為多屏模式提供了一些系統的API。一種重要的函式和資料結構可以參見MSDN
    • EnumDisplayMonitors():該函式遍歷當前使用者系統中所包含的顯示器。應用程式可以通過回撥函式獲取當前使用者系統中所擁有的顯示器一些基本資訊。
    • GetMonitorInfo():獲取指定顯示器的相關資訊。
    • MonitorEnumProc():EnumDisplayMonitors()的回撥函式。
    • MonitorFromPoint()、MonitorFromRect()、MonitorFromWindow():獲取指定點、矩形、視窗所在的顯示器控制程式碼。
    • MONITORINFOMONITORINFOEX這兩個結構中儲存著相應顯示器的相關資訊,如座標、是否為主顯示器等。

Show Code

  • 一個簡單的自動獲取系統各個顯示器資訊的類
struct Monitors
{
    std::vector<MONITORINFO>   monitorinfos;

    static BOOL CALLBACK MonitorEnum(HMONITOR hMon,HDC hdc,LPRECT lprcMonitor,LPARAM pData)
    {
        MONITORINFO iMonitor;
        iMonitor.cbSize = sizeof(MONITORINFO);
        GetMonitorInfo(hMon, &iMonitor);
        
        Monitors* pThis = reinterpret_cast<Monitors*>(pData);
        pThis->monitorinfos.push_back(iMonitor);
        return TRUE;
    }

    Monitors()
    {
        EnumDisplayMonitors(0, 0, MonitorEnum, (LPARAM)this);
    }
};

// 用法
Monitors monitors;
cout << "You have " << monitors.monitorinfos.size() << " monitors connected.";
複製程式碼

相關文章