もっと詳しく

少し前に公開した「C#でスクリーン(画面) キャプチャするには」の記事で、スクリーンをキャプチャする方法と、スクリーンキャプチャクラス(ScreenCapture)を紹介しました。

その中で、キャプチャ画像をファイルに保存する方法について少しだけ触れました。

その後スクリーンキャプチャツールを自作する中で、キャプチャ画像のファイル保存を目的としたヘルパークラスを作成したので、紹介させていただきます。

なお、この記事の最後に紹介するソースコードは、ScreenCaptureクラスも含んでいますので、そのままコピペしてお使いいただけます。

概要

ScreenCaptureSaver クラスは、 ScreenCaptureクラスを継承して作成しています。

基本的には ScreenCapture クラスで用意した以下の4つの機能について、取得したキャプチャ画像をそのままファイルに保存するメソッドを用意しています。

  • 指定区画のキャプチャ
  • プライマリスクリーンのキャプチャ
  • アクティブウィンドウのキャプチャ
  • スクリーン番号で指定したスクリーンのキャプチャ

更に、以下のメソッドを追加しています。

  • 指定区画のキャプチャ画像をクリップボードにコピー
  • クリップボードの画像をファイルに保存
  • 指定したBitmapイメージのファイル保存

キャプチャ画像を保存するためのファイル名については、指定フォルダに連番付きで次々と保存したいため、空き連番を探すようにしています(CreateFileNameメソッド)。

連番の開始番号はコンストラクタの引数で指定できますが、省略時は0から開始します。

クラスのリファレンス

ScreenCaptureSaverクラスのメソッドを一覧にまとめました。

Saveから始まるメソッドには、フォルダ名(folder)、ファイル名(fileName)、フォーマット(format)、イメ今回はイプ(imageType)、Jpeg品質(Quality)が指定できるようになっています。

コンストラクタかプロパティで指定できるようにすれば、各メソッドの引数を減らすことも可能ですが、今回はメソッドごとに指定するようにしています。

内容 メソッド名と引数 戻り値
コンストラクタ ScreenCaptureSaver(
int num=0 //ファイル連番の開始値
)
なし
指定区画のキャプチャ画像
をファイルに保存
void SaveRect(
int x1, int y1, int x2, int y2, //対象座標
string folder, //フォルダ名
string fileName, //ファイル名(拡張子は除く)
string format = “_{0:000}”, //連番部のフォーマット
string imageType = “jpg”, //イメージの種類
int quality = 100 //Jpgの品質
)
なし
全スクリーンのキャプチャ画像
をファイルに保存
void SaveAll(
string folder, //フォルダ名
string fileName, //ファイル名(拡張子は除く)
string format = “_{0:000}”, //連番部のフォーマット
string imageType = “jpg”, //イメージの種類
int quality = 100 //Jpgの品質
)
なし
プライマリスクリーンの
キャプチャ画像をファイルに保存
void SavePrymary(
string folder, //フォルダ名
string fileName, //ファイル名(拡張子は除く)
string format = “_{0:000}”, //連番部のフォーマット
string imageType = “jpg”, //イメージの種類
int quality = 100 //Jpgの品質
)
なし
アクティブウィンドウの
キャプチャ画像をファイルに保存
void SaveActive(
string folder, //フォルダ名
string fileName, //ファイル名(拡張子は除く)
string format = “_{0:000}”, //連番部のフォーマット
string imageType = “jpg”, //イメージの種類
int quality = 100 //Jpgの品質
)
なし
クリップボードの画像を
ファイルに保存
void SaveClipboard(
string folder, //フォルダ名
string fileName, //ファイル名(拡張子は除く)
string format = “_{0:000}”, //連番部のフォーマット
string imageType = “jpg”, //イメージの種類
int quality = 100 //Jpgの品質
)
なし
指定区画のキャプチャ画像を
クリップボードにコピー
void ToClipboard(
int x1, int y1, int x2, int y2 //対象座標
)
なし
指定したスクリーン番号の
キャプチャ画像をファイルに保存
void SaveScreen(
int no //スクリーン番号
string folder, //フォルダ名
string fileName, //ファイル名(拡張子は除く)
string format = “_{0:000}”, //連番部のフォーマット
string imageType = “jpg”, //イメージの種類
int quality = 100 //Jpgの品質
)
なし
指定したBitmapイメージを
ファイルに保存
void Save(
Bitmap img //Bitmapイメージ
string folder, //フォルダ名
string fileName, //ファイル名(拡張子は除く)
string format = “_{0:000}”, //連番部のフォーマット
string imageType = “jpg”, //イメージの種類
int quality = 100 //Jpgの品質
)
なし
指定したBitmapイメージを
ファイルに保存
void Save(
Bitmap img //Bitmapイメージ
string fileName, //ファイル名(拡張子は除く)
string imageType = “jpg”, //連番部のイメージの種類
int quality = 100 //Jpgの品質
)
なし
連番を含むファイル名の生成 string CreateFileName(
string folder, //フォルダ名
string fileName, //ファイル名(拡張子は除く)
string format = “_{0:000}”, //連番部のフォーマット
string ext = “jpg” //拡張子
)
ファイル名

