GetCursorPosで現在のカーソル位置を得る方法
Windowsアプリケーションでマウスポインターの位置を取得するには、Win32 APIのGetCursorPosを使用します。画面上の絶対座標をPOINT構造体へ格納できるため、マウス操作の記録、ツールチップの配置、ドラッグ処理、スクリーンキャプチャ範囲の決定などに利用できます。
GetCursorPosで現在のカーソル位置を得る処理は、呼び出し方自体は簡単です。ただし、取得した座標がクライアント領域基準ではなく、仮想スクリーン全体を基準とする点には注意が必要です。複数ディスプレイやDPIスケーリングを扱う場合、座標系の違いが不具合の原因になります。
関数はカーソルの位置を直接返すのではなく、呼び出し側が用意したPOINT型の変数へ書き込みます。戻り値は成功時に0以外、失敗時に0です。通常のデスクトップアプリケーションでは成功するケースが多いものの、APIの戻り値を確認する習慣を付けておくと、診断しやすいコードになります。
Windows APIの宣言や関連関数を調べるときは、Windows API リファレンスのような資料も役立ちます。ここでは、基本的なC++コードから座標変換、マルチモニター対応、他の入力APIとの使い分けまでを整理します。
基本的な呼び出し方
C++ではWindows.hをインクルードし、POINT構造体を宣言してからGetCursorPosを呼び出します。xには水平方向、yには垂直方向の座標が格納されます。原点は通常、プライマリーモニターの左上です。
#include <windows.h>
#include <iostream>
int main()
{
POINT cursorPosition{};
if (GetCursorPos(&cursorPosition))
{
std::cout << "X: " << cursorPosition.x
<< ", Y: " << cursorPosition.y << std::endl;
}
return 0;
}
戻り値を無視しても動作する場合はありますが、失敗時に未更新の値を処理してしまう可能性があります。POINT cursorPosition{}のように初期化し、成功した場合だけ座標を利用する形が安全です。座標はLONG型なので、一般的な画面サイズでは十分な範囲を持ちます。
画面座標とウィンドウ座標の違い
GetCursorPosが返すのはスクリーン座標です。ウィンドウの左上を原点とするクライアント座標が必要な場合は、ScreenToClientを使って変換します。逆に、クライアント座標を画面座標へ変換する場合はClientToScreenを使用します。
POINT point{};
if (GetCursorPos(&point))
{
HWND windowHandle = FindWindow(nullptr, L"サンプルウィンドウ");
if (windowHandle != nullptr &&
ScreenToClient(windowHandle, &point))
{
// point はクライアント領域基準の座標
}
}
タイトルバーやウィンドウ枠を含むウィンドウ全体の座標が必要なら、対象によってはScreenToClientではなく、ウィンドウ位置を取得して差し引く処理が適しています。座標を比較する前に、「画面基準か、クライアント領域基準か」を決めておくことが重要です。
マウス入力を監視する処理
カーソル位置を一度だけ取得する処理と、一定間隔で監視する処理では設計が異なります。タイマーで定期的にGetCursorPosを呼ぶ場合は、更新間隔、CPU使用率、画面再描画の頻度を調整します。周期処理の考え方は、タイマー処理の解説も参考になります。
次のように、一定間隔で前回値と比較すれば、カーソルが移動したときだけ処理を実行できます。
POINT previous{};
bool hasPrevious = false;
POINT current{};
if (GetCursorPos(¤t))
{
if (!hasPrevious ||
current.x != previous.x ||
current.y != previous.y)
{
// カーソル移動時の処理
previous = current;
hasPrevious = true;
}
}
頻繁なポーリングが不要な場合は、ウィンドウメッセージのWM_MOUSEMOVEを使う方法もあります。ただし、これは対象ウィンドウ上で発生するマウス移動が中心です。画面全体の位置を監視したい場合や、別のアプリケーション上のカーソルを対象にする場合には、GetCursorPosのほうが扱いやすいことがあります。
座標取得で確認するポイント
実装時は、取得した値をそのまま表示位置や描画位置に使えるとは限りません。特に負の座標、DPI仮想化、権限の異なるウィンドウ、カーソルの表示状態を意識する必要があります。
- 複数モニターでは左側や上側の画面が負の座標になる
- スクリーン座標とクライアント座標を混同しない
- APIの戻り値を確認してから
POINTの値を使う - DPI対応設定による座標の見え方を確認する
- 取得処理と描画処理のスレッドを適切に分ける
マウスボタンの状態やカーソルの表示状態も必要なら、GetCursorInfo、GetAsyncKeyState、GetKeyStateなどを組み合わせます。GetCursorPosは位置だけを返す関数なので、クリック状態や現在のカーソル形状まで自動的に取得するものではありません。
複数ディスプレイとDPIへの対応
仮想スクリーンでは、プライマリーモニターの左上を基準に全ディスプレイが配置されます。セカンダリーモニターが左側にある場合、カーソルのx座標は負になります。アプリケーション側で「負の値は異常」と判断すると、マルチモニター環境で表示位置が崩れます。
DPIスケーリングが有効な環境では、物理ピクセルと論理ピクセルの扱いも確認が必要です。プロセスのDPI認識レベル、ウィンドウのDPI、使用しているWindowsのバージョンによって、座標変換の結果が変わる場合があります。
| 目的 | 主なAPI | 座標の基準 | 特徴 |
|---|---|---|---|
| 画面上のカーソル位置 | GetCursorPos |
スクリーン座標 | 画面全体を対象にできる |
| ウィンドウ内のマウス移動 | WM_MOUSEMOVE |
クライアント座標 | メッセージ処理と相性がよい |
| 画面座標から変換 | ScreenToClient |
クライアント座標へ変換 | 対象ウィンドウが必要 |
| クライアント座標から変換 | ClientToScreen |
スクリーン座標へ変換 | ポップアップ配置などに便利 |
| カーソル情報の取得 | GetCursorInfo |
スクリーン座標を含む | 表示状態も確認できる |
座標に基づいてポップアップや独自フォントの表示位置を決める場合は、描画対象のDPI設定も揃える必要があります。フォント作成について確認したい場合は、カスタムフォントの選択にあるGDIオブジェクトの扱いも関連します。
実装時の整理と確認
単純なカーソル位置取得なら、次のコードだけでも十分です。しかし実際のアプリケーションでは、取得した座標を何に使うかによって、変換やエラー処理を追加します。
POINT point{};
if (GetCursorPos(&point))
{
// point.x と point.y はスクリーン座標
}
else
{
DWORD errorCode = GetLastError();
// 必要に応じてエラーを記録する
}
取得結果をログへ保存する場合は、時刻と座標を同時に記録すると、再現性の低いマウス操作を調査しやすくなります。画面端の判定では、GetSystemMetrics(SM_XVIRTUALSCREEN)などで仮想スクリーンの範囲を取得し、負の座標を含めて比較します。
GetCursorPosは、現在位置を知るための低レベルで安定したAPIです。まずスクリーン座標として受け取り、その後に必要なタイミングでScreenToClientなどを使って変換する設計にすると、処理の責任範囲が明確になります。最初の確認として、POINTを初期化し、戻り値を判定するコードを小さなWin32テストプログラムで実行してください。