同じ場所を繰り返し画面キャプチャする簡易ツール

# はじめに

パソコン操作の手順書を作るときなど、同じ領域の画面キャプチャを繰り返し取得したいことがあり、そんなときにちょっと便利になりそうな簡易ツールを作ってみました。
この簡易ツールは、特別なソフトのインストール不要で、Windows環境のPowerShellで動作するスクリプトです。
基本的に、自分用ということで、インストーラーなどを用意せず、エラー処理の作り込みは限定的です。

## 動作環境

- Windows 10
- PowerShell 5.1

# 使い方

まず、次のリンクからZIPファイルをダウンロードし、適当な場所に展開します。

- [TryPowerShellプロジェクトのダウンロードリンク](https://github.com/kurukurupapa/TryPowerShell/archive/refs/heads/master.zip)
- 参考：[TryPowerShellプロジェクトのトップページ](https://github.com/kurukurupapa/TryPowerShell)

次に、今回作成した簡易ツールを実行します。
コマンドプロンプトから実行する場合、展開したフォルダの「Windows\ScreenCapture\ScreenCapture.bat」を実行します。
PowerShellのターミナルから実行する場合、展開したフォルダの「Windows\ScreenCapture\ScreenCapture.ps1」を実行します。

以下、展開したフォルダの「Windows\ScreenCapture」にパスを通した前提で、実行例を記述します。
コマンドプロンプトでの実行例です。

```batch
rem ヘルプを表示する。
ScreenCapture.bat
rem 画面全体をキャプチャする。
ScreenCapture.bat All D:\tmp\capture.png
rem 指定領域を、3秒間隔で10回、キャプチャする。
ScreenCapture.bat 0,0,800,600 D:\tmp\capture.png -Interval 3 -Repetition 10
```

PowerShellのターミナルでの実行例です。

```powershell
# ヘルプを表示する。
ScreenCapture.ps1
# 画面全体をキャプチャする。
ScreenCapture.ps1 "All" D:\tmp\capture.png
# 指定領域を、3秒間隔で10回、キャプチャする。
ScreenCapture.ps1 "0,0,800,600" D:\tmp\capture.png -Interval 3 -Repetition 10
```

# 実装のポイント

この簡易ツールをPowerShellで実装するうえで、主要な処理となった部分をメモしておきます。
今後、同じような処理を使いたい場面が出てきたときに、思い返せるようにしたいと思います。

## 画面キャプチャ

.NetのSystem.Drawing.Graphicsを使って、画面キャプチャすることができました。

```powershell
Add-Type -AssemblyName System.Drawing
function CaptureToFile($rect, $outPath) {
  # Save時に、相対パスだとエラーになることがあるので、絶対パスに変更。
  # パスが存在しないことも考慮して.Netで実装。Resolve-Pathだとエラーになる。
  $outPath = GetFullPath $outPath
  # キャプチャ
  $bitmap = New-Object System.Drawing.Bitmap($rect.Width, $rect.Height)
  $graphics = [System.Drawing.Graphics]::FromImage($bitmap)
  $graphics.CopyFromScreen($rect.Left, $rect.Top, 0, 0, $bitmap.Size)
  $bitmap.Save($outPath)
  $graphics.Dispose()
  $bitmap.Dispose()
  Write-Host "キャプチャしました。 $($rect.Location) $($rect.Size) $outPath"
}
function GetFullPath($path) {
  [System.IO.Directory]::SetCurrentDirectory((Get-Location))
  return [System.IO.Path]::GetFullPath($path)
}

# 上記関数の呼び出し例
$rect = New-Object System.Drawing.Rectangle(0, 0, 500, 500)
CaptureToFile $rect "work\capture.png"
```

マルチモニターを考慮して画面キャプチャするためには、次の処理が必要でした。

```powershell
# マルチモニター考慮
# 全モニターを1つの画像ファイルに保存
$screens = [System.Windows.Forms.Screen]::AllScreens
$top    = ($screens.Bounds.Top    | Measure-Object -Minimum).Minimum
$left   = ($screens.Bounds.Left   | Measure-Object -Minimum).Minimum
$right  = ($screens.Bounds.Right  | Measure-Object -Maximum).Maximum
$bottom = ($screens.Bounds.Bottom | Measure-Object -Maximum).Maximum
$rect = [System.Drawing.Rectangle]::FromLTRB($left, $top, $right, $bottom)
CaptureToFile $rect "work\capture.png"

# プライマリモニターのみ
$rect = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds
CaptureToFile $rect "work\capture.png"

# アクティブモニターのみ →C#/.NET関数を自作する必要がありそうなので省略。
```

## 画像をクリップボードへコピー

次のようにして、キャプチャした画像を、クリップボードに保存することができました。
2つの実装方法を試し、どちらでも動作しました。

```powershell
# 画像をクリップボードにコピー
$bitmap = New-Object System.Drawing.Bitmap($outPath)
# 案1
$data = New-Object System.Windows.Forms.DataObject
$data.SetImage($bitmap)
[Windows.Forms.Clipboard]::SetDataObject($data, $true)
# 案2
[Windows.Forms.Clipboard]::SetImage($bitmap)
# 後片付け
$bitmap.Dispose()
```

## マウス位置・ボタンの参照

キャプチャの取得範囲を指定するのに、マウスをドラッグして指定できると便利かと思い、マウスで矩形選択する処理も書いてみました。

```powershell
function GetDragRect() {
  Write-Host "マウスをドラッグして矩形領域を選択してください。"
  while ([System.Windows.Forms.Control]::MouseButtons -eq 'None') { Start-Sleep 0.5 }
  $p1 = [System.Windows.Forms.Control]::MousePosition
  Write-Host "Pressed"
  while ([System.Windows.Forms.Control]::MouseButtons -ne 'None') { Start-Sleep 0.5 }
  $p2 = [System.Windows.Forms.Control]::MousePosition
  Write-Host "Released"
  $rect = [System.Drawing.Rectangle]::FromLTRB(
    [Math]::Min($p1.X, $p2.X), [Math]::Min($p1.Y, $p2.Y),
    [Math]::Max($p1.X, $p2.X), [Math]::Max($p1.Y, $p2.Y))
  Write-Host $rect
  return $rect
}
$rect = GetDragRect
```

# 参考サイト

実装するのに以下のサイトを参考にさせていただきました。ありがとうございます！

- [簡単！PowerShellで作成できる画面録画ツールの紹介と作り方[No18] - BookALittle](https://bookalittle.com/howtocheck-operation-byrecording-pstool/)
- [インターネット上の画像をクリップボードにコピーするコード(PowerShell版)](https://gist.github.com/bu762/6e0f3668e59d4a932821)
- [Windowsのコマンドラインからスクリーンショットを撮る(PowerShell) | Misohena Blog](https://misohena.jp/blog/2021-08-08-take-screenshot-on-windows-power-shell.html)