Mixed-Mode DPI 縮放比例和 DPI 感知 API
Sub-Process DPI 感知支援
SetThreadDpiAwarenessCoNtext 可讓您在單一進程中使用不同的 DPI 縮放模式。 在Windows 10年度更新版之前,視窗的 DPI 感知會系結至整個進程的 DPI 感知模式, (DPI 感知、系統 DPI 感知或Per-Monitor DPI 感知) 。 但現在,使用 SetThreadDpiAwarenessCoNtext,最上層視窗可以有與整個進程 DPI 感知模式不同的 DPI 感知模式。 這也會影響子視窗,因為它們一律會有與其父視窗相同的 DPI 感知模式。
使用 SetThreadDpiAwarenessCoNtext可讓開發人員決定在定義傳統型應用程式的 DPI 特定行為時,想要專注于其開發工作的位置。 例如,應用程式的主要最上層視窗可以依每個監視器調整,而次要最上層視窗可以透過作業系統的點陣圖縮放來調整。
DPI 感知內容
在 SetThreadDpiAwarenessCoNtext 可用性之前,進程在應用程式二進位檔的資訊清單中,或透過在進程初始化期間呼叫 SetProcessDpiAwareness 來定義進程的 DPI 感知。 使用 SetThreadDpiAwarenessCoNtext,每個執行緒可以有個別的 DPI 感知內容,可能不同于整個進程 DPI 感知模式的內容。 執行緒的 DPI 感知內容是以 DPI_AWARENESS_CONTEXT 類型表示,並以下列方式運作:
- 執行緒可以隨時變更其 DPI 感知內容。
- 變更內容之後所做的任何 API 呼叫都會在對應的 DPI 內容 (中執行,而且可能會虛擬化) 。
- 建立視窗時,其 DPI 感知會定義為該時間呼叫執行緒的 DPI 感知。
- 呼叫視窗的視窗程式時,執行緒會自動切換至建立視窗時所使用的 DPI 感知內容。
使用 SetThreadDpiAwarenessCoNtext的常見案例如下:從一個內容 (執行的執行緒開始,例如DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE) 暫時切換至不同的內容 (DPI_AWARENESS_CONTEXT_UNAWARE) 、建立視窗,然後立即將執行緒內容切換回先前的狀態。 建立的視窗會有 DPI_AWARENESS_CONTEXT_UNAWARE的 DPI 內容,而呼叫執行緒的內容將會還原至 DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE ,並接著呼叫 SetThreadDpiAwarenessCoNtext。 在此案例中,與呼叫執行緒相關聯的視窗會以每一監視器內容執行 (,因此不會由作業系統) 進行點陣圖延展,而新建立的視窗則不會感知 DPI (,因此會在顯示集上自動縮放為 > 100% 縮放比例) 。
圖 1 說明主要進程執行緒如何使用 DPI_AWARENESS_CONTEXT_PER_MONITOR執行、將其內容切換為 DPI_AWARENESS_CONTEXT_UNAWARE,以及建立新的視窗。 然後,新建立的視窗會以DPI_AWARENESS_CONTEXT_UNAWARE的 DPI 感知內容 執行,每當 訊息分派給它或從它發出 API 呼叫時。 在建立新視窗之後,主執行緒會立即還原至先前 DPI_AWARENESS_CONTEXT_PER_MONITOR的內容。
新的 DPI 相關 API
除了 在 SetThreadDpiAwarenessCoNtext 所提供的單一程式中支援不同的 DPI 感知模式之外,傳統型應用程式還新增了下列 DPI 特定功能:
-
EnableNonClientDpiScaling
-
注意
Per Monitor V2 DPI 感知模式會自動啟用此功能,因此在應用程式中呼叫EnableNonClientDpiScaling因此不需要。
從視窗的WM_NCCREATE處理常式內呼叫EnableNonClientDpiScaling會導致最上層視窗的非工作區自動調整 DPI。 如果最上層視窗是每一監視器 DPI 感知 (是因為進程本身是每一監視器 DPI 感知,還是因為視窗是在個別監視器 DPI 感知執行緒內建立) ,則每當視窗的 DPI 變更時,標題列、捲軸、功能表和功能表列都會縮放 DPI。
-
請注意,使用此 API 時,子視窗的非用戶端區域,例如子編輯控制項的非用戶端捲軸,將不會自動縮放 DPI。
-
注意
EnableNonClientDpiScaling 必須從 WM_NCCREATE 處理常式呼叫。
-
-
*ForDpi API
數個常用的 API,例如 GetSystemMetrics 沒有 HWND 的任何內容,因此無法針對其傳回值產生適當的 DPI 感知。 從以不同 DPI 感知模式或內容執行的執行緒呼叫這些 API,可能會傳回未針對呼叫執行緒內容調整的值。 GetSystemMetricForDpi、 SystemParametersInfoForDpi和 AdjustWindowRectExForDpi 會執行與其 DPI 未察覺的相同功能,但採用 DPI 作為引數,並從目前線程的內容推斷 DPI 感知。
GetSystemMetricForDpi 和 SystemParametersInfoForDpi 會根據此方程式傳回 DPI 縮放的系統計量值和系統參數值:
GetSystemMetrics (...) @ DPI == GetSystemMetricsForDpi (..., DPI)
因此,呼叫 GetSystemMetrics (或 SystemParametersInfoForDpi) ,在具有特定系統 DPI 值的裝置上執行時,會傳回其 DPI 感知變數 (GetSystemMetricsForDpi 和 SystemParametersInfoForDpi) 相同的 DPI 值。
AdjustWindowRectExForDpi 會採用 HWND,並以 DPI 敏感的方式來計算視窗矩形所需的大小。
-
GetDpiForWindow
-
GetDpiForWindow 會傳回與提供的 HWND 相關聯的 DPI。 答案將取決於 HWND 的 DPI 感知模式:
HWND 的 DPI 感知模式 傳回值 知道 96 系統 系統 DPI Per-Monitor 關聯最上層視窗主要位於的顯示器 DPI
(如果提供子視窗,則會傳回對應最上層父視窗的 DPI)
-
GetDpiForWindow 會傳回與提供的 HWND 相關聯的 DPI。 答案將取決於 HWND 的 DPI 感知模式:
-
GetDpiForSystem
-
呼叫 GetDpiForSystem 比呼叫 GetDC 和GetDeviceCaps 來取得系統 DPI 更有效率。
-
任何可在使用子進程 DPI 感知的應用程式中執行的元件,都不應該假設系統 DPI 在程式的生命週期期間是靜態的。 例如,如果在 DPI_AWARENESS_CONTEXT_UNAWARE 感知內容下執行的執行緒會查詢系統 DPI,則答案會是 96。 不過,如果該相同執行緒切換至 DPI_AWARENESS_CONTEXT_SYSTEM 感知內容,並重新查詢系統 DPI,則答案可能會不同。 若要避免使用快取的 (,而且可能過時) 系統 DPI 值,請使用 GetDpiForSystem 來擷取相對於呼叫執行緒 DPI 感知模式的系統 DPI。
-