職場のパソコンでマウス操作やキーボード操作を自動化する

# はじめに
今年もこの季節がやってきた。社内システムにひたすらデータを入力する作業。延々と同じ操作の繰り返し。クリック、入力、クリック、入力。こんなんやってられっか！というわけで、入力操作を自動化しようと思い立った。
## 方針
やることは、業務システム内でボタンをクリック、データを入力、これの繰り返し。クリックする場所や記入する文言は常に一緒。ということは、一件のデータ入力におけるマウスおよびキーボード操作をすべて自動化することができれば、あとはそれをループさせるだけで完全自動化が達成されるはず。従って、任意の座標をクリックする操作と任意の文言を入力する操作を自動的に行う方法を調べればよい。
## 条件
職場のパソコンはWindows10。セキュリティ上、無許可でソフトをインストールすることや外部のデータを持ち込むことはできない。よって既存の自動化ツールの使用は無し。「業務効率化のため」とか言っても、「なに遊んでんだ」「前例がない」とか言われるのがオチ。つまり、ほぼ素の状態のWindowsパソコンで、操作自動化を目指す。
## 結論
WindowsPowerShellでマウス操作およびキーボード操作を所定の順番で行うスクリプトを作成し、自動化に成功した。
# 自動化への道のり
試行錯誤の過程。
## Pythonで自動化
そもそも自動化をやろうと思ったきっかけが「退屈なことはPythonにやらせよう」という本を本屋で見かけたからだったので、最初はPythonでやってみようと思っていた。正直Pythonってなんぞやのレベルだったが、調べてみたところPythonを用いた自動化は簡単に行えそうであった。下記の記事が大変参考になった。
https://qiita.com/konitech913/items/301bb63c8a69c3fcb1bd

例えば、下記は練習で作った、メモ帳を自動で開いて文字入力を行うスクリプト。実行するとメモ帳が開いて、abcdefghiと入力されるはず。

```py
# PyAutoGUIライブラリのインポート
import pyautogui

# timeモジュールのsleep関数をインポート
from time import sleep

# Windowsボタン押下
pyautogui.press('win')

# 検索窓に「notepad」と入力
pyautogui.write('notepad')

# Enterボタン押下
pyautogui.press('enter')

# メモ帳が起動するまで待機（待機時間は適宜調整）
sleep(1)

# 文字を入力
pyautogui.write('abcdefghi')
```


また、下記はペイントを開いて四角を描くスクリプト。

```py
# PyAutoGUIライブラリのインポート
import pyautogui

# timeモジュールのsleep関数をインポート
from time import sleep

# Windowsボタン押下
pyautogui.press('win')

# 検索窓に「paint」と入力
pyautogui.write('paint')

# Enterボタン押下
pyautogui.press('enter')

# ペイントが起動するまで待機（待機時間は適宜調整）
sleep(1)

# マウスを描き始めの場所まで移動（座標値は適宜調整）
pyautogui.moveTo(200,200)

# 各頂点へマウスをドラッグして四角を描く（座標値は適宜調整）
pyautogui.dragTo(200,600)
pyautogui.dragTo(800,600)
pyautogui.dragTo(800,200)
pyautogui.dragTo(200,200)
```

各関数の意味等は他の記事を参照。正直よくわかってない。ともかく、これで指定の座標をクリックしたり指定の文言を入力することができているので、すでに方針を満たしている。早くも自動化達成か。

しかし重大な問題点で、Pythonがインストールされていなければこのスクリプトの実行はできない。これでは条件に抵触する。結局Pythonを使うという方向性は頓挫した。
## C言語で自動化
次に考えたのは、C言語で操作を自動で実行するプログラムを作成する方法。あらかじめ作成したプログラムをコンパイルし、実行ファイルを職場のパソコンに持っていって実行させれば、特に何かをインストールすることなく自動操作が実現できるはず。まあ、条件にある通り外部から職場のパソコンにファイルを持ってくることは基本的にできないので、すでに雲行きが怪しいが…

