とある科学の備忘録

とある科学の備忘録

CやPythonのプログラミング、Arduino等を使った電子工作をメインに書いています。また、木製CNCやドローンの自作製作記も更新中です。たまに機械学習とかもやってます。

【C++ Win32API】マウスの処理の全て

サンプルプログラム

#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
ATOM InitApp(HINSTANCE);
BOOL InitInstance(HINSTANCE, int);

char szClassName[] = "mouse";	// ウィンドウクラス
BOOL bStart = FALSE, bSeikai = TRUE;

RECT rec;
HPEN hPen;
HBRUSH hBrush;


int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst,LPSTR lpsCmdLine, int nCmdShow){
	MSG msg;
	BOOL bRet;

	if (!InitApp(hCurInst))
		return FALSE;
	if (!InitInstance(hCurInst, nCmdShow))
		return FALSE;

	while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) {
		if (bRet == -1) {
			break;
		}else {
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}
	return (int)msg.wParam;
}

ATOM InitApp(HINSTANCE hInst){
	WNDCLASSEX wc;
	wc.cbSize = sizeof(WNDCLASSEX);
	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc = WndProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = hInst;
	wc.hIcon = NULL;
	wc.hCursor = (HCURSOR)LoadImage(NULL,MAKEINTRESOURCE(IDC_ARROW),IMAGE_CURSOR,0,0,LR_DEFAULTSIZE | LR_SHARED);
	wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
	wc.lpszMenuName = NULL;
	wc.lpszClassName = (LPCSTR)szClassName;
	wc.hIconSm = NULL;
	return (RegisterClassEx(&wc));
}

BOOL InitInstance(HINSTANCE hInst, int nCmdShow){
	HWND hWnd;
	hWnd = CreateWindow(szClassName,"mouse", WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL, NULL, hInst,NULL);
	if (!hWnd) return FALSE;
	ShowWindow(hWnd, nCmdShow);
	UpdateWindow(hWnd);
	return TRUE;
}


LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp){
	HDC hdc;
	PAINTSTRUCT ps;

	char *mouse_move  = "mouse is moving...";
	char *mouse_left  = "Left Click!!          ";
	char *mouse_right = "Right Click!!         ";
	char mouse_pos[50];

	switch (msg) {
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		//TODO : 描画処理
		EndPaint(hWnd, &ps);
		break;

	case WM_LBUTTONDOWN:
		hdc = GetDC(hWnd);
		TextOut(hdc, 0, 0, mouse_left, (int)strlen(mouse_left));
		ReleaseDC(hWnd, hdc);

		break;
		

	case WM_MOUSEMOVE:
		hdc = GetDC(hWnd);

		GetClientRect(hWnd, &rec);  //ウインドウのサイズを取得
		hBrush = (HBRUSH)CreateSolidBrush(RGB(0, 0, 0));
		SelectObject(hdc, hBrush);
		PatBlt(hdc, 0, 0, rec.right, rec.bottom, PATCOPY);


		POINTS mouse_p = MAKEPOINTS(lp);
		wsprintf(mouse_pos, "x座標 =  %d ,  y座標= %d ", mouse_p.x, mouse_p.y);
		TextOut(hdc, 0, 30, mouse_pos, (int)strlen(mouse_pos));
		TextOut(hdc, 0, 0, mouse_move, (int)strlen(mouse_move));

		hPen = CreatePen(PS_SOLID, 0, RGB(255, 0, 255)); //図形の枠線の色を設定
		SelectObject(hdc, hPen);
		hBrush = (HBRUSH)CreateSolidBrush(RGB(255, 255, 0));
		SelectObject(hdc, hBrush);		//図形内部の塗りつぶし方と色を設定

		Ellipse(hdc, mouse_p.x-10, mouse_p.y-10, mouse_p.x+10, mouse_p.y+10);		//楕円を描く

		ReleaseDC(hWnd, hdc);
		break;
		

	case WM_RBUTTONDOWN:
		hdc = GetDC(hWnd);
		TextOut(hdc, 0, 0, mouse_right, (int)strlen(mouse_right));
		ReleaseDC(hWnd, hdc);
		break;

	case WM_DESTROY:
		PostQuitMessage(0);
		break;

	default:
		return (DefWindowProc(hWnd, msg, wp, lp));
	}
	return 0;
}

実行結果

window上でマウスを動かすと、マウスの位置が表示されます。右クリック、左クリックすると、それに対応した文字が表示されます。
f:id:pythonjacascript:20200321002342j:plain



解説

以下、上のプログラムの解説に入ります。
マウス関連の処理は主に、①WndProc関数でイベントとして処理する
②諸関数(例:)を用いて状態を取得・設定する
です。上のプログラムでは、WM_LBUTTONDOWNなどと書かれている部分が①、
と書かれている部分が②にあたります



マウスを操作したときに発生するイベント

ユーザーがマウスをクリックしたり、ドラッグしたりすると、以下のようなイベントが発生し、プロシージャが呼び出されます。

以下がそのイベントを使った例です

説明
WM_LBUTTONDOWN 左ボタンを押したとき
WM_LBUTTONDBLCLK 左ボタンをダブルクリックしたとき
WM_LBUTTONUP 左ボタンを離した時
WM_RBUTTONDOWN 右ボタンを押したとき
WM_RBUTTONDBLCLK 右ボタンをダブルクリック
WM_RBUTTONUP 右ボタンを離した時
WM_MBUTTONDOWN 中央ボタンを押したとき
WM_MBUTTONUP 中央ボタンを離した時
WM_MOUSEMOVE マウスカーソルが動いたとき
WM_MOUSELEAVE マウスがウィンドウ領域から出た

