0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ゼロから作るRPA その1の4 マウス動作記録編(再生)

Last updated at Posted at 2019-10-17

#はじめに
その1の3では、マウスの動きを記録する機能を作りました。今回は、その記録した動作を再現する処理を作ります。

#環境
私の使用している環境を以下に挙げます。環境が異なる場合は適宜読み替えてください。

  • Windows 10 pro
  • Visual Studio 2019 Community Edition

#今回やること

  1. 画面に「再生」ボタンを追加する
  2. ファイルを読み込む
  3. 記録したマウス操作ごとに、マウスを動作させる

#ボタン追加
記録ボタンを追加した時と同様に、再生ボタンをダイアログに追加します。

image.png

次にダイアログのコールバック関数に、再生ボタンクリックのメッセージ処理を追加します。

DaphRPApp.cpp
	case WM_COMMAND:
		switch (LOWORD(wp)) 
		{
		case IDC_REC_BTN:
			StartMouseHook(hDlgWnd);
			return FALSE;
		case IDC_PLAY_BTN:
			return FALSE;
		default:
			return FALSE;
		}

ボタンのIDをIDC_PLAY_BTNとしたので、そのボタンの処理をWM_COMMANDの処理に追加します。

#ファイル読み込み
記録の時に保存したマウスイベントログファイルを読み込みます。

DaphRPApp.cpp
	std::ifstream fin;
	fin.open(L"rec.log", std::ios::in);

ifstreamを使用してファイルを読み取ります。
次に、ファイルを一行ずつ読み込みます。

DaphRPApp.cpp
	while (true)
	{
		char line[128] = { 0 };
		fin >> line;
		if (line[0] == '\0')
			continue;
	}

>>を使って、一行ずつ読み込みます。空行が来たら処理を終了しています。
次に、ファイルの内容を解析します。
ファイルはCSV形式で、以下のフォーマットで保存されています。
メッセージ, X座標, Y座標, タイムスタンプ
そこで、strtok_sを使用して、カンマ区切り文字列をそれぞれ取得します。
取得した後は、それぞれの文字列を数値に変換し保持します。

DaphRPApp.cpp
		char* ctx = nullptr;

		char* token = strtok_s(line, ",", &ctx);
		if (token == nullptr)
			continue;

		int msg = atoi(token);

		token = strtok_s(nullptr, ",", &ctx);
		if (token == nullptr)
			continue;

		int x = atoi(token);

		token = strtok_s(nullptr, ",", &ctx);
		if (token == nullptr)
			continue;

		int y = atoi(token);

		token = strtok_s(nullptr, ",", &ctx);
		if (token == nullptr)
			continue;

		int timestamp = atoi(token);

これで、保存したマウスメッセージの解析は完了です。

#マウス動作
解析した値を使用して、マウスを動かします。
マウスを動かすために、SendInput関数を使用します。

SendInput
UINT WINAPI SendInput(
  _In_ UINT    nInputs,
  _In_ LPINPUT pInputs,
  _In_ int     cbSize
);

SendInputは、キーボードやマウスをプログラムで操作するための関数です。
以下の通り、解析した値を設定して、SendInputを呼び出します。

SendInput
		int time = prevtime == 0 ? 0 : timestamp - prevtime;
		if (time < 0)
			time = 0;

		INPUT inp[1] = { 0 };
		inp[0].type = INPUT_MOUSE;
		inp[0].mi.time = 0;
		inp[0].mi.dwExtraInfo = 0;
		inp[0].mi.dx = x * (65535 / GetSystemMetrics(SM_CXSCREEN));
		inp[0].mi.dy = y * (65535 / GetSystemMetrics(SM_CYSCREEN));
		inp[0].mi.mouseData = 0;
		inp[0].mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;

		switch (msg)
		{
		case WM_LBUTTONDOWN:
			inp[0].mi.dwFlags |= MOUSEEVENTF_LEFTDOWN;
			break;
		case WM_LBUTTONUP:
			inp[0].mi.dwFlags |= MOUSEEVENTF_LEFTUP;
			break;
		case WM_MOUSEMOVE:
			break;
		case WM_MOUSEWHEEL:
			break;
		case WM_MOUSEHWHEEL:
			break;
		case WM_RBUTTONDOWN:
			inp[0].mi.dwFlags |= MOUSEEVENTF_RIGHTUP;
			break;
		case WM_RBUTTONUP:
			inp[0].mi.dwFlags |= MOUSEEVENTF_RIGHTDOWN;
			break;
		}

		Sleep(time);
		SendInput(1, inp, sizeof(INPUT));

INPUT構造体に、INPUT_MOUSEを指定して、マウスを動作させることを指定します。
そのあとは、xやyを設定します。これらの値は、画面の幅や高さを65535までで表す必要があるため、65535をスクリーンの幅と高さで割ったものをかけて算出しています。

ホイールの動作は今のところ未対応なので、処理を入れていません。暇を見つけて拡張します。

#動きを見てみましょう
動画だと、ただ絵を描いているだけに見えるかもしれませんが、記録したものを再生した動画です。本当ですよ~。

#つづく
これで、マウス動作の記録と再生ができるようになりました。UWSCでいうところの、低レベル記録です。
キーボードの記録再生は、同じようにフックして記録して再生すればいいので、簡単に作れると思います。
もし需要があれば書きますので、コメントください。

次回は、OpenCVを使った、画像指定による項目クリックの実装です。
https://qiita.com/yasunari_matsuo/items/1dd10e0379570eef96d0

これまでの記事は以下です。

その1の1
https://qiita.com/yasunari_matsuo/items/b1e56ad06c6a7843dfae
その1の2
https://qiita.com/yasunari_matsuo/items/a1d294f09ac21e9508b6
その1の3
https://qiita.com/yasunari_matsuo/items/14fe987a162936f7a1ae

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?