Windows GUIプログラミング入門26 画面キャプチャ

# ■はじめに

今回は画面全体のスクリーンショットを撮り、jpeg画像として保存するアプリケーションを作ります。

# ■開発環境

* Windows 10
* Visual Studio Community 2019 （Version 16.6.5）
* .NET Framework 4.8 / .NET Core 3.1

# ■.NET Framework版のみの作業

新しいプロジェクトで`WPFアプリ (.NET Framework) C#`を選択します。

ソリューションエクスプローラーで`参照`を右クリックし、`参照の追加`を選択します。
![11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142879/6cae412e-d154-6ab0-2558-5aa200b58802.png)
アセンブリで`System.Drawing`のチェックボックスをチェックし、`OK`ボタンを選択します。

# ■.NET Core版のみの作業

新しいプロジェクトで`WPF App (.NET Core) C#`を選択します。

ソリューションエクスプローラーでプロジェクトを右クリック、`NuGetパッケージの管理`を選択します。
![22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142879/0db80123-226d-a09b-04f9-92278ff87c84.png)

`System.Drawing.Common`をインストールします。
![23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142879/89bce8ac-0eb8-351f-f70c-47e3de159c1d.png)

# ■作成（.NET Framework/.NET Core共通）

## ◇画面

![31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142879/ab0b5120-5dbe-e632-da99-a3b4a477ec5f.png)

```xml:MainWindow.xaml
<Window
    x:Class="ScreenShot.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:ScreenShotCore"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Title="キャプチャ"
    Width="640"
    Height="400"
    ResizeMode="CanResizeWithGrip"
    WindowStyle="ToolWindow"
    mc:Ignorable="d">
    <Window.Resources>
        <!-- ボタンの既定スタイル -->
        <Style TargetType="Button">
            <Setter Property="Width" Value="150" />
            <Setter Property="Margin" Value="10" />
            <Setter Property="Padding" Value="5" />
        </Style>
    </Window.Resources>

    <Grid Background="SlateGray">
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <Viewbox>
            <Image x:Name="CaptureImage" Margin="5" />
        </Viewbox>
        <TextBlock
            x:Name="SaveInfo"
            Margin="10"
            HorizontalAlignment="Center"
            VerticalAlignment="Center"
            FontSize="60"
            Text="保存しました。"
            Visibility="Collapsed" />

        <StackPanel
            Grid.Row="1"
            Background="LightSteelBlue"
            Orientation="Horizontal">
            <Button Click="CaptureButton_Click" Content="キャプチャ(_1)" />
            <Button Click="SaveButton_Click" Content="保存(_2)" />
            <Button Click="CaptureAndSaveButton_Click" Content="キャプチャして保存(_3)" />
        </StackPanel>
    </Grid>
</Window>
```

## ◇コード

