LoginSignup
2
2

私PowerShellだけどスクリーンショットを撮影したい

Posted at

3行で要約

  • PowerShellスクリプト(ps1ファイル)でスクリーンショットを取得する方法の紹介と解説

  • PowerShellからDirectX使う方式でのスクリーンショット取得は難しい

  • ライブラリを使うと楽に実現可能

はじめに

仕事でスクリーンショットや画面の動画を撮ることが結構あります。

  • スクリーンショット:Windowsキー+PrintScreenキーでファイル保存
  • 動画:Windowsキー+Gキーで、Game Barを表示して動画保存

というWindowsの標準機能を私は使っているのですが、

  • 撮影ファイル名のカスタムをしたい
  • 数秒間の撮影が目的であるのにGame Barは高機能すぎる

という課題があったのでPowerShellで実現する方法を調査しました。

方式の検討

Windowsでスクリーンショットを取得する方法にはいくつか方法があります。
私が知っているのは以下の3つ

  1. PrintScreenキーを押下してクリップボードから画像を取り出す
    伝統的手法ですが、さすがに現代では使いたくない

  2. GDIのPrintWindow API
    PrintWindow APIはウィンドウ単位でキャプチャできるので便利な場合もあるのですがPowerShellからGDIの画像保存とかしたくない

  3. DirectXのWindows.Graphics.Capture
    Windows.Graphics.Capture APIは高速にスクリーンショット取得できるのですが他の候補と比べるとインターフェースが複雑

他の方式があるか検索したところDesktop Duplication APIというものもあるらしい。Windows.Graphics.Captureより機能が少ないようですので今回の対象外。

今回はWindows.Graphics.Capture使って保存する方式で調べてみました。

問題点:PowerShellからのDirectX呼び出しが困難

MS公式の解説を見ながらPowerShell+C#で実装してみたのですが、スクリーンショットのID3D11Texture2Dがうまく取得できない。
取得するインターフェースか構造体の定義が間違っているのだと思うがPowerShellからデバッグする方法がわからない。

ライブラリを探してみよう

DirectX11を直接実行するのが困難なのでライブラリを探してみました。
ScreenCapturerというライブラリがDirectXを使用して画像が取得できるので使いやすそう。

ScreenCapturerはSharpDXに依存してるようですので、必要なDLLの入手はnugetに頼りましょう。

ScreenCapturerの依存関係

面倒なのでnugetで依存関係DLLをまとめて入手します。

nugetコマンドがインストールされていない場合は、こちらからnuget.exeを入手してください。実行ファイルだけダウンロードできるのでインストーラーとかは不要です。

以下のコマンドを実行してダウンロードとコピーしてください。


>nuget install ScreenCapturer

()
Executing nuget actions took 578 ms

>dir /B
nuget.exe
ScreenCapturer.3.0.1
SharpDX.4.2.0
SharpDX.Direct3D11.4.2.0
SharpDX.DXGI.4.2.0
System.Drawing.Common.7.0.0


>copy /B ".\ScreenCapturer.3.0.1\lib\netstandard2.0\ScreenCapturer.dll" .\
        1 個のファイルをコピーしました。

>copy /B ".\SharpDX.4.2.0\lib\netstandard1.1\SharpDX.dll" .\
        1 個のファイルをコピーしました。

>copy /B ".\SharpDX.Direct3D11.4.2.0\lib\netstandard1.1\SharpDX.Direct3D11.dll"  .\
        1 個のファイルをコピーしました。

>copy /B ".\SharpDX.DXGI.4.2.0\lib\netstandard1.1\SharpDX.DXGI.dll" .\
        1 個のファイルをコピーしました。

>copy /B "./System.Drawing.Common.7.0.0\lib\net462\System.Drawing.Common.dll" .\
        1 個のファイルをコピーしました。


ソースコード

DLLと同じフォルダに以下のps1ファイルを配置して実行することでスクリーンショットが撮影できます。

screen_shot.ps1

$Refs = @(
"netstandard",
"System.Drawing",
".\System.Drawing.Common.dll",
".\ScreenCapturer.dll"
)

$Source = @"
using System;
using System.Drawing;
using ScreenCapturerNS;

public static class ScreenShot
{
    static Int32 count = 0;
    static Int32 displayIndex = 0;
    static Int32 adapterIndex = 0;

    public static void Start()
    {
        Console.WriteLine("Start");        
        ScreenCapturer.StartCapture(((System.Drawing.Bitmap bmp) => { 
                ScreenShot.count++; 
                bmp.Save("screenshot" + ScreenShot.count + ".png");            
            }),
            ScreenShot.displayIndex ,
            ScreenShot.adapterIndex );
    }

    public static void Stop()
    {
        Console.WriteLine("Stop");
        ScreenCapturer.StopCapture();
    }
}
"@

Add-Type -TypeDefinition $Source -Language CSharp -ReferencedAssemblies $Refs -Debug:$false 
$null = [System.Reflection.Assembly]::LoadFrom("./ScreenCapturer.dll")

# 開始
[ScreenShot]::Start()

# 1 秒スリープ
Start-Sleep -Seconds 1

# 停止
[ScreenShot]::Stop()

PowerShellというよりはC#で実現してる感じです。
Bitmapが取得できるのでクロップなどの編集もできます。
インターバルや撮影回数を指定したい場合もC#で書いてください。

おわりに

もう少し部品としての使いやすさを詰めたいけど、撮影できたので今回はこれで終わりにします。

参考情報

私PowerShellだけど…シリーズ

私PowerShellだけど、君のタスクトレイで暮らしたい
私PowerShellだけど「送る」からファイルを受け取りたい(コンテキストメニュー登録もあるよ)
私powershellだけどタスクトレイの片隅でアイを叫ぶ
私PowerShellだけど子を持つ親になるのはいろいろ大変そう
私PowerShellだけどあなたにトーストを届けたい(プログレスバー付)
私Powershellだけど日付とサイズでログを切り替えたい(log4net)
私PowerShellだけどスクリプトだけでサービス登録したい

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2