DOBON.NET プログラミング道: .NET Framework, VB.NET, C#, Visual Basic, Visual Studio, インストーラ, ...

画面をキャプチャする

画面をキャプチャ(ハードコピー)し、イメージをBitmapオブジェクトとして取得する方法を幾つか紹介します。

.NET Framework 2.0以降で、Graphics.CopyFromScreenメソッドを使用する方法

.NET Framework 2.0から追加されたGraphics.CopyFromScreenメソッドを使用すれば、画面上の指定された範囲をキャプチャすることができます。

画面全体をキャプチャしてPictureBox1に表示する例を以下に示します。

VB.NET
コードを隠すコードを選択
'Imports System.Drawing
'Imports System.Windows.Forms

'Bitmapの作成
Dim bmp As New Bitmap(Screen.PrimaryScreen.Bounds.Width, _
    Screen.PrimaryScreen.Bounds.Height)
'Graphicsの作成
Dim g As Graphics = Graphics.FromImage(bmp)
'画面全体をコピーする
g.CopyFromScreen(New Point(0, 0), New Point(0, 0), bmp.Size)
'解放
g.Dispose()

'表示
PictureBox1.Image = bmp
C#
コードを隠すコードを選択
//using System.Drawing;
//using System.Windows.Forms;

//Bitmapの作成
Bitmap bmp = new Bitmap(Screen.PrimaryScreen.Bounds.Width,
    Screen.PrimaryScreen.Bounds.Height);
//Graphicsの作成
Graphics g = Graphics.FromImage(bmp);
//画面全体をコピーする
g.CopyFromScreen(new Point(0, 0), new Point(0, 0), bmp.Size);
//解放
g.Dispose();

//表示
PictureBox1.Image = bmp;
注意:.NET Framework 3.5以下のCopyFromScreenメソッドには、ハンドルリークのバグがあります(「System.ArgumentException: Parameter is not valid. at System.Drawing.Graphics.GetHdc()」によると、.NET Framework 2.0 SP1と3.5にバグがあるということです)。詳しくは、「Need help confirming possible bug in Graphics.CopyFromScreen regarding leakage of Gdi object handle」をご覧ください。

