Natural Intelligence による記事要点まとめ
・簡単に構築できるマルチモニタ環境は便利である。
・接続されたモニタの台数やモニタごとの解像度を取得できる。
・スクリーン全体のキャプチャもできる。
ソフトウェア開発においても、ドキュメント作成においても、スマホよりはるかに広い画面を使える PC ですが、 それでもなお、2 画面を同時に使えるマルチモニタ環境に慣れてしまうと、もう 1 画面には戻れません。 SSD を使い始めたらもう HDD には戻れない、と同じように。
デスクトップ PC の場合、モニタを 2 台同時に使用するのは簡単です。
モニタが 2 台あれば、
私の環境では、デスクトップ PC の背面の USB に次の製品を接続し、サブモニタとして使うモニタ 2 に接続するのみです。 電源などは必要ありません。
▼ Amazon 広告 ▼
アイ・オー・データ マルチ画面 USB グラフィック アナログ RGB 対応 WXGA+/SXGA対応 USB2.0 接続 USB-RGB2
アイ・オー・データ 外付けグラフィックアダプター アナログ専用モデル USB アナログ RGB 変換 マルチディスプレイ USB-RGB2S
アイ・オー・データ マルチ画面 USB グラフィック DVI-I/アナログ RGB 対応 WUXGA/フル HD 対応 USB2.0 接続 USB-RGB/D2
Amazon の購入履歴を確認したところ、2013 年に購入していました。 もうすぐ 10 年です。 実際に使用しているのは上の広告の一番上のアナログ用で、いまだに壊れずに動いています。
後継機種が発売されており、もう新品は入手しにくそうです。
初期設定も簡単です。 説明書に従って接続し認識されたら、デスクトップの何もないところで右クリックし 「ディスプレイ設定」を選択して、配置などを指定するだけです。
新しいモニタと古いモニタで解像度が異なっていても大丈夫です。 余っているモニタが生き返ります。
ここでは使い方ではなく、すでに設定が済んでいる 2 台のモニタの C++ からの検出や制御について、書いています。
コードは Windows 10、Visual Studio 2015 で作成、確認したものです。
一部
システム SSD を大容量のものに交換する
DELL デスクトップ PC で、システム SSD を大容量のものに交換する手順について書いています。
もちろん OS ごとデータを移動させます。
なお、本サイトのご利用に際しては、必ずプライバシーポリシー(免責事項等)をご参照ください。
投稿 December 15, 2022
Windows で、接続されているモニタの台数を取得するのは簡単です。
int nNum = GetSystemMetrics(SM_CMONITORS);
モニタが 1 台であれば 1 が、2 台であれば 2 が返されます。
GetSystemMetrics 関数は、 システム設定を取得する便利な Windows API です。 この関数に定数 SM_CMONITORS を渡すだけです。 SM_CMONITORS は WinUser.h で値 80 が割り当てられています。 Visual Basic から使用する場合は、80 を渡せばよいことになります。
投稿 December 15, 2022
以下の実験は、メインモニタを左に、サブモニタを右に並べた状態で行っています。
画面解像度を得ようといつものように取得を試みると、メインモニタの解像度が返されました。
int w = GetSystemMetrics(SM_CXSCREEN); int h = GetSystemMetrics(SM_CYSCREEN);
メインモニタが 1920x1080、サブモニタが 1366x768 でこの呼び出しを行うと、1920x1080 が返されました。 まずはこの呼び出しでメインモニタの解像度を取得できました。
次のような呼び出しを行うと、バーチャルスクリーンの解像度を取得できます。 「バーチャル」ですから「仮想」です。
int vw = GetSystemMetrics(SM_CXVIRTUALSCREEN); int vh = GetSystemMetrics(SM_CYVIRTUALSCREEN);
1920x1080 と 1366x768 で運用のとき、3286x1080 が返されました。
横方向に並んでいますので 1920 + 1366 = 3286 でぴったりですが、 縦方向はメインモニタは 1080 ですが、サブモニタは 768 ですので、サブモニタの下部には表示できない領域が含まれています。
サブモニタの解像度を取得できる関数や引数は、なさそうです。
int x = GetSystemMetrics(SM_XVIRTUALSCREEN); int y = GetSystemMetrics(SM_YVIRTUALSCREEN);
この呼び出しで、バーチャルスクリーンの原点の座標を取得できるようです。 左にメインモニタ、右にサブモニタを配置している場合は、x、y とも 0 が返されます。
モニタが左右に配置されている場合、 SM_CXVIRTUALSCREEN で取得したバーチャルスクリーンの横幅は、 SM_CXSCREEN で取得したメインモニタの横幅より大きくなっています。 この場合、メインモニタが左でサブモニタが右なのか、その逆にメインモニタが右でサブモニタが左なのか、判断する必要があります(連続的に使用したい場合)。
バーチャルスクリーンの原点の座標が (0,0) ならサブモニタが右に配置されていると言えます。 しかし x 座標が 0 未満、例えば -1366 なら、サブモニタが左に配置されているということになります。
モニタが上下に配置されている場合、 SM_CXVIRTUALSCREEN で取得したバーチャルスクリーンの横幅は、 SM_CXSCREEN で取得したメインモニタの横幅と一致しているはずです(メインモニタの解像度がサブモニタの解像度より低くない場合)。 この場合には、メインモニタが上でサブモニタが下なのか、その逆にメインモニタが下でサブモニタが上なのか、判断する必要があります。
同様に、バーチャルスクリーンの原点の座標が (0,0) ならサブモニタが下に配置されていると言えます。 しかし y 座標が 0 未満、例えば -768 なら、サブモニタが上に配置されているということになります。
前提条件がない場合は、バーチャルスクリーンの原点の座標の符号をみて判断する、ということになりそうです。
サブモニタの情報を取得するために、サブモニタ上の座標を指定します。
モニタが横に並んでいる場合は、x 座標は、サブモニタが左なら -1、右ならメインモニタの解像度 w を使います。 どちらの場合でも y 座標は 0 です。
モニタが縦に並んでいる場合は、y 座標は、サブモニタが上なら -1、下ならメインモニタの解像度 h を使います。 どちらの場合でも x 座標は 0 です。
サブモニタ上にある座標を POINT 構造体、 あるいは MFC の CPoint クラスに設定します。 そしてそれを MonitorFromPoint 関数 に渡してモニタのハンドル HMONITOR を取得します。
HMONITOR hMonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTOPRIMARY);
取得に失敗すると NULL が返されるようです。
モニタのハンドルを取得できたら、モニタの情報を取得できます。
MONITORINFO mi; mi.cbSize = sizeof(MONITORINFO); GetMonitorInfo(hMonitor, &mi);
GetMonitorInfo 関数 にモニタのハンドルを渡すと、 MONITORINFO 構造体 に情報を設定してくれます。
MONITORINFO 構造体の RECT rcMonitor メンバに、そのモニタの表示位置を仮想画面座標で表した値を得られます。 つまり、そのモニタ、今の場合はサブモニタの解像度は、次のように計算できます。
int w2 = mi.rcMonitor.right - mi.rcMonitor.left; int h2 = mi.rcMonitor.bottom - mi.rcMonitor.top;
サブモニタの原点が (mi.rcMonitor.left, mi.rcMonitor.top) になることに注意が必要です。
サブモニタを縦に並べた場合、バーチャルスクリーンの解像度は 1920 x 1848 となり、 サブモニタが下にあるときの領域は (0, 1080) から 1366x768 となりました。 サブモニタが上にあるときには (0, -768) から 1366x768 となりました。
サブモニタが左にある場合には、バーチャルスクリーンの解像度は 3286x1080 ですが、 サブモニタの領域は (-1366,0) からの 1366x768 です。
なお、「ディスプレイの配置を変更する」画面では、 モニタを横に並べる場合でも、y 座標を上に合わせなくても中間にできたり、下を揃えたりできますので、原点座標は変わってきます。 縦に並べる場合でも同様に、x 座標を左にあわせなくても、右に合わせたり、中間に設定したりもできますので注意します。
投稿 December 15, 2022
ここまでの内容とは特に強い関係はないのですが、バーチャルスクリーン全体のキャプチャをファイルに保存してみました。
まずはバーチャルスクリーンの解像度を取得します。
int w = GetSystemMetrics(SM_CXVIRTUALSCREEN); int h = GetSystemMetrics(SM_CYVIRTUALSCREEN);
続いてデスクトップのウィンドウを取得します。
CWnd* pwndDesktop = CWnd::GetDesktopWindow(); CPaintDC dcDesktop(pwndDesktop);
ここに出てくるクラス、CWnd や CPaintDC は MFC 特有です。
GetDesktopWindow 関数はデスクトップのウィンドウクラスを取得するための関数で、ウィンドウを表すクラス CWnd のポインタが返されます。 Windows API の GetDesktopWindow 関数 を使えば、ウィンドウハンドル HWND を取得できます。
CPaintDC は、描画用 DC、デバイス・コンテキストです。
デスクトップと互換性のある CBitmap を作成します。 ここにスクリーンショットを格納します。
CBitmap bitmap; bitmap.CreateCompatibleBitmap(&dcDesktop, w, h);
メモリ DC を作成して、今作成した互換性のある CBitmap を選択します。 メモリ DC とは、実際のデバイスとは関連付けられていない、仮想の描画面です。
CDC memDC; memDC.CreateCompatibleDC(&dcDesktop); CBitmap* pBmp = memDC.SelectObject(&bitmap);
デスクトップの DC からこのメモリ DC に全体をコピーします。
memDC.BitBlt(0, 0, w, h, &dcDesktop, 0, 0, SRCCOPY); memDC.SelectObject(pBmp);
MFC の BitBlt 関数 により、メモリ DC に、dcDesktop の (0,0) からの画像を、(0,0) から横 w ピクセル、縦 h ピクセルの領域をコピーしています。 Windows API の BitBlt 関数 と同等です。
コピーしたら、もとの CBitmap を選択して戻しておきます。
MFC の CImage クラス を使うと、ファイルへの保存が簡単です。
CImage img; img.Attach((HBITMAP)bitmap.GetSafeHandle()); img.Save(_T("desktop.bmp")); img.Detach();
デスクトップがコピーされた CBitmap の bitmap を CImage に選択(アタッチ)し、 カレントフォルダに desktop.bmp として保存しています。 保存したら関連付けを解除(デタッチ)しておきます。
保存されたファイルを見ると、サブモニタで不足している領域は黒になりました。 memDC.BitBlt でサブモニタの部分だけを切り出せば、サブモニタだけのキャプチャも簡単でしょう。
最後に、もしどうしても MFC が嫌なら、妥協して MFC DLL にして本体と切り離す、というのはいかがでしょうか?
Visual C++/MFC で、サーバーに FTP 接続してファイルをアップロード、ダウンロードするコードを書いています。
Windows のファイルにある 3 種類のタイムスタンプ、作成日時、更新日時とアクセス日時を書き換えられるコードについて、書いています。
古い Visual C++ プロジェクトから新しい環境に移行したとき、_sprintf でエラーが出る場合の回避方法について、書いています。
すでにインストールされている VS 2019 をアンインストールし、VS Community 2022 をインストールしなおしています。
Visual Studio Community 2019 を共存インストールする(デスクトップ)
VS 2019 をメイン デスクトップ機に共存インストールした様子を、書いています。 システムドライブの容量を節約し、データドライブにインストールされるようにしています。
本サイトの新着情報を紹介しています。