ソースコード一式

C#でスクリーン(画面) キャプチャするには」の記事で紹介した ScreenCapture クラスのソースコードを全て含んでいます。

ソースコードの前半が今回作成した ScreenCaptureSaverクラス、後半が ScreenCapture になっています。

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Drawing.Imaging;


namespace WinCapture
{
    ///============================================================================
    /// 以下は、新しく作成したヘルパークラス(ScreenCaptureを継承)
    ///============================================================================
    /// <summary>
    /// キャプチャ画像のファイル保存クラス
    /// </summary>
    public class ScreenCaptureSaver : ScreenCapture
    {
        //ファイル連番の開始番号
        private int _startNum = 0;

        //コンストラクタ
        public ScreenCaptureSaver(int num = 0)
        {
            _startNum = num;
        }

        /// <summary>
        /// 指定区画の保存
        /// </summary>
        /// <param name="x1"></param>
        /// <param name="y1"></param>
        /// <param name="x2"></param>
        /// <param name="y2"></param>
        /// <param name="folder"></param>
        /// <param name="fileName"></param>
        /// <param name="format"></param>
        /// <param name="imageType"></param>
        /// <param name="quality"></param>
        public void SaveRect(int x1, int y1, int x2, int y2,string folder, string fileName, string format = "_{0:000}", string imageType = "jpg", int quality = 100)
        {
            Save(Capture(x1,y1,x2,y2), folder, fileName, format, imageType, quality);
        }

        /// <summary>
        /// 全スクリーンの保存
        /// </summary>
        /// <param name="folder"></param>
        /// <param name="fileName"></param>
        /// <param name="format"></param>
        /// <param name="imageType"></param>
        /// <param name="quality"></param>
        public void SaveAll(string folder, string fileName, string format = "_{0:000}", string imageType = "jpg", int quality = 100)
        {
            Save(GetAllScreen(), folder, fileName, format, imageType, quality);
        }

        /// <summary>
        /// プライマリスクリーンの保存
        /// </summary>
        /// <param name="folder"></param>
        /// <param name="fileName"></param>
        /// <param name="format"></param>
        /// <param name="imageType"></param>
        /// <param name="quality"></param>
        public void SavePrymary(string folder, string fileName, string format = "_{0:000}", string imageType = "jpg", int quality = 100)
        {
            Save(GetPrymaryScreen(), folder, fileName, format, imageType, quality);
        }

        /// <summary>
        /// アクティブウィンドウの保存
        /// </summary>
        /// <param name="folder"></param>
        /// <param name="fileName"></param>
        /// <param name="format"></param>
        /// <param name="imageType"></param>
        /// <param name="quality"></param>
        public void SaveActive(string folder, string fileName, string format = "_{0:000}", string imageType = "jpg", int quality = 100)
        {
            Save(GetActiveWindow(), folder, fileName, format, imageType, quality);
        }

        /// <summary>
        /// クリップボードの画像を保存
        /// </summary>
        /// <param name="folder"></param>
        /// <param name="fileName"></param>
        /// <param name="format"></param>
        /// <param name="imageType"></param>
        /// <param name="quality"></param>
        public void SaveClipboard(string folder, string fileName, string format = "_{0:000}", string imageType = "jpg", int quality = 100)
        {
            Save((Bitmap)System.Windows.Forms.Clipboard.GetImage(), folder, fileName, format, imageType, quality);
        }