CopyFromScreenメソッドにはもう1つバグがあります。それは、CopyFromScreenメソッドの最後に指定できるCopyPixelOperation構造体のパラメータに、CopyPixelOperation値の1つしか指定できない点です。例えばここに「CopyPixelOperation.SourceCopy Or CopyPixelOperation.CaptureBlt」を指定すると、例外InvalidEnumArgumentExceptionが発生します。そのため、Opacityプロパティで半透明にしたフォームや、TransparencyKeyプロパティで一部を透明にしたフォームを正しくキャプチャすることができません。下で紹介するWin32 APIの方法では、BitBltの最後のパラメータを「SRCCOPY Or CAPTUREBLT」(C#では「SRCCOPY | CAPTUREBLT」)とすることで、これらのフォームも正常にキャプチャできます。詳しくは、「copyFromScreen - no alpha window captured」をご覧ください。

Print Screenキーストロークを送信する方法

Print ScreenキーストロークをSendKeys.SendWaitメソッド(またはSendKeys.Sendメソッド)で送信し、クリップボードにコピーされたイメージを取得する方法も考えられます。Ctrl+PrintScreenキーストローク("^{PRTSC}")で画面全体がキャプチャされ、Alt+PrintScreenキーストローク("%{PRTSC}")でアクティブなウィンドウがキャプチャされます(PrintScreenキーストロークのみでもアクティブなウィンドウがキャプチャされるようです)。

ただしこの方法では確実に画面のイメージを取得できる保障はありません。

補足:MSDNの「SendKeys.Send メソッド」には、「{PRTSC} (今後使用するために予約されている)」と書かれています。

SendKeysに関しては、「VB6のSendKeysと同じ事を行うには?」でも説明しています。また、クリップボードの画像を取得する方法は、「クリップボードの画像を表示する」で説明しています。

この方法により画面全体のイメージを取得し、PictureBox(PictureBox1)に表示するサンプルを以下に示します。

VB.NET
コードを隠すコードを選択
'Imports System.Drawing
'Imports System.Windows.Forms

'画面全体のイメージをクリップボードにコピー
SendKeys.SendWait("^{PRTSC}")
'次のようにすると、アクティブなウィンドウのイメージをコピー
'SendKeys.SendWait("%{PRTSC}")
'SendKeys.SendWait("{PRTSC}")

'DoEventsを呼び出したほうがよい場合があるらしい
'Application.DoEvents()

'クリップボードにあるデータの取得
Dim d As IDataObject = Clipboard.GetDataObject()
'クリップボードにデータがあったか確認
If d IsNot Nothing Then
    'ビットマップデータ形式に関連付けられているデータを取得
    Dim img As Image = DirectCast(d.GetData(DataFormats.Bitmap), Image)
    If img IsNot Nothing Then
        'データが取得できたときはPictureBoxに表示する
        PictureBox1.Image = img

        '画面のイメージデータは大きいため、
        '用がなくなればクリップボードから削除した方がいいかもしれない
        Clipboard.SetDataObject(New DataObject())
    End If
End If
C#
コードを隠すコードを選択
//using System.Drawing;
//using System.Windows.Forms;

//画面全体のイメージをクリップボードにコピー
SendKeys.SendWait("^{PRTSC}");
//次のようにすると、アクティブなウィンドウのイメージをコピー
//SendKeys.SendWait("%{PRTSC}");
//SendKeys.SendWait("{PRTSC}");

//DoEventsを呼び出したほうがよい場合があるらしい
//Application.DoEvents();

//クリップボードにあるデータの取得
IDataObject d = Clipboard.GetDataObject();
//クリップボードにデータがあったか確認
if (d != null)
{
    //ビットマップデータ形式に関連付けられているデータを取得
    Image img = (Image)d.GetData(DataFormats.Bitmap);
    if (img != null)
    {
        //データが取得できたときはPictureBoxに表示する
        PictureBox1.Image = img;

        //画面のイメージデータは大きいため、
        //用がなくなればクリップボードから削除した方がいいかもしれない
        Clipboard.SetDataObject(new DataObject());
    }
}

Win32 APIを使用する方法

最後に、Win32 APIを使う方法を紹介します。この方法関しては、ウェブ上で優れたサンプルがいくつも公開されています。その一部を紹介します。

以下はC#のサンプルです。

以下はVB.NETのサンプルです。

以下に画面全体をキャプチャするメソッド(CaptureScreen)と、アクティブなウィンドウをキャプチャするメソッド(CaptureActiveWindow)のごく簡単なサンプルを紹介します。

VB.NET
コードを隠すコードを選択
'Imports System.Drawing
'Imports System.Windows.Forms
'Imports System.Runtime.InteropServices

Const SRCCOPY As Integer = 13369376
Const CAPTUREBLT As Integer = 1073741824

<DllImport("user32.dll")> _
Private Shared Function GetDC(ByVal hwnd As IntPtr) As IntPtr
End Function

<DllImport("gdi32.dll")> _
Private Shared Function BitBlt(ByVal hDestDC As IntPtr, _
    ByVal x As Integer, ByVal y As Integer, _
    ByVal nWidth As Integer, ByVal nHeight As Integer, _
    ByVal hSrcDC As IntPtr, _
    ByVal xSrc As Integer, ByVal ySrc As Integer, _
    ByVal dwRop As Integer) As Integer
End Function

<DllImport("user32.dll")> _
Private Shared Function ReleaseDC(ByVal hwnd As IntPtr, _
    ByVal hdc As IntPtr) As IntPtr
End Function

''' <summary>
''' プライマリスクリーンの画像を取得する
''' </summary>
''' <returns>プライマリスクリーンの画像</returns>
Public Shared Function CaptureScreen() As Bitmap
    'プライマリモニタのデバイスコンテキストを取得
    Dim disDC As IntPtr = GetDC(IntPtr.Zero)
    'Bitmapの作成
    Dim bmp As New Bitmap(Screen.PrimaryScreen.Bounds.Width, _
        Screen.PrimaryScreen.Bounds.Height)
    'Graphicsの作成
    Dim g As Graphics = Graphics.FromImage(bmp)
    'Graphicsのデバイスコンテキストを取得
    Dim hDC As IntPtr = 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
End Function

<StructLayout(LayoutKind.Sequential)> _
Private Structure RECT
    Public left As Integer
    Public top As Integer
    Public right As Integer
    Public bottom As Integer
End Structure

<DllImport("user32.dll")> _
Private Shared Function GetWindowDC(ByVal hwnd As IntPtr) As IntPtr
End Function

<DllImport("user32.dll")> _
Private Shared Function GetForegroundWindow() As IntPtr
End Function

<DllImport("user32.dll")> _
Private Shared Function GetWindowRect(ByVal hwnd As IntPtr, _
    ByRef lpRect As RECT) As Integer
End Function

''' <summary>
''' アクティブなウィンドウの画像を取得する
''' </summary>
''' <returns>アクティブなウィンドウの画像</returns>
Public Shared Function CaptureActiveWindow() As Bitmap
    'アクティブなウィンドウのデバイスコンテキストを取得
    Dim hWnd As IntPtr = GetForegroundWindow()
    Dim winDC As IntPtr = GetWindowDC(hWnd)
    'ウィンドウの大きさを取得
    Dim winRect As New RECT
    GetWindowRect(hWnd, winRect)
    'Bitmapの作成
    Dim bmp As New Bitmap(winRect.right - winRect.left, _
        winRect.bottom - winRect.top)
    'Graphicsの作成
    Dim g As Graphics = Graphics.FromImage(bmp)
    'Graphicsのデバイスコンテキストを取得
    Dim hDC As IntPtr = 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
End Function
C#
コードを隠すコードを選択
//using System.Drawing;
//using System.Windows.Forms;
//using System.Runtime.InteropServices;

private  const int SRCCOPY = 13369376;
private  const int CAPTUREBLT = 1073741824;

[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);

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

/// <summary>
/// プライマリスクリーンの画像を取得する
/// </summary>
/// <returns>プライマリスクリーンの画像</returns>
public static 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;
}

[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 GetWindowDC(IntPtr hwnd);

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

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

/// <summary>
/// アクティブなウィンドウの画像を取得する
/// </summary>
/// <returns>アクティブなウィンドウの画像</returns>
public static 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;
}
  • 履歴:
  • 2007/3/3 Graphics.CopyFromScreenメソッドを使用する方法を追加。
  • 2010/3/29 Print Screenキーによる方法のコードで、クリップボードにデータがあるか確認するようにした。
  • 2012/3/20 Graphics.CopyFromScreenメソッドのバグに関する記述を追加。

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • このサイトで紹介されているコードの多くは、例外処理が省略されています。例外処理については、こちらをご覧ください。
  • コードの先頭に記述されている「Imports ??? がソースファイルの一番上に書かれているものとする」(C#では、「using ???; がソースファイルの一番上に書かれているものとする」)の意味が分からないという方は、こちらをご覧ください。