Who watches the watchmen ? - C#による画面キャプチャツール -

 ブログを本格的に書き始めて間もないが画面のキャプチャについて気づいたことがいくつかある.

一つは実行画面のキャプチャを貼るとぐっと見栄えが良くなること.

もう一つはキャプチャを取得するのが非常にめんどくさいことだ.

そこでC#の勉強がてら自動でキャプチャを取って保存してくれるツールを作ってみた.



欲しい機能は

  • タイマーで自動的に画面全体とアクティブなウィンドウのキャプチャを取得し,保存してくれる.
  • 手動での画面キャプチャ
  • いらなくなった画像の破棄

くらいである

いきなりだが「画面 キャプチャ C#」で検索してみる.

するといいものを見つけた.

ここ
画面をキャプチャする: .NET Tips: C#, VB.NET

今回はこれを参考にさせてもらおうっと(^^)v

画面のデザインはこうした

次にタイマーをツールボックスからフォームにドラッグアンドドロップ

イベントはForm1_Load, comboBox1_SelectedIndexChanged, button1_Click, button2_Click, button3_Click, timer1_Tickを定義した.

そしてForm1.csを編集

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

namespace AutoCapture
{
    public partial class Form1 : Form
    {
        private bool running = false;//実行中か否か
        private int sec = 5000;//sec秒に一回キャプチャを行う.
        private int fileNum = 0;//ファイルの番号

        CaptureWindow capturewindow = new CaptureWindow();

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            comboBox1.SelectedIndex = 4;//コンボボックスに初期値を設定しておく
        }

        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            sec = Convert.ToInt32(comboBox1.Text) * 1000;

            if (comboBox1.SelectedIndex < 4)
            {
                checkBox1.Checked = false;
                checkBox1.Enabled = false;
            }
            else
                checkBox1.Enabled = true;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            running = !running;
            if (running == true)
            {
                button1.Text = "Stop";
                button2.Enabled = false;
                button3.Enabled = false;
                timer1.Interval = sec;
                timer1.Start();
            }
            else
            {
                button1.Text = "Start";
                button2.Enabled = true;
                button3.Enabled = true;
                timer1.Stop();
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            CaptureAndSaveScreen();
        }

        private void button3_Click(object sender, EventArgs e)
        {   
            DialogResult result = MessageBox.Show("本当に削除しますか?",
                                                    "確認",
                                                    MessageBoxButtons.OKCancel,
                                                    MessageBoxIcon.Question);
            if (result == DialogResult.OK)
            {
                foreach(string str in System.IO.Directory.GetFiles(@".\", @"*.bmp"))
                {
                    System.IO.File.Delete(str);
                }
            }
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            if (checkBox1.Checked == true)
            {
                CaptureAndSaveScreen();
            }

            if (checkBox2.Checked == true)
            {
                CaptureAndSaveActiveWindow();
            }
        }

        private void CaptureAndSaveScreen()
        {
            Bitmap screen;
            Bitmap img = capturewindow.CaptureScreen();
            screen = new Bitmap(img);
            img.Dispose();
            screen.Save(fileNum.ToString() + @".bmp", ImageFormat.Bmp);
            screen.Dispose();
            fileNum++;
        }

        private void CaptureAndSaveActiveWindow()
        {
            Bitmap activeWindow;
            Bitmap img = capturewindow.CaptureActiveWindow();
            activeWindow = new Bitmap(img);
            img.Dispose();
            activeWindow.Save(fileNum.ToString() + @".bmp", ImageFormat.Bmp);
            activeWindow.Dispose();
            fileNum++;
        }
    }

    class CaptureWindow
    {
        private const int SRCCOPY = 13369376;

        [DllImport("user32.dll")]
        private static extern IntPtr GetDC(IntPtr hwnd);

        [DllImport("gdi32.dll")]
        private static extern int BitBlt(IntPtr hDestDC,
            int x,
            int y,
            int nWidth,
            int nHeight,
            IntPtr hSrcDC,
            int xSrc,
            int ySrc,
            int dwRop);

        [StructLayout(LayoutKind.Sequential)]
        private struct RECT
        {
            public int left;
            public int top;
            public int right;
            public int bottom;
        }

        [DllImport("user32.dll")]
        private static extern IntPtr ReleaseDC(IntPtr hwnd, IntPtr hdc);

        [DllImport("user32.dll")]
        private static extern IntPtr GetWindowDC(IntPtr hwnd);

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

        [DllImport("user32.dll")]
        private static extern int GetWindowRect(IntPtr hwnd,
            ref  RECT lpRect);

        public Bitmap CaptureScreen()
        {
            //プライマリモニタのデバイスコンテキストを取得
            IntPtr disDC = GetDC(IntPtr.Zero);
            //Bitmapの作成
            Bitmap bmp = new Bitmap(Screen.PrimaryScreen.Bounds.Width,
                Screen.PrimaryScreen.Bounds.Height);
            //Graphicsの作成
            Graphics g = Graphics.FromImage(bmp);
            //Graphicsのデバイスコンテキストを取得
            IntPtr hDC = g.GetHdc();
            //Bitmapに画像をコピーする
            BitBlt(hDC, 0, 0, bmp.Width, bmp.Height,
                disDC, 0, 0, SRCCOPY);
            //解放
            g.ReleaseHdc(hDC);
            g.Dispose();
            ReleaseDC(IntPtr.Zero, disDC);

            return bmp;
        }

        public Bitmap CaptureActiveWindow()
        {
            //アクティブなウィンドウのデバイスコンテキストを取得
            IntPtr hWnd = GetForegroundWindow();
            IntPtr winDC = GetWindowDC(hWnd);
            //ウィンドウの大きさを取得
            RECT winRect = new RECT();
            GetWindowRect(hWnd, ref winRect);
            //Bitmapの作成
            Bitmap bmp = new Bitmap(winRect.right - winRect.left,
                winRect.bottom - winRect.top);
            //Graphicsの作成
            Graphics g = Graphics.FromImage(bmp);
            //Graphicsのデバイスコンテキストを取得
            IntPtr hDC = g.GetHdc();
            //Bitmapに画像をコピーする
            BitBlt(hDC, 0, 0, bmp.Width, bmp.Height,
                winDC, 0, 0, SRCCOPY);
            //解放
            g.ReleaseHdc(hDC);
            g.Dispose();
            ReleaseDC(hWnd, winDC);

            return bmp;
        }
    }
}

 Startボタンを押すとコンボボックスで選択した値をインターバルとして自動的にキャプチャを取得する,テスクトップとアクティブなウィンドウの画像の取得が可能.
 しかしインターバルが5秒以下だとデスクトップの画像の取得が間に合わない可能性があるため,コンボボックスで5以下が選択された場合はデスクトップをチェックできないようにした.画像はexeと同一ディレクトリに保存される.

 CaptureDesktopボタンを押すと手動でデスクトップのキャプチャを撮ることができる.

 Deleteボタンを押すと撮った画像をすべて破棄する.ごみ箱に移動せず,そのまま破棄する.

 CapureAndSaveScreenメソッドとCaptureAndSaveActiveWindowメソッドで一回一回画像を保存しているのはさすがにまずかったかな?(^^;)