🤖

PowerShellで自動操作中にマウス操作を無効にしたい

2022/12/24に公開

目的

PowerShellで自動化を行っている場合に、ユーザのマウス入力が行われると自動化がうまくいかないことがある。このため、自動操作中はマウスのイベントを無効にしたい。

環境:
PSVersion 5.1.22000.1335
Windows11

管理者権限ありで動かす場合

WindowsAPIのBlockInputを使用する。

# 管理者権限が必要になる
# クリックを受け付けなくなって困ったらCTRL+ALT+DEL
# メモ帳を動かすスクリプトは以下から
# https://www.tekizai.net/entry/2021/10/02/063000
$Win32 = &{
$cscode = @"
[DllImport("User32.dll")]
public static extern bool BlockInput(
  bool fBlockIt
);

"@
    return (add-type -memberDefinition $cscode -name "Win32ApiFunctions" -passthru)
}
# マウスの操作禁止
$Win32::BlockInput($TRUE)

Write-Output "操作できなくなる"
#ウィンドウの新しい幅 (ピクセル単位)
$width=300
#ウィンドウの新しい高さ (ピクセル単位)。
$height=300
#コントロールの左側の絶対画面座標。
$x=0
#コントロールの上部の絶対画面座標。
$y=0
Add-Type -AssemblyName UIAutomationClient
$notepad=Start-Process notepad -PassThru
Start-Sleep 1
$hwnd=$notepad.MainWindowHandle
#ハンドルからウィンドウを取得する
$window=[System.Windows.Automation.AutomationElement]::FromHandle($hwnd)
#ウィンドウサイズの状態を把握するためにWindowPatternを使う
$windowPattern=$window.GetCurrentPattern([System.Windows.Automation.WindowPattern]::Pattern)
#ウィンドウサイズを変更する準備としてサイズを通常に変更する
$windowPattern.SetWindowVisualState([System.Windows.Automation.WindowVisualState]::Normal)
#ウィンドウサイズを変更するためのパターン。
$transformPattern=$window.GetCurrentPattern([System.Windows.Automation.TransformPattern]::Pattern)
#Maximamだと移動もサイズ変更もできないので適当なサイズに変更する。
$transformPattern.Resize($width,$height)
#暴れる間隔
$sleep=0.5
#暴れる秒数
$duration=5
$startTime=Get-Date
while($true)
{
   Start-Sleep $sleep
   $transformPattern.Move($x,$y)
   $x=Get-Random -Maximum 1000 -Minimum 0
   $y=Get-Random -Maximum 1000 -Minimum 0
   $currentTime=Get-Date
   $durationMinute=($currentTime-$startTime).Seconds
   if(($durationMinute -ge $duration) -or $notepad.HasExited){
      Stop-Process $notepad
      break
   }
}

# マウスの操作復活
$Win32::BlockInput($FALSE)
Write-Output "操作できる"

管理者権限なしの場合

WH_MOUSE_LLのイベントをフックしてイベントを握り潰す.

# 管理者権限が必要ない
# クリックを受け付けなくなって困ったらCTRL+ALT+DEL
# 中身のメモ帳を動かすスクリプトは以下から
# https://www.tekizai.net/entry/2021/10/02/063000
# フックロジックは以下から
# https://www.ipentec.com/document/csharp-get-mouse-pointer-screen-position-using-global-hook
$cscode = @"
using System;
using System.Runtime.InteropServices;

public class WinHelper 
{
  [StructLayout(LayoutKind.Sequential)]
  struct POINT
  {
    public int x;
    public int y;
  }

  [StructLayout(LayoutKind.Sequential)]
  struct MOUSEHOOKSTRUCT
  {
    public POINT pt;
    public IntPtr hwnd;
    public int wHitTestCode;
    public int dwExtraInfo;
  }

  [StructLayout(LayoutKind.Sequential)]
  struct MOUSEHOOKSTRUCTEX
  {
    public MOUSEHOOKSTRUCT mouseHookStruct;
    public int MouseData;
  }

  [StructLayout(LayoutKind.Sequential)]
  struct MSLLHOOKSTRUCT
  {
    public POINT pt;
    public uint mouseData;
    public uint flags;
    public uint time;
    public IntPtr dwExtraInfo;
  }