調べてみたところ、WindowsAPIなるものを呼び出すプログラムを作成すれば、自動操作が実現できるようだ。下記のサイトが参考になった。
http://www.sm.rim.or.jp/~shishido/inputevent.html
http://kaitei.net/winapi/
https://kagasan.hatenablog.com/entry/2016/11/27/175657
https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes

例えば先程のペイントで自動で四角を描くスクリプトは、C言語で書くと下記のようになる。

```c
#include <stdio.h>
#include <windows.h> /* windows.hを読み込み */

int main(){
	
	/* Windowsボタン押下 */
	keybd_event(VK_LWIN,0,KEYEVENTF_EXTENDEDKEY|0,0);
	Sleep(10);
	keybd_event(VK_LWIN,0,KEYEVENTF_EXTENDEDKEY|KEYEVENTF_KEYUP,0);
	
	/* 待機（待機時間は適宜調整、以下同様） */
	Sleep(500);
	
	/* 検索窓に「paint」と入力 */
	keybd_event(0x50,0,KEYEVENTF_EXTENDEDKEY|0,0);
	Sleep(10);
	keybd_event(0x50,0,KEYEVENTF_EXTENDEDKEY|KEYEVENTF_KEYUP,0);
	Sleep(10);
	keybd_event(0x41,0,KEYEVENTF_EXTENDEDKEY|0,0);
	Sleep(10);
	keybd_event(0x41,0,KEYEVENTF_EXTENDEDKEY|KEYEVENTF_KEYUP,0);
	Sleep(10);
	keybd_event(0x49,0,KEYEVENTF_EXTENDEDKEY|0,0);
	Sleep(10);
	keybd_event(0x49,0,KEYEVENTF_EXTENDEDKEY|KEYEVENTF_KEYUP,0);
	Sleep(10);
	keybd_event(0x4E,0,KEYEVENTF_EXTENDEDKEY|0,0);
	Sleep(10);
	keybd_event(0x4E,0,KEYEVENTF_EXTENDEDKEY|KEYEVENTF_KEYUP,0);
	Sleep(10);
	keybd_event(0x54,0,KEYEVENTF_EXTENDEDKEY|0,0);
	Sleep(10);
	keybd_event(0x54,0,KEYEVENTF_EXTENDEDKEY|KEYEVENTF_KEYUP,0);
	
	/* 待機 */
	Sleep(500);
	
	/* Enterボタン押下 */
	keybd_event(VK_RETURN,0,KEYEVENTF_EXTENDEDKEY|0,0);
	Sleep(10);
	keybd_event(VK_RETURN,0,KEYEVENTF_EXTENDEDKEY|KEYEVENTF_KEYUP,0);
	
	/* ペイントが起動するまで待機 */
	Sleep(1000);
	
	/* マウスを描き始めの場所まで移動 */
	SetCursorPos(200,200);
	Sleep(100);
	
	/* 各頂点へマウスをドラッグして四角を描く（座標値は適宜調整） */
	mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);
	Sleep(100);
	SetCursorPos(200,600);
	Sleep(100);
	SetCursorPos(800,600);
	Sleep(100);
	SetCursorPos(800,200);
	Sleep(100);
	SetCursorPos(200,200);
	Sleep(100);
	mouse_event(MOUSEEVENTF_LEFTUP,0,0,0,0);
	
	return 0;
}
```

これをコンパイルして実行すれば、自動でペイントが起動されて四角が描かれるはず。検索窓にpaintと入力するところは、「p」「a」「i」「n」「t」をそれぞれ`keybd_event`で順番にキー入力するという力技。Pythonの`pyautogui.write`のようにテキストを直接入力できるような方法があればよかったのだが、調べてもよくわからなかった。