こんな感じで使います

//ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp){
	switch (msg) {
	case WM_MOUSEMOVE:
		//マウスが動いたときの処理
		break;
		

	case WM_LBUTTONDOWN:
		//左クリックされたときの処理
		break;

	default:
		return (DefWindowProc(hWnd, msg, wp, lp));
	}
	return 0;
}

 

これらのイベントでは、wpには仮想キー(Ctrl、Shift)が押されたかどうかの情報が格納されています。

マウスの現在位置(ウィンドウ内の相対座標)を取得する

WM_LBUTTONDOWN等のマウスイベント発生時の現在位置は、WndProc関数の第3引数lParamで確認することができます

方法1

WM_LBUTTONDOWN message (Winuser.h) - Win32 apps | Microsoft Docs

↑の公式のドキュメントには以下のような方法が載っています
サンプル

#include <windowsx.h>

(省略)

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp){
	(省略)
	short xPos, yPos;

	switch (msg) {
	case WM_LBUTTONDOWN:
	case WM_MOUSEMOVE:
		hdc = GetDC(hWnd);
		xPos = GET_X_LPARAM(lp);
		yPos = GET_Y_LPARAM(lp);
		(省略......)

  

方法2.

サンプルプログラムで紹介したような方法もあります

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp){
	PAINTSTRUCT ps;
	(省略)
	char mouse_pos[50];

	switch (msg) {
	case WM_MOUSEMOVE:
		POINTS mouse_p = MAKEPOINTS(lp);
		wsprintf(mouse_pos, "x座標 =  %d ,  y座標= %d ", mouse_p.x, mouse_p.y);
		(省略......)


MAKEPOINTSマクロを使ってPOINTS構造体にマウスの現在位置を格納します。POINTS構造体にはSHORT型のx, yの2つの変数があります。

typedef struct tagPOINTS {
    SHORT x;    //x座標
    SHORT y;    //y座標
} POINTS, *POINTS;


LONG型のPOINT構造体もあります

typedef struct tagPOINTS {
    LONG x;    //x座標
    LONG y;    //y座標
} POINT, *POINT;


※注意
lParamからマウスのxy座標を取得するのにLOWORDやHIWORDを使用しないで下さい。マルチモニタを使っているとき、xy座標はマイナスになる場合がありますが、LOWORDとHIWORDはそれに対応していません


マウスの現在位置(モニター左上からの絶対座標)を取得する

GetCursorPos関数を使います

POINT mouse_p;
GetCursorPos(&mouse_p);
wsprintf(mouse_pos, "x座標 =  %d ,  y座標= %d ", mouse_p.x, mouse_p.y);

  

マウスの位置をプログラムで変更する

プログラムでマウスカーソルを移動させるには、SetCursorPos関数を使います
ただし、SetCursorPos関数はモニターの左上からの絶対座標です(ウィンドウ内の座標ではありません)。

BOOL SetCursorPos(int X, int Y);

  
ウィンドウ内の座標に移動させるには、以下の様にウィンドウの座標を取得して、動かしたい相対座標を指定する必要があります。

ウィンドウの現在位置を取得するにはGetWindowInfo関数を使います。

BOOL GetWindowInfo(HWND hwnd, PWINDOWINFO pwi);


第二引数のWINDOWINFO構造体にウィンドウ情報が格納されます

typedef struct tagWINDOWINFO {
  DWORD cbSize;
  RECT  rcWindow;
  RECT  rcClient;
  DWORD dwStyle;
  DWORD dwExStyle;
  DWORD dwWindowStatus;
  UINT  cxWindowBorders;
  UINT  cyWindowBorders;
  ATOM  atomWindowType;
  WORD  wCreatorVersion;
} WINDOWINFO, *PWINDOWINFO, *LPWINDOWINFO;

サンプルプログラム
以下のプログラムは、マウスをウィンドウ内の相対座標で見て(xPos, yPos)に移動させます

int xPos_absolute, yPos_absolute;

int xPos = 0;  //移動させたいx座標(ウィンドウ内の相対座標)
int yPos = 100; //移動させたいy座標(ウィンドウ内の相対座標)
WINDOWINFO windowInfo;

//ウィンドウの位置を取得
windowInfo.cbSize = sizeof(WINDOWINFO);
GetWindowInfo(hWnd, &windowInfo);

//マウスの移動先の絶対座標(モニター左上からの座標)
xPos_absolute = xPos + windowInfo.rcWindow.left;
yPos_absolute = yPos + windowInfo.rcWindow.top + 35; //ウィンドウのタイトルバーの分(35px)をプラス

SetCursorPos(xPos_absolute, yPos_absolute);

マウスの特定のボタンが押されているかをチェックする

GetAsyncKeyState関数を使うことでマウスの左右中央のボタンが押されているかを確認できます。

SHORT GetAsyncKeyState(int vKey);


GetAsyncKeyState function (winuser.h) - Win32 apps | Microsoft Docs

サンプル↓

#include <windows.h>
if (GetAsyncKeyState(VK_LBUTTON)) {
	//左ボタンが押されている
}

if (GetAsyncKeyState(VK_RBUTTON)) {
	//右ボタンが押されている
}

if (GetAsyncKeyState(VK_MBUTTON)) {
	//中央ボタンが押されている
}