Windows PowerShell初めて使ってみたのでメモ。
やりたかったのは他のアプリケーションに対するキー操作とマウスによるクリック。
で、ポイントは
- 他アプリケーションに対するマウス操作はWin32APIを使う。
- Windows PowerShell ではC#がインラインコンパイル可能→.NETで出来ることは大体出来る。
ってトコ。
以下は
- 入力欄に数字を入力
- Enterキー押下
- ポップアップでダイアログ表示が表示されるので、ボタンが表示されている座標でクリック
- Windowが閉じていたら数字をコンソールに出力する
を行うスクリプトです。0-9でどれが求められている入力かを試行する感じです。
# Win32API をいじるC#クラス
# C#のソースを変数に格納
$source = @"
using System;
using System.Runtime.InteropServices; // for DllImport, Marshal
using System.Windows.Forms;
public class Win32SendKey {
// マウス関連のWin32API
[DllImport("user32.dll")]
extern static uint SendInput(
uint nInputs, // INPUT 構造体の数(イベント数)
INPUT[] pInputs, // INPUT 構造体
int cbSize // INPUT 構造体のサイズ
);
[StructLayout(LayoutKind.Sequential)]
struct INPUT
{
public int type; // 0 = INPUT_MOUSE(デフォルト),
// 1 = INPUT_KEYBOARD
public MOUSEINPUT mi;
}
[StructLayout(LayoutKind.Sequential)]
struct MOUSEINPUT
{
public int dx ;
public int dy ;
public int mouseData ; // amount of wheel movement
public int dwFlags;
public int time; // time stamp for the event
public IntPtr dwExtraInfo;
}
const int MOUSEEVENTF_MOVED = 0x0001 ;
const int MOUSEEVENTF_LEFTDOWN = 0x0002 ; // 左ボタン Down
const int MOUSEEVENTF_LEFTUP = 0x0004 ; // 左ボタン Up
const int MOUSEEVENTF_RIGHTDOWN = 0x0008 ; // 右ボタン Down
const int MOUSEEVENTF_RIGHTUP = 0x0010 ; // 右ボタン Up
const int MOUSEEVENTF_MIDDLEDOWN = 0x0020 ; // 中ボタン Down
const int MOUSEEVENTF_MIDDLEUP = 0x0040 ; // 中ボタン Up
const int MOUSEEVENTF_WHEEL = 0x0080 ;
const int MOUSEEVENTF_XDOWN = 0x0100 ;
const int MOUSEEVENTF_XUP = 0x0200 ;
const int MOUSEEVENTF_ABSOLUTE = 0x8000 ;
const int screen_length = 0x10000 ; // for MOUSEEVENTF_ABSOLUTE (この値は固定)
// WindowActivate関連のWin32API
[System.Runtime.InteropServices.DllImport(
"user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
static extern IntPtr FindWindow(
string lpClassName,
string lpWindowName);
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern bool SetForegroundWindow(IntPtr hWnd);
// PowerShellから呼び出されるメソッド
public static void LeftClick(int x, int y) { // 指定した座標を左クリックするメソッド
INPUT[] input = new INPUT[3];
// MOUSEEVENTF_ABSOLUTEの場合、画面サイズは 65535 で考えるので
// 自分の解像度に合わせて修正すること(この場合 1024*768)
// マウスに対する一連の動作の配列。1回目は移動。2回目は左ボタン押下。3回目は左ボタン開放。
input[0].mi.dx = x * (65535 / 1024);
input[0].mi.dy = y * (65535 / 768);
input[0].mi.dwFlags = MOUSEEVENTF_MOVED | MOUSEEVENTF_ABSOLUTE;
input[1].mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
input[2].mi.dwFlags = MOUSEEVENTF_LEFTUP;
SendInput(3, input, Marshal.SizeOf(input[0]));
}
public static bool ActivateWindow(string winTitle) { // 指定したWindowをアクティブにするメソッド
IntPtr hWnd = FindWindow(null, winTitle);
if (hWnd != IntPtr.Zero) {
SetForegroundWindow(hWnd);
return true;
}
else {
return false;
}
}
public static void SendKey(string key) { // 指定したキーを送信するメソッド
SendKeys.SendWait(key);
}
}
"@
# 規定では.NET FrameworkのSystem.Windows.Formsアセンブリ読み込まれないので追加する
Add-Type -Language CSharp -TypeDefinition $source -ReferencedAssemblies System.Windows.Forms
$window_name = "操作したいWindow名" # 操作したいWindow名を設定
for ($i = 0; $i -lt 10; $i++) { # 10回繰り返す
if ([Win32SendKey]::ActivateWindow($window_name)) { # Windowがあったらアクティブにして以下の処理を行う
[Win32SendKey]::SendKey($i) # ループの添え字を送信
[Win32SendKey]::SendKey("{ENTER}") # ENTERキーを入力
Start-Sleep -m 100 # 100msec = 0.1sec 待つ
[Win32SendKey]::LeftClick(500, 600) # (500, 600)の座標にあるダイアログのOKボタンをクリック
Start-Sleep -m 100 # 100msec = 0.1sec 待つ
}
else { # Windowがない場合は終了
Write-Host($i - 1) # ループの添え字-1(試行がうまくいった数字)を出力
break
}
}ということで原型はこんな感じで、後はテキトーに。