見ての通り、Pythonほど簡潔に書くことはできないし、ネットで調べても何やら難解な記事ばかりで参考にできる情報の量はあまり多くなかったが、それでもマウス操作やキーボード操作の自動化はできているので、一応やりたいことは実現出来そうだ。あとは、必要な操作を順番に行うプログラムを自宅で書いてコンパイルし、実行ファイルをなんとかして職場に持っていけばよい。

と、ここまで来て、もっといい方法がありそうなことに気付いた。それが、以下に記すWindowsPowerShellを使う方法である。
## WindowsPowerShellで自動化（結論）
ここまで書いてきたことを調べる過程で、どうやらWindowsのパソコンにはWindowsPowerShellなるものがデフォルトで備わっていて、これまでやろうとしていたことは全てWindowsPowerShellでもできるらしいということを知った。Windowsにデフォルトで入っているなら当然職場のパソコンにも入っているはずだし、実行ファイルを職場に持っていくというようなことをせずとも、職場でスクリプトの作成や編集をすることが可能。というわけで、現時点で最もハードルの低い方法に思えたので、WindowsPowerShellを使う方向性に切り替えた。

スクリプトの書き方は下記のページを参考にした。
https://tarenagashi.hatenablog.jp/entry/2020/12/07/213259
https://mitosuya.net/automation-input-keyboard
https://qiita.com/nimzo6689/items/488467dbe0c4e5645745
https://note.com/mirupiso/n/n1190de896767
### 任意の場所のクリック、任意の文字列の入力
再三登場のペイントに四角を描くスクリプトは、WindowsPowerShellだと下記のようになる。

```powershell
#System.Windows.Formsなどのインポート
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

# Windows APIの宣言
$signature=@'
[DllImport("user32.dll",CharSet=CharSet.Auto,CallingConvention=CallingConvention.StdCall)]
public static extern void mouse_event(long dwFlags, long dx, long dy, long cButtons, long dwExtraInfo);
'@
$SendMouseClick = Add-Type -memberDefinition $signature -name "Win32MouseEventNew" -namespace Win32Functions -passThru


# Windowsキー（Ctrl+Escキー）押下
[System.Windows.Forms.SendKeys]::SendWait("^{ESC}")
Start-Sleep -m 100

# 検索窓に「paint」と入力
[System.Windows.Forms.SendKeys]::SendWait("paint")
Start-Sleep -m 100

# Enterボタン押下
[System.Windows.Forms.SendKeys]::SendWait("{ENTER}")

# ペイントが起動するまで待機（待機時間は適宜調整）
Start-Sleep -m 1000

# マウスを描き始めの場所まで移動（座標値は適宜調整、以下同様）
[System.Windows.Forms.Cursor]::Position = New-Object System.Drawing.Point(200, 200)
Start-Sleep -m 100

# 各頂点へマウスをドラッグして四角を描く
$SendMouseClick::mouse_event(0x00000002, 0, 0, 0, 0)
Start-Sleep -m 100
[System.Windows.Forms.Cursor]::Position = New-Object System.Drawing.Point(200, 600)
Start-Sleep -m 100
[System.Windows.Forms.Cursor]::Position = New-Object System.Drawing.Point(800, 600)
Start-Sleep -m 100
[System.Windows.Forms.Cursor]::Position = New-Object System.Drawing.Point(800, 200)
Start-Sleep -m 100
[System.Windows.Forms.Cursor]::Position = New-Object System.Drawing.Point(200, 200)
Start-Sleep -m 100
$SendMouseClick::mouse_event(0x00000004, 0, 0, 0, 0)

```

