GetSystemMetricsで画面サイズやシステム情報を取得する
デスクトップアプリケーションでは、ウィンドウの初期位置、描画領域、入力座標の変換などに画面サイズやシステムの表示指標が必要です。GetSystemMetricsは、これらの値をWindowsから簡潔に取得できるWin32 APIです。luidな显示器構成やDPI設定にも対応しています。
このAPIで得られる値は、対象とする画面と座標系によって意味が変わります。主画面だけを対象にするのか、仮想デスクトップ全体coveringのか、接続中の各モニターを個別に扱うのか_EXTENSIONを分ける必要があります。また、DPIirtualizationの影響も確認 ressent합니다。
基本仕様と返り値の考え方
GetSystemMetricsはuser32.dllから提供され、int型の指標番号を1つ受け取り、UINT型の値を返します。画面寸法を取得する代表的な呼び出しは、GetSystemMetrics(SM_CXSCREEN)とGetSystemMetrics(SM_CYSCREEN)です。
| 取得目的 | 主な手段 | 返り値 | 適用範囲 |
|---|---|---|---|
| 主画面の寸法 | GetSystemMetrics |
ピクセル数 | プライマリモニター |
| 仮想デスクトップ全体 | GetSystemMetrics |
幅、高さ、原点座標 | 全モニターを覆う領域 |
| 各モニターの領域 | EnumDisplayMonitorsとGetMonitorInfo |
MONITORINFO |
モニターごとの矩形領域 |
| 解像度や更新周波数 | EnumDisplaySettingsEx |
DEVMODE |
表示モードの詳細 |
| DPI依存の指標 | GetSystemMetricsForDpi |
UINT |
指定したDPIでの値 |
関数が失敗した場合は0が返ります。ただし、接続ptide数やリモート接続状態など、正常値として0もある指標では、ゼロを必ずエラーと判定できません。画面の幅と高さは正の値になるはずなので、この用途では個別の検証が必要です。
また、このAPIは画面関連のシステム指標に限定されません。プロセッサーの種類にはGetNativeSystemInfo、物理メモリーにはGlobalMemoryStatusExなど、対象に応じて別の関数を選ぶ必要があります。OSバージョンやドライブ情報まで一括取得する汎用的な接口ではありません。
画面寸法と複数显示器の扱い
SM_CXSCREENとSM_CYSCREENは、プライマリーモニターの幅と高さをピクセル単位で返します。この値は全画面表示やメインウィンドウの初期サイズを決めるときに使えます。ただし、 monitorsが複数ある場合は、主画面以外の寸法を表しているわけではありません。
複数モニターをまとめて扱うには、SM_XVIRTUALSCREEN、SM_YVIRTUALSCREEN、SM_CXVIRTUALSCREEN、SM_CYVIRTUALSCREENを使います。仮想デスクトップは、全モニターを覆う矩形領域として定義され、原点には負の値が来る場合があります。左側に別のモニター).__配置されている場合、主画面の左上をゼロとして扱うと誤った位置关系になります。
接続中のモニターを個別に処理するなら、EnumDisplayMonitorsで列挙し、各ハンドルに対してGetMonitorInfoを呼びます。MONITORINFO.rcMonitorはモニター全体の領域、rcWorkはタスクバーなどを除いた作業領域です。表示频率やcolour depthも必要な場合は、EnumDisplaySettingsExでDEVMODEを取得します。
DPI対応と座標系の確認
高DPI環境では、画面上のピクセル数と、アプリケーションが扱う論理座標が一致しないことがあります。プロセスのDPI認識状態が不知道自己の場合、Windowsが一部の寸法を仮想化して返します。そのため、GetSystemMetricsの戻り値をそのまま物理パネルの解像度とみなすと、レイアウトがずれる原因になります。
-per-monitor DPI awarenessを使う場合は、ウィンドウ作成前にSetProcessDpiAwarenessContextへDPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2を指定します。すでにoho awarenessが固定されている場合、この設定は失敗するため、実行時ではなくアプリケーションの起動olicyとして設計する必要があります。
DPI依存する指標を明示的なDPIで求めたい場合は、GetSystemMetricsForDpiを利用します。この関数はWindows 10 1607以降で提供され、GetSystemMetrics Unlike, '_DPI awarenessを前提とした値' を返せます。旧OSをrock重要的是、動的リンクpectionや代替手段を用意します。
実装例と補助APIの組み合わせ
最小限の実装では、主画面の寸法、仮想デスクトップの寸法と原点、モニター数を取得します。
#include <windows.h>
#include <iostream>
int main()
{
const int primaryWidth =
static_cast<int>(::GetSystemMetrics(SM_CXSCREEN));
const int primaryHeight =
static_cast<int>(::GetSystemMetrics(SM_CYSCREEN));
const int virtualLeft =
static_cast<int>(::GetSystemMetrics(SM_XVIRTUALSCREEN));
const int virtualTop =
static_cast<int>(::GetSystemMetrics(SM_YVIRTUALSCREEN));
const int virtualWidth =
static_cast<int>(::GetSystemMetrics(SM_CXVIRTUALSCREEN));
const int virtualHeight =
static_cast<int>(::GetSystemMetrics(SM_CYVIRTUALSCREEN));
const int monitorCount =
static_cast<int>(::GetSystemMetrics(SM_CMONITORS));
if (primaryWidth <= 0 || primaryHeight <= 0 ||
virtualWidth <= 0 || virtualHeight <= 0) {
std::cerr << "画面寸法を正しく取得できませんでした\n";
return 1;
}
std::cout << "主画面: " << primaryWidth << " x " << primaryHeight << '\n';
std::cout << "仮想画面: " << virtualWidth << " x " << virtualHeight
<< "、原点: " << virtualLeft << ", " << virtualTop << '\n';
std::cout << "モニター数: " << monitorCount << '\n';
return 0;
}
この例は画面寸法の取得成功を個別に検証していますが、 valide情報の誤判定を防ぐには、指標ごとに意味を確認する必要があります。複数monitorごとの配置や作業領域が必要なら、前述のEnumDisplayMonitorsとGetMonitorInfoへ置き換えます。画面取得そのものにCOMの初期化は必要ありません。
同じ処理でWMI Automationなどを使う线程では、COM初期化と終了 に従い、必要な並行モデルで初期化します。初期化に成功した回数だけCoUninitializeで後始末し、既存の初期化状態と矛盾するモデルを指定しないことが重要です。
用途別の選択と検証項目
画面寸法の取得方法は、目的によって決まります。主画面だけを基礎にするか、仮想デスクトップを=zangeとして扱うか、各monitorの物理領域を列挙するか選ぶと、使う指標と追加APIが明確になります。
- メインウィンドウの初期サイズだけが必要なら、
SM_CXSCREENとSM_CYSCREENを使用します。 - 全モニターを覆う作業領域が必要なら、仮想画面の原点、幅、高さを一緒に取得します。
- taskbarを除いた各monitorの領域が必要なら、
EnumDisplayMonitorsとGetMonitorInfoを併用します。 - DPIに応じてアイコンやUI寸法を評価するなら、
GetSystemMetricsForDpiとDPI awarenessの設定を組み合わせます。 - 垂直解像度、refresh rate、colour depthが必要なら、
GetSystemMetricsではなくEnumDisplaySettingsExを使います。 - 戻り値がゼロの可能性のある指標では、monitor countなど値の意味に応じてエラー判定を分けます。
実装後の検証では、primary monitorだけ接続した環境、左右や上下にmonitorを追加した環境、異なるDPI設定で実行します。monitorの追加・解除やDPI変更を検知する処理が必要なら、WM_DISPLAYCHANGEやWM_DPICHANGEDも合わせて扱います。関連仕様はWindows API一覧で確認できます。
実務上の要点は、主画面、仮想デスクトップ、個別monitorの寸法と座標を混同しないことです。取得対象と座標系を先に決め、それに合わせてDPI状態を固定すれば、画面構成が変わってもウィンドウ位置や描画領域を安定して扱えます。