        /// <summary>
        /// クリップボードに画像をコピー
        /// </summary>
        /// <param name="x1"></param>
        /// <param name="y1"></param>
        /// <param name="x2"></param>
        /// <param name="y2"></param>
        public void ToClipboard(int x1, int y1, int x2, int y2)
        {
            //キャプチャ画像をそのままクリップボードにセットすると
            //白い点があちこちにノイズで残る場合があるので
            //一旦ファイルに保存したものを読み出してセット
            var temp = System.IO.Path.GetTempFileName();
            Save(Capture(x1, y1, x2, y2),temp);
            var img = System.Drawing.Image.FromFile(temp);
            System.Windows.Forms.Clipboard.SetImage(img);
            img.Dispose();
            System.IO.File.Delete(temp);
        }
        /// <summary>
        /// 指定したスクリーン領域の保存
        /// </summary>
        /// <param name="no"></param>
        /// <param name="folder"></param>
        /// <param name="fileName"></param>
        /// <param name="format"></param>
        /// <param name="imageType"></param>
        /// <param name="quality"></param>
        public void SaveScreen(int no,string folder, string fileName, string format = "_{0:000}", string imageType = "jpg", int quality = 100)
        {
            var img = GetScreen(GetScreenList()[no]);
            Save(img,folder,fileName,format,imageType, quality);
        }

        /// <summary>
        /// 指定したBitmapの保存
        /// </summary>
        /// <param name="img"></param>
        /// <param name="folder"></param>
        /// <param name="fileName"></param>
        /// <param name="format"></param>
        /// <param name="imageType"></param>
        /// <param name="quality"></param>
        public void Save(Bitmap img, string folder, string fileName, string format = "_{0:000}", string imageType = "jpg", int quality = 100)
        {
            Save(img, CreateFileName(folder, fileName, format, imageType), imageType, quality);
        }

        /// <summary>
        /// 指定したBitmapの保存
        /// </summary>
        /// <param name="img"></param>
        /// <param name="fileName"></param>
        /// <param name="format"></param>
        /// <param name="quality"></param>
        public void Save(Bitmap img, string fileName, string imageType = "jpg", int quality = 100)
        {
            if (img == null)
            {
                return;
            }

            switch (imageType)
            {
                case "jpg":
                    var parameters = new EncoderParameters(1);
                    parameters.Param[0] = new EncoderParameter(Encoder.Quality, quality);
                    var jpeg_encoder = ImageCodecInfo.GetImageEncoders().FirstOrDefault(i => i.MimeType == "image/jpeg");
                    img.Save(fileName, jpeg_encoder, parameters);
                    break;
                case "bmp":
                    img.Save(fileName, ImageFormat.Bmp);
                    break;
                case "png":
                    img.Save(fileName, ImageFormat.Png);
                    break;
                case "gif":
                    img.Save(fileName, ImageFormat.Gif);
                    break;
            }
        }

        /// <summary>
        /// ファイル名とフォーマットから連番付きファイル名を生成する。
        /// 既にファイルが存在する場合、連番を順次進めてファイル名を生成する。
        /// </summary>
        /// <param name="folder"></param>
        /// <param name="fileName"></param>
        /// <param name="format"></param>
        /// <param name="ext"></param>
        /// <param name="num"></param>
        /// <returns></returns>
        public string CreateFileName(string folder, string fileName, string format, string ext)
        {
            int fileno = _startNum;

            while (true)
            {
                try
                {
                    var path = System.IO.Path.Combine(folder, string.Format(fileName + format + "." + ext, fileno++));

                    if (!System.IO.File.Exists(path))
                    {
                        return path;
                    }
                }
                catch
                {
                    fileName = "Image_" + DateTime.Now.ToString("yyyyMMdd_ff");
                }
            }
        }
    }

    ///============================================================================
    /// 以下は、「C#でスクリーン(画面) キャプチャするには」の記事で紹介した内容
    ///============================================================================
    /// <summary>
    /// スクリーンキャプチャクラス
    /// </summary>
    public class ScreenCapture
    {
        // ウィンドウの境界情報を取得するための定数
        int DWMWA_EXTENDED_FRAME_BOUNDS = 9;

        [StructLayout(LayoutKind.Sequential)]
        private struct WindowRect
        {
            public int X1;
            public int Y1;
            public int X2;
            public int Y2;
        }


        [DllImport("user32.dll")]
        extern static IntPtr GetForegroundWindow();

        [DllImport("dwmapi.dll")]
        extern static int DwmGetWindowAttribute(IntPtr hWnd, int dwAttribute, out WindowRect rect, int cbAttribute);


        /// <summary>
        /// 全てのスクリーンを取得
        /// </summary>
        /// <returns></returns>
        public Screen[] GetScreenList()
        {
            return System.Windows.Forms.Screen.AllScreens.ToArray();
        }


