RPAで業務を効率化って、専用ソフトとか無くてもソコソコできますよ

# きっかけ
身近で「某社のRPAのソフトを契約して、事務系の業務を自動化するんだぁ」って話が複数聞こえてきて。。。
そんなん無くても、「VBScriptだけでもソコソコの作業は自動化できるよ」ってのが伝わらないので、自分の作業自動化用に作っていたVBScriptのClassを公開してみます。

# 概要
仕事で、PC（Windows）でやって大抵の作業は

 - ブラウザの操作（ちゃんと制御できるのはIEのみですが）
 - マウス操作
 - キーボード入力

ができれば、ほぼいけます（ただし、「力業で何とか」の場合もありますが）。

あとは、

 - エクセル操作
 - ファイルの入出力

あたりでしょうか。

ちょっと特殊（？）なところで

 - CSVにSQLを投げて検索、集計

ができれば、大丈夫かな。
あとは、その組み合わせで、日々の定型業務はこなせると思います。

で、「VBScriptでもClass作れるんじゃん」ってことを、恥ずかしながら最近知ったので、Class化して便利に使っている、今日この頃です。

# Classファイル
## IE関連

```vb:IeUtilClass.vbs
' IE関連のクラス
Class IeUtilClass
	' 内部変数宣言 -------------------------------------------------------------
	Private Ie

	' コンストラクタ
	Private Sub Class_Initialize()
		Set Ie = CreateObject("InternetExplorer.Application")
	End Sub
	' デストラクタ
	Private Sub Class_Terminate()
		Set Ie = Nothing
	End Sub

	' Public関数 ---------------------------------------------------------------
	' URLオープン
	Public Sub Navigate(str)
		Ie.Navigate(str)
	End Sub
	
	Public Sub Visible(bln)
		Ie.Visible = bln
	End Sub
	
	Public Sub FullScreen(bln)
		Ie.FullScreen = bln
	End Sub
	
	Public Sub Quit
		Ie.Quit
	End Sub

	Public Sub WaitProc
		Do While ie.Busy = True Or ie.readystate <> 4
		Loop
	End Sub
	
	Public Function getElementById(id)
		Set getElementById = Ie.document.getElementById(id)
	End Function
	
	Public Function GetElementsByName(name)
		Set GetElementsByName = Ie.document.GetElementsByName(name)
	End Function
	
	Public Function GetElementsByClassName(name)
		Set GetElementsByClassName = Ie.document.GetElementsByClassName(name)
	End Function
	
	Public Function GetElementsByTagName(name)
		Set GetElementsByTagName = Ie.document.GetElementsByTagName(name)
	End Function

End Class
```

## マウス関連