```CSharp:MainWindow.xaml.cs
using System;
using System.IO;
using System.Threading.Tasks;
using System.Windows;
using DIMG = System.Drawing.Imaging;
using DRW = System.Drawing;
using MIMG = System.Windows.Media.Imaging;

namespace ScreenShot
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        /// <summary>
        /// キャプチャ時、自ウィンドウを隠すための待ち時間
        /// </summary>
        private static readonly int CaptureWaitMSec = 300;

        [System.Runtime.InteropServices.DllImport("gdi32.dll")]
        private static extern bool DeleteObject(IntPtr objectHandle);

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public MainWindow()
        {
            InitializeComponent();
        }

        /// <summary>
        /// キャプチャボタン押下
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void CaptureButton_Click(object sender, RoutedEventArgs e)
        {
            CaptureImage.Source = null;
            // ウィンドウ最小化
            WindowState = WindowState.Minimized;
            // ウィンドウが最小化するまで少し待つ
            await Task.Delay(CaptureWaitMSec);
            // キャプチャ
            CaptureImage.Source = GetFullScreenImage();
            // ウィンドウを元に戻す
            WindowState = WindowState.Normal;
        }

        /// <summary>
        /// 保存ボタン押下
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void SaveButton_Click(object sender, RoutedEventArgs e)
        {
            if (CaptureImage.Source == null)
            {
                MessageBox.Show("まずキャプチャしてください。", "警告", MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            // 画像保存
            SaveImage();
            // 保存通知
            await NotifySaveImage();
        }

        /// <summary>
        /// キャプチャして保存ボタン押下
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void CaptureAndSaveButton_Click(object sender, RoutedEventArgs e)
        {
            // ウィンドウ最小化
            WindowState = WindowState.Minimized;
            // ウィンドウが最小化するまで少し待つ
            await Task.Delay(CaptureWaitMSec);
            // キャプチャ
            CaptureImage.Source = GetFullScreenImage();
            // 画像保存
            SaveImage();
            // ウィンドウを元に戻す
            WindowState = WindowState.Normal;
            // 保存通知
            await NotifySaveImage();
        }

        /// <summary>
        /// 画面全体のイメージ取得
        /// </summary>
        /// <returns></returns>
        private MIMG.BitmapSource GetFullScreenImage()
        {
            int w = (int)SystemParameters.PrimaryScreenWidth;
            int h = (int)SystemParameters.PrimaryScreenHeight;

            using (var bmp = new DRW.Bitmap(w, h, DIMG.PixelFormat.Format32bppRgb))
            using (var grph = DRW.Graphics.FromImage(bmp))
            {
                // スクリーンイメージをコピー
                grph.CopyFromScreen(
                    sourceX: 0, sourceY: 0,
                    destinationX: 0, destinationY: 0,
                    bmp.Size);

                IntPtr bmpHandle = bmp.GetHbitmap();

                try
                {
                    return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
                        bmpHandle,
                        IntPtr.Zero,
                        Int32Rect.Empty,
                        MIMG.BitmapSizeOptions.FromEmptyOptions());
                }
                finally
                {
                    DeleteObject(bmpHandle);
                }
            }
        }

        /// <summary>
        /// 画像ファイルを保存
        /// </summary>
        private void SaveImage()
        {
            // ピクチャフォルダのスクリーンショットフォルダ
            string saveFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "Screenshots");
            string saveFilePath = Path.Combine(saveFolder, $"スクショ{DateTime.Now:yyyyMMdd-HHmmss}.jpeg");

            if (!Directory.Exists(saveFolder))
            {
                // 保存先フォルダが無ければ作成
                Directory.CreateDirectory(saveFolder);
            }

            // 画像ファイル保存
            using (Stream stream = new FileStream(saveFilePath, FileMode.Create))
            {
                var encoder = new MIMG.JpegBitmapEncoder();
                encoder.Frames.Add(MIMG.BitmapFrame.Create((MIMG.BitmapSource)CaptureImage.Source));
                encoder.Save(stream);
            }
        }

        /// <summary>
        /// 保存通知
        /// </summary>
        /// <returns></returns>
        /// <remarks>
        /// 画像保存メッセージを表示し、画面の画像をクリアする。
        /// </remarks>
        private async Task NotifySaveImage()
        {
            // 保存通知
            SaveInfo.Visibility = Visibility.Visible;
            await Task.Delay(250);
            // 画像クリア
            CaptureImage.Source = null;
            await Task.Delay(250);
            // 保存通知隠す
            SaveInfo.Visibility = Visibility.Collapsed;
        }

    }
}
```

# ■実行してみる

![41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142879/0812f0e7-b003-b79f-a28a-e5c340b8df86.png)

`キャプチャ`ボタンを押しました。
一瞬、画面が隠れて全画面のスクリーンショットが画面に表示されます。
![42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142879/5c11afe8-fb56-e5e4-a732-2afebc1c0146.png)

画面のサイズを拡大すると、画像も引き伸ばされます。
![43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142879/d8a2efba-0fee-fe9d-6dbd-ddaccd9e0441.png)

`保存`ボタンを押すと画面に表示されている画像が「ピクチャ」フォルダの「スクリーンショット」フォルダに保存されます。
![45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142879/b823d998-4544-d8e0-f2bc-9f234a55bab9.png)

![46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142879/2fe8aead-3c0b-e62c-f2e1-4505af231c92.png)

おしまい

---

[＜＜ 最初の記事](http://qiita.com/Kosen-amai/items/f9e3df2aa80363f5af5b "Windows GUIプログラミング入門1 メッセージボックス表示") 　 [＜ 前の記事](https://qiita.com/Kosen-amai/items/e31b62a6f892cb5a7c14 "Windows GUIプログラミング入門25 コード整形") 　 [次の記事 ＞](https://qiita.com/Kosen-amai/items/8a5b89f6d7237c5cd671 "Windows GUIプログラミング入門27 ヘルプ画面")