各処理の詳細はやはり他の記事等を参照くださいますよう。よく分かってないので、無くても動く部分とかあるかもしれない。Pythonほど分かりやすくはないが、任意の場所のクリックや文言の入力ができているのがお分かりいただけるだろうか。Windowsキー押下については、下記のページの「Ctrl+EscキーでWindowsキー押下になる」というテクを使った。
https://qiita.com/kiduki/items/51301918c4b415255890
### 座標値の取得
方針は満たされた。では、これで実際に自動化スクリプトを作ろうとすると、クリックするボタンの座標をどう設定すればよいか、という問題が浮上する。これについては、[先述の記事](https://tarenagashi.hatenablog.jp/entry/2020/12/07/213259)のやり方を参考にした。

```powershell
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") 

Start-Sleep -s 3

$X = [System.Windows.Forms.Cursor]::Position.X
$Y = [System.Windows.Forms.Cursor]::Position.Y

Write-Output "X: $X | Y: $Y"
Read-Host
```

このスクリプトを実行すると、3秒後に現在のマウスカーソルの座標値が表示される。これを使って、例えばクリックしたいボタンの上にマウスカーソルを置いておけば、ボタンの座標を取得できる。取得した値を以下のスクリプトの`x,y`に代入すれば、そのボタンをクリックできるという寸法だ。

```powershell
# マウスカーソル移動
[System.Windows.Forms.Cursor]::Position = New-Object System.Drawing.Point(x, y)

# クリックイベント生成
$SendMouseClick::mouse_event(0x0002, 0, 0, 0, 0);
$SendMouseClick::mouse_event(0x0004, 0, 0, 0, 0);
```
### 繰り返し処理
繰り返し処理については、色々なやり方があるのだろうけど、下記の記事の方法がシンプルだったので参考にした。
https://mk-55.hatenablog.com/entry/2016/12/17/115632

```powershell
1..n | %{
	# 繰り返したい処理
}
```

これにより、任意の処理をn回繰り返すことができる。スクリプトの一部で繰り返し処理を行ったり、あるいはスクリプト全体を繰り返したり。
### 完成
これにて、すべての駒は出揃った。任意の場所のクリック、任意の文字列の入力、ループ。あとはこの三種の神器を組み合わせてシステムへの入力操作の手順を1個1個再現していくことで、無事入力作業を完全に自動化することに成功した。これまで夜遅くまで虚無になりながらやっていた作業が、ちょっくら休憩に行ってコーヒーでも飲んでいる間に終わっているのは中々に爽快であった。
# 課題
操作自動化に無事成功したが、いくつか課題も残っている。
## システムのユーザーインターフェースが変わると使えなくなる
今回作成した自動化スクリプトでは、ボタン等の座標を一個一個取得し、その数値をスクリプト上にベタ打ちすることでボタンのクリック操作を実現した。従って、システムのバージョンアップなどによりボタンの配置が変わったりしたら再度座標の取得し直しになる。というか、なった…
## 別のパソコンで使えない
別のパソコンでも使おうと思って気付いたのだが、一般的に、別のパソコンでは画面上のボタン等の座標値が違う。従って、別のパソコンで使いたい場合はそのパソコンで改めて座標の取得し直しになる。やってられん…
## 変化に弱い
当然っちゃ当然だが、全く同じ操作をずっと繰り返すスクリプトなので、例外的な入力を行う案件が一件でも混ざってるとそこで止まる。また、いつもより読み込みに時間がかかったりして待機時間をはみ出してしまうとハマる。待機時間はなるべく長めに取っておいたほうが安定性は増すが、スピードは落ちる。条件分岐等を駆使すればもっと汎用性を持たせられるかもしれないが、そこまでやる価値があるかどうか…
## 何が起こるかわからない
素人が作っているので、何か変なことが起こる可能性を否定できない。もっとも、一年くらい使ってみたが、特に変なことは何も起きなかった。
# まとめ
職場のパソコンで操作を自動化するためにあれこれ考えた結果、WindowsPowerShellで自動化させることに成功した。操作自動化に関する記事は数あれど、素のWindowsパソコンを自動化するという試みはあまり見かけなかったので、どこかの誰かの一助になればこれ幸いである。

操作自動化の一般論や善し悪しについては下記の記事が詳しい。
https://qiita.com/mima_ita/items/4149a4cdb9a33084258b
https://qiita.com/mima_ita/items/453bb6c313e459c44689