  const int HC_ACTION = 0;
  private delegate IntPtr MouseHookCallback(int nCode, uint msg, ref MSLLHOOKSTRUCT msllhookstruct);

  [DllImport("user32.dll")]
  static extern IntPtr SetWindowsHookEx(int idHook, MouseHookCallback lpfn, IntPtr hMod, IntPtr dwThreadId);


  [DllImport("user32.dll")]
  static extern bool UnhookWindowsHookEx(IntPtr hHook);

  [DllImport("user32.dll")]
  static extern IntPtr CallNextHookEx(IntPtr hHook, int nCode, uint wParam, ref MSLLHOOKSTRUCT msllhookstruct);

  private static IntPtr MyHookProc(int nCode, uint wParam, ref MSLLHOOKSTRUCT lParam)
  {
    if (nCode == HC_ACTION) {
      // 0以外を返すことでイベントを握りつぶす
      return (IntPtr)1;
    }
    return CallNextHookEx(hHook, nCode, wParam, ref lParam);
  }
  static IntPtr hHook = IntPtr.Zero;
  static MouseHookCallback proc;
  const int WH_MOUSE_LL = 14;
  public static IntPtr Block()
  {
    proc = MyHookProc;
    hHook = SetWindowsHookEx(WH_MOUSE_LL,proc, IntPtr.Zero, IntPtr.Zero);
    return hHook;
    // return IntPtr.Zero;
  }
  public static bool UnBlock() {
    if (hHook == IntPtr.Zero) return false;
    return UnhookWindowsHookEx(hHook);
  }
}
"@
Add-Type -TypeDefinition $cscode
Write-Output $Win32
# 操作禁止
[WinHelper]::Block()

Write-Output "操作できなくなる"
#ウィンドウの新しい幅 (ピクセル単位)
$width=300
#ウィンドウの新しい高さ (ピクセル単位)。
$height=300
#コントロールの左側の絶対画面座標。
$x=0
#コントロールの上部の絶対画面座標。
$y=0
Add-Type -AssemblyName UIAutomationClient
$notepad=Start-Process notepad -PassThru
Start-Sleep 1
$hwnd=$notepad.MainWindowHandle
#ハンドルからウィンドウを取得する
$window=[System.Windows.Automation.AutomationElement]::FromHandle($hwnd)
#ウィンドウサイズの状態を把握するためにWindowPatternを使う
$windowPattern=$window.GetCurrentPattern([System.Windows.Automation.WindowPattern]::Pattern)
#ウィンドウサイズを変更する準備としてサイズを通常に変更する
$windowPattern.SetWindowVisualState([System.Windows.Automation.WindowVisualState]::Normal)
#ウィンドウサイズを変更するためのパターン。
$transformPattern=$window.GetCurrentPattern([System.Windows.Automation.TransformPattern]::Pattern)
#Maximamだと移動もサイズ変更もできないので適当なサイズに変更する。
$transformPattern.Resize($width,$height)
#暴れる間隔
$sleep=0.5
#暴れる秒数
$duration=5
$startTime=Get-Date
while($true)
{
   Start-Sleep $sleep
   $transformPattern.Move($x,$y)
   $x=Get-Random -Maximum 1000 -Minimum 0
   $y=Get-Random -Maximum 1000 -Minimum 0
   $currentTime=Get-Date
   $durationMinute=($currentTime-$startTime).Seconds
   if(($durationMinute -ge $duration) -or $notepad.HasExited){
      Stop-Process $notepad
      break
   }
}

# 操作復活
[WinHelper]::UnBlock()
Write-Output "操作できる"

参考

【PowerShell】メモ帳が暴れ回る最高のスクリプト
https://www.tekizai.net/entry/2021/10/02/063000

グローバルフックを利用して常時 マウスポインタのスクリーン座標を取得する (C#プログラミング)
https://www.ipentec.com/document/csharp-get-mouse-pointer-screen-position-using-global-hook

○特定のキーボードのキーを使用不能にする
http://7ujm.net/C++/Hook2.html

Discussion