以下のサイトを参考に（というか、ほぼそのまま）クラス化しました。
[VBScriptでマウスポインタを動かしたりクリックしたり座標を取得したりするサンプル · GitHub](https://gist.github.com/kunst1080/5134725)

マウスを操作するには、WindowsのAPIをたたく必要がありますが、エクセルを使えば簡単なようです。
「まぁ、ほとんどの業務用のPCならExeclはあるでしょ」ってことで、APIたたくのにExcelを使って楽してます。
「エクセル使うのやだ」って方は・・・ググってください　m(__)m
（そういうサンプルも見かけました）

```vb:MouseUtilClass.vbs
' マウス関連のクラス
Class MouseUtilClass
	' 内部変数宣言 -------------------------------------------------------------
	Private Excel
	Private SCREEN_X
	Private SCREEN_Y

	' Property宣言 -------------------------------------------------------------
	' Private-----------------------------------------------
	'キーコード
	Private Property Get VK_SHIFT
		VK_SHIFT = &H10
	End Property
	'マウス定数
	Private Property Get MOUSEEVENTF_ABSOLUTE
		MOUSEEVENTF_ABSOLUTE = &H8000&
	End Property
	Private Property Get MOUSE_MOVE
		MOUSE_MOVE = &H1
	End Property
	Private Property Get MOUSEEVENTF_LEFTDOWN
		MOUSEEVENTF_LEFTDOWN = &H2
	End Property
	Private Property Get MOUSEEVENTF_LEFTUP
		MOUSEEVENTF_LEFTUP = &H4
	End Property
	
	' Private関数 --------------------------------------------------------------
	' コンストラクタ
	Private Sub Class_Initialize()
		Dim items
		Set items = CreateObject("WbemScripting.SWbemLocator").ConnectServer.ExecQuery("Select * From Win32_VideoController")
		For Each item In items
			SCREEN_X = item.CurrentHorizontalResolution
			SCREEN_Y = item.CurrentVerticalResolution
		Next
	End Sub
	' デストラクタ
	Private Sub Class_Terminate()
		Set Excel = Nothing
	End Sub

	' Public関数 ---------------------------------------------------------------
	'クリック
	Public Sub MouseClick
	    Dim dwFlags
	    dwFlags = MOUSEEVENTF_LEFTDOWN Or MOUSEEVENTF_LEFTUP
	    Call API_mouse_event(dwFlags, 0, 0, 0, 0)
	    WScript.Sleep 100
	End Sub

	'SHIFT+クリック
	Public Sub MouseClickShift
	    Dim dwFlags
	    
	    Call API_keybd_event(VK_SHIFT,0,1,0)
	    dwFlags = MOUSEEVENTF_LEFTDOWN Or MOUSEEVENTF_LEFTUP
	    Call API_mouse_event(dwFlags, 0, 0, 0, 0)
	    Call API_keybd_event(VK_SHIFT,0,3,0)
	    WScript.Sleep 100
	End Sub

	'ダブルクリック
	Public Sub DoubleClick
	    MouseClick
	    MouseClick
	End Sub

	'マウスポインタ移動
	Public Sub MouseMove(x, y)
	    Dim pos_x, pos_y, dwFlags
	    
	    dwFlags = MOUSEEVENTF_ABSOLUTE Or MOUSE_MOVE
	    pos_x = Int(x * 65535 / SCREEN_X)
	    pos_y = Int(y * 65535 / SCREEN_Y)
	    Call API_mouse_event(dwFlags, pos_x, pos_y, 0, 0)
	    WScript.Sleep 100
	End Sub

	' マウスポインタの座標取得
	Public Function GetMousePos()
		GetMousePos = API_GetMessagePos
	End Function
	Public Function GetMousePosX()
		Dim pos
		pos = API_GetMessagePos
		GetMousePosX = pos(0)
	End Function
	Public Function GetMousePosY()
		Dim pos
		pos = API_GetMessagePos
		GetMousePosX = pos(1)
	End Function

	' Private関数 --------------------------------------------------------------
	'************************
	'APIを叩く処理
	'************************
	Private Sub API_mouse_event(dwFlags, dx, dy, dwData, dwExtraInfo)
	    Dim strFunction
	    Const API_STRING = "CALL(""user32"",""mouse_event"",""JJJJJj"", $1, $2, $3, $4, $5)"
	    strFunction = Replace(Replace(Replace(Replace(Replace(API_STRING, "$1", dwFlags), "$2", dx), "$3", dy), "$4", dwData), "$5", dwExtraInfo)
	    Call Excel.ExecuteExcel4Macro(strFunction)
	End Sub

	Private Sub API_keybd_event(bVk, bScan, dwFlags, dwExtraInfo)
	    Dim strFunction
	    Const API_STRING = "CALL(""user32"",""keybd_event"",""JJJJJ"", $1, $2, $3, $4)"
	    strFunction = Replace(Replace(Replace(Replace(API_STRING, "$1", bVk), "$2", bScan), "$3", dwFlags), "$4", dwExtraInfo)
	    Call Excel.ExecuteExcel4Macro(strFunction)
	End Sub

	Private Function API_GetMessagePos()
	    Dim ret, strHex, x, y
	    Dim strFunction
	    Const API_STRING = "CALL(""user32"",""GetMessagePos"",""J"")"
	    strFunction = API_STRING
	    ret = Excel.ExecuteExcel4Macro(strFunction)
	    strHex = Right("00000000" & Hex(ret), 8)
	    x = CLng("&H" & Right(strHex, 4))
	    y = CLng("&H" &  Left(strHex, 4))
	    API_GetMessagePos = Array(x, y)
	End Function
End Class
```

## キーボード関連

Sendkeysは半角のみ（全角は未対応）なので、全角を投げたい場合はクリップボード経由で行うのですが、そこはSendkeys関数を宣言して、内部で「全角を含むかどうか」を判断し、Sendkeysでいくか、クリップボードを使うかを判断しています。

```vb:KeyboardUtilClass.vbs
' キーボード関連のクラス
Class KeyboardUtilClass
	' 内部変数宣言 -------------------------------------------------------------
	Private wsh

	' Private関数 --------------------------------------------------------------
	' コンストラクタ
	Private Sub Class_Initialize()
		Set wsh = WScript.CreateObject("WScript.Shell")
	End Sub
	' デストラクタ
	Private Sub Class_Terminate()
		Set wsh = Nothing
	End Sub

	' Public関数 ---------------------------------------------------------------
	'クリック
	Public Sub Sendkeys(str)
		If Len(str) = LenByte(str) Then
			wsh.SendKeys str
		Else
			' 全角文字が含まれている場合は、クリップボード経由で
			wsh.Run "cmd.exe /c echo " & str & "| clip", 0, true 
			wsh.SendKeys "^v"
		End If
	End Sub

	' Private関数 --------------------------------------------------------------
	Private Function LenByte(ByVal s)
		Dim c, i, k
		c = 0
		For i = 0 To Len(s) - 1
			k = Mid(s, i + 1, 1)
			If (Asc(k) And &HFF00) = 0 Then
				c = c + 1
			Else
				c = c + 2
			End If
		Next
		LenByte = c
	End Function
End Class
```

# 呼び出しサンプル

上記のクラスの呼び出しサンプルです。
呼び出し元のスクリプトで、クラスファイルを読み込む必要があるので、そのへんを参考にしてください。
操作の自動化をしていると、どうしても「マウスカーソルを特定の座標に移動させて、クリック」みたいなことが必要になります。
そんなとき、「ここの座標っていくつよ？」を調べるためのスクリプトです。
（カーソルを移動させるのはオマケ）

フォルダ構成が以下の場合のサンプルです。

```
VBScripts
│  99.mousepos.vbs
│
└─lib
        IeUtilClass.vbs
        KeyboardUtilClass.vbs
        MouseUtilClass.vbs
```

```vb:99.mousepos.vbs
' マウス座標取得スクリプト

' ライブラリ読み込み
	Dim fso
	Set fso = CreateObject("Scripting.FileSystemObject")
	' 実行ファイルパス取得
	Dim myPath
	myPath = fso.getParentFolderName(WScript.ScriptFullName)
	' マウス関連クラス -------------------------------------
	Dim libMouse
	Set libMouse = fso.OpenTextFile(myPath & "\lib\MouseUtilClass.vbs")
	ExecuteGlobal libMouse.ReadAll()
	libMouse.Close
	Set libMouse = Nothing

' メイン処理 -------------------------------------------------------------------
	Dim mouseUtil
	Set mouseUtil = New MouseUtilClass
	
	Dim pos
	WScript.Echo "3"
	WScript.Sleep(1000)
	WScript.Echo "2"
	WScript.Sleep(1000)
	WScript.Echo "1"
	WScript.Sleep(1000)
	pos = mouseUtil.GetMousePos()
	WScript.Echo pos(0) & " / " & pos(1)
	
	WScript.Sleep(1000)
	WScript.Echo "Move to "
	WScript.Sleep(1000)
	Call mouseUtil.MouseMove(pos(0)+100,pos(1)+100)
	
	' 解放
	Set mouseUtil = Nothing
	Set fso = Nothing
	'終了
	WScript.Quit

```

# ひとまず

ここまで。

 - ファイルの入出力

は、次の機会に。
ファイルの読み書きは、文字コードの問題があるので、クラス化しとくと便利ですね。

会社業務だと、

 - エクセル操作
 - データベース関係
 - CSVにSQLを投げて検索、集計
 - ネットワークドライブの接続・切断

あたりを使うこともありますね。
CSVにSQL投げて検索は地味に便利です。
でも、このへんはクラスにしてみましたが、あまり便利感がなかったのは、僕の作り方が悪かったのか・・・というところ。
（要望があれば、公開します）