        /// <summary>
        /// 全てのスクリーンを1つに合わせた座標を取得する
        /// </summary>
        /// <returns></returns>
        public (int x1, int y1, int x2, int y2) GetAllScreenRec()
        {
            var rect = new Rectangle(0, 0, 0, 0);

            // プライマリスクリーン全体の座標を取得
            foreach (var screen in GetScreenList())
            {
                //Console.WriteLine(s.Bounds);
                rect.X = Math.Min(rect.X, screen.Bounds.X);
                rect.Y = Math.Min(rect.Y, screen.Bounds.Y);
                rect.Width = Math.Max(rect.Width, screen.Bounds.Right);
                rect.Height = Math.Max(rect.Height, screen.Bounds.Bottom);
            }
            return (rect.X, rect.Y, rect.Width, rect.Height);
        }

        /// <summary>
        /// 指定した区画をキャプチャする
        /// </summary>
        /// <param name="x1"></param>
        /// <param name="y1"></param>
        /// <param name="x2"></param>
        /// <param name="y2"></param>
        /// <returns></returns>
        public Bitmap Capture(int x1, int y1, int x2, int y2)
        {
            (x1, x2) = (x1 > x2) ? (x2, x1) : (x1, x2);
            (y1, y2) = (y1 > y2) ? (y2, y1) : (y1, y2);

            var rect = new Rectangle(x1, y1, x2 - x1, y2 - y1);

            if (rect.Width * rect.Height > 0)
            {
                var bmp = new Bitmap(rect.Width, rect.Height);
                var graphics = Graphics.FromImage(bmp);
                graphics.CopyFromScreen(rect.X, rect.Y, 0, 0, rect.Size);
                graphics.Dispose();
                return bmp;
            }
            return null;
        }

        /// <summary>
        /// マルチスクリーン全体をキャプチャ
        /// </summary>
        /// <returns></returns>
        public Bitmap GetAllScreen()
        {
            var rect = GetAllScreenRec();
            return Capture(rect.x1, rect.y1, rect.x2, rect.y2);
        }

        /// <summary>
        /// プライマリスクリーン全体をキャプチャする
        /// </summary>
        /// <returns></returns>
        public Bitmap GetPrymaryScreen()
        {
            // プライマリスクリーン全体をキャプチャ
            return Capture(0, 0, Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
        }

        /// <summary>
        /// 指定したスクリーン全体をキャプチャする
        /// </summary>
        /// <param name="screen"></param>
        /// <returns></returns>
        public Bitmap GetScreen(Screen screen)
        {
            // 指定したスクリーンをキャプチャ
            return Capture(0, 0, screen.Bounds.Width, screen.Bounds.Height);
        }

        /// <summary>
        /// アクティブウィンドウをキャプチャする
        /// </summary>
        /// <returns></returns>
        public Bitmap GetActiveWindow()
        {
            // アクティブウィンドウを取得
            IntPtr window_handle = GetForegroundWindow();

            //ウィンドウ表示エリアの座標を取得
            DwmGetWindowAttribute(window_handle, DWMWA_EXTENDED_FRAME_BOUNDS, out WindowRect wr, Marshal.SizeOf(typeof(WindowRect)));

            //キャプチャの実行
            return Capture(wr.X1, wr.Y1, wr.X2, wr.Y2);
        }

    }
}

クリップボードへのコピーについて

キャプチャ画像をクリップボードへコピーする処理については、キャプチャ画像を一時ファイルに保存した後で、それを再度読み込んでからクリップボードにセットするという回りくどい方法を採用しています。

var temp = System.IO.Path.GetTempFileName();
Save(Capture(x1, y1, x2, y2),temp);
var img = System.Drawing.Image.FromFile(temp);
System.Windows.Forms.Clipboard.SetImage(img);
img.Dispose();
System.IO.File.Delete(temp);

本来は、次の1行で問題は無いなずなのですが、これだとクリップボードにセットした画像が、環境によってはノイズまみれになってしまう問題が発生しました。

System.Windows.Forms.Clipboard.SetImage(Capture(x1,y1,x2,y2))

別のPCで試したところ現象は発生しなかったので、私が使っている環境の問題かもしれませんが、回避策として見つけたのがファイルを経由してクリップボードにセットするという方法でした。

まとめ

今回は以前紹介した ScreenCaptureクラスを継承し、キャプチャ画像のファイル保存を行う ScreenCaptureSaverクラスを紹介しました。

私のブログ記事に掲載する画像を、効率的に行うためにスクリーンキャプチャツールを作ろうと思い、その延長上で今回のクラスが出来上がりました。

みなさんにとって、あまり必然性は無いかもしれませんが、もしプログラムでキャプチャ画像を簡単にファイル保存したいというニーズが発生したら、是非この記事を参考にしてください。

スクリーンキャプチャツールが完成した際には、また別の記事で紹介させていただきます。