捕获屏幕图像俗称抓屏,在http://cnblogs.com/spidertan/archive/2004/09/03/39362.aspx提供了C#的版本,本文提供VB.NET和C#两个语言版本的代码(ScreenCapture.VB和ScreenCapture.CS),主要功能就是捕获屏幕或者窗口上的图象。
http://www.codeguru.com/code/legacy/cs_graphics/CaptureScreenDemo.zip提供了完整的工程代码,但跟本文没有关系,仅仅提供下载资源。

用ScreenCapture这个类特别简单,该类有四个方法:

public Image CaptureScreen()
捕获整个屏幕的图象 
public Image CaptureWindow(IntPtr handle)
捕获窗口上的图象 
public void CaptureWindowToFile(IntPtr handle, string filename, ImageFormat format) 
捕获窗口图像并且保存为一个文件 
public void CaptureScreenToFile(string filename, ImageFormat format)
捕获整个屏幕的图像并且保存为一个文件
示例与快速入门
VB.NET

Dim sc As New ScreenCapture()
' 捕获整个屏幕的图象
Dim img As Image = sc.CaptureScreen()
' 在ID为imageDisplay的Picture控件显示图像
Me.imageDisplay.Image = img
' 捕获窗口图像并且保存为一个文件
sc.CaptureWindowToFile(Me.Handle, "C: emp2.gif", ImageFormat.Gif)
C#

ScreenCapture sc = new ScreenCapture();
// 捕获整个屏幕的图象
Image img = sc.CaptureScreen();
// 在ID为imageDisplay的Picture控件显示图像
this.imageDisplay.Image = img; 
// 捕获窗口图像并且保存为一个文件
sc.CaptureWindowToFile(this.Handle,"C:\temp2.gif",ImageFormat.Gif);
ScreenCapture类的代码
VB.NET

Imports System
Imports System.Runtime.InteropServices
Imports System.Drawing
Imports System.Drawing.Imaging


Namespace ScreenShotDemoNamespace ScreenShotDemo
    _
   '/ <summary>
   '/ Provides functions to capture the entire screen, or a particular window, and save it to a file.
   '/ </summary>
   Public Class ScreenCaptureClass ScreenCapture
      
      '/ <summary>
      '/ Creates an Image object containing a screen shot of the entire desktop
      '/ </summary>
      '/ <returns></returns>
      Public Function CaptureScreen()Function CaptureScreen() As Image
         Return CaptureWindow(User32.GetDesktopWindow())
      End Function 'CaptureScreen
      
      
      '/ <summary>
      '/ Creates an Image object containing a screen shot of a specific window
      '/ </summary>
      '/ <param name="handle">The handle to the window. (In windows forms, this is obtained by the Handle property)</param>
      '/ <returns></returns>
      Public Function CaptureWindow()Function CaptureWindow(handle As IntPtr) As Image
         ' get te hDC of the target window
         Dim hdcSrc As IntPtr = User32.GetWindowDC(handle)
         ' get the size
         Dim windowRect As New User32.RECT()
         User32.GetWindowRect(handle, windowRect)
         Dim width As Integer = windowRect.right - windowRect.left
         Dim height As Integer = windowRect.bottom - windowRect.top
         ' create a device context we can copy to
         Dim hdcDest As IntPtr = GDI32.CreateCompatibleDC(hdcSrc)
         ' create a bitmap we can copy it to,
         ' using GetDeviceCaps to get the width/height
         Dim hBitmap As IntPtr = GDI32.CreateCompatibleBitmap(hdcSrc, width, height)
         ' select the bitmap object
         Dim hOld As IntPtr = GDI32.SelectObject(hdcDest, hBitmap)
         ' bitblt over
         GDI32.BitBlt(hdcDest, 0, 0, width, height, hdcSrc, 0, 0, GDI32.SRCCOPY)
         ' restore selection
         GDI32.SelectObject(hdcDest, hOld)
         ' clean up 
         GDI32.DeleteDC(hdcDest)
         User32.ReleaseDC(handle, hdcSrc)
         
         ' get a .NET image object for it
         Dim img As Image = Image.FromHbitmap(hBitmap)
         ' free up the Bitmap object
         GDI32.DeleteObject(hBitmap)
         
         Return img
      End Function 'CaptureWindow
      
      
      '/ <summary>
      '/ Captures a screen shot of a specific window, and saves it to a file
      '/ </summary>
      '/ <param name="handle"></param>
      '/ <param name="filename"></param>
      '/ <param name="format"></param>
      Public Sub CaptureWindowToFile()Sub CaptureWindowToFile(handle As IntPtr, filename As String, format As ImageFormat)
         Dim img As Image = CaptureWindow(handle)
         img.Save(filename, format)
      End Sub 'CaptureWindowToFile
      
      
      '/ <summary>
      '/ Captures a screen shot of the entire desktop, and saves it to a file
      '/ </summary>
      '/ <param name="filename"></param>
      '/ <param name="format"></param>
      Public Sub CaptureScreenToFile()Sub CaptureScreenToFile(filename As String, format As ImageFormat)
         Dim img As Image = CaptureScreen()
         img.Save(filename, format)
      End Sub 'CaptureScreenToFile
       _
      
      '/ <summary>
      '/ Helper class containing Gdi32 API functions
      '/ </summary>
      Private Class GDI32Class GDI32
         
         Public SRCCOPY As Integer = &HCC0020
          ' BitBlt dwRop parameter
         Public Shared<DllImport("gdi32.dll")>  _
         Function BitBlt()Function BitBlt(hObject As IntPtr, nXDest As Integer, nYDest As Integer, nWidth As Integer, nHeight As Integer, hObjectSource As IntPtr, nXSrc As Integer, nYSrc As Integer, dwRop As Integer) As Boolean
         
         Public Shared<DllImport("gdi32.dll")>  _
         Function CreateCompatibleBitmap()Function CreateCompatibleBitmap(hDC As IntPtr, nWidth As Integer, nHeight As Integer) As IntPtr
         
         Public Shared<DllImport("gdi32.dll")>  _
         Function CreateCompatibleDC()Function CreateCompatibleDC(hDC As IntPtr) As IntPtr
         
         Public Shared<DllImport("gdi32.dll")>  _
         Function DeleteDC()Function DeleteDC(hDC As IntPtr) As Boolean
         
         Public Shared<DllImport("gdi32.dll")>  _
         Function DeleteObject()Function DeleteObject(hObject As IntPtr) As Boolean
         
         Public Shared<DllImport("gdi32.dll")>  _
         Function SelectObject()Function SelectObject(hDC As IntPtr, hObject As IntPtr) As IntPtr
      End Class 'GDI32
       _
      
      '/ <summary>
      '/ Helper class containing User32 API functions
      '/ </summary>
      Private Class User32Class User32
         <StructLayout(LayoutKind.Sequential)>  _
         Public Structure RECTStructure RECT
            Public left As Integer
            Public top As Integer
            Public right As Integer
            Public bottom As Integer
         End Structure 'RECT
         
         
         Public Shared<DllImport("user32.dll")>  _
         Function GetDesktopWindow()Function GetDesktopWindow() As IntPtr
         
         Public Shared<DllImport("user32.dll")>  _
         Function GetWindowDC()Function GetWindowDC(hWnd As IntPtr) As IntPtr
         
         Public Shared<DllImport("user32.dll")>  _
         Function ReleaseDC()Function ReleaseDC(hWnd As IntPtr, hDC As IntPtr) As IntPtr
         
         Public Shared<DllImport("user32.dll")>  _
         Function GetWindowRect()Function GetWindowRect(hWnd As IntPtr, ByRef rect As RECT) As IntPtr
      End Class 'User32
   End Class 'ScreenCapture 
End Namespace 'ScreenShotDemo

C#

using System;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Drawing.Imaging;

namespace ScreenShotDemo
{
    /**//// <summary>
    /// Provides functions to capture the entire screen, or a particular window, and save it to a file.
    /// </summary>
    public class ScreenCapture
    {
        /**//// <summary>
        /// Creates an Image object containing a screen shot of the entire desktop
        /// </summary>
        /// <returns></returns>
        public Image CaptureScreen() 
        {
            return CaptureWindow( User32.GetDesktopWindow() );
        }

        /**//// <summary>
        /// Creates an Image object containing a screen shot of a specific window
        /// </summary>
        /// <param name="handle">The handle to the window. (In windows forms, this is obtained by the Handle property)</param>
        /// <returns></returns>
        public Image CaptureWindow(IntPtr handle)
        {
            // get te hDC of the target window
            IntPtr hdcSrc = User32.GetWindowDC(handle);
            // get the size
            User32.RECT windowRect = new User32.RECT();
            User32.GetWindowRect(handle,ref windowRect);
            int width = windowRect.right - windowRect.left;
            int height = windowRect.bottom - windowRect.top;
            // create a device context we can copy to
            IntPtr hdcDest = GDI32.CreateCompatibleDC(hdcSrc);
            // create a bitmap we can copy it to,
            // using GetDeviceCaps to get the width/height
            IntPtr hBitmap = GDI32.CreateCompatibleBitmap(hdcSrc,width,height); 
            // select the bitmap object
            IntPtr hOld = GDI32.SelectObject(hdcDest,hBitmap);
            // bitblt over
            GDI32.BitBlt(hdcDest,0,0,width,height,hdcSrc,0,0,GDI32.SRCCOPY);
            // restore selection
            GDI32.SelectObject(hdcDest,hOld);
            // clean up 
            GDI32.DeleteDC(hdcDest);
            User32.ReleaseDC(handle,hdcSrc);

            // get a .NET image object for it
            Image img = Image.FromHbitmap(hBitmap);
            // free up the Bitmap object
            GDI32.DeleteObject(hBitmap);

            return img;
        }

        /**//// <summary>
        /// Captures a screen shot of a specific window, and saves it to a file
        /// </summary>
        /// <param name="handle"></param>
        /// <param name="filename"></param>
        /// <param name="format"></param>
        public void CaptureWindowToFile(IntPtr handle, string filename, ImageFormat format) 
        {
            Image img = CaptureWindow(handle);
            img.Save(filename,format);
        }

        /**//// <summary>
        /// Captures a screen shot of the entire desktop, and saves it to a file
        /// </summary>
        /// <param name="filename"></param>
        /// <param name="format"></param>
        public void CaptureScreenToFile(string filename, ImageFormat format) 
        {
            Image img = CaptureScreen();
            img.Save(filename,format);
        }
       
        /**//// <summary>
        /// Helper class containing Gdi32 API functions
        /// </summary>
        private class GDI32
        {
            
            public const int SRCCOPY = 0x00CC0020; // BitBlt dwRop parameter

            [DllImport("gdi32.dll")]
            public static extern bool BitBlt(IntPtr hObject,int nXDest,int nYDest,
                int nWidth,int nHeight,IntPtr hObjectSource,
                int nXSrc,int nYSrc,int dwRop);
            [DllImport("gdi32.dll")]
            public static extern IntPtr CreateCompatibleBitmap(IntPtr hDC,int nWidth, 
                int nHeight);
            [DllImport("gdi32.dll")]
            public static extern IntPtr CreateCompatibleDC(IntPtr hDC);
            [DllImport("gdi32.dll")]
            public static extern bool DeleteDC(IntPtr hDC);
            [DllImport("gdi32.dll")]
            public static extern bool DeleteObject(IntPtr hObject);
            [DllImport("gdi32.dll")]
            public static extern IntPtr SelectObject(IntPtr hDC,IntPtr hObject);
        }
 
        /**//// <summary>
        /// Helper class containing User32 API functions
        /// </summary>
        private class User32
        {
            [StructLayout(LayoutKind.Sequential)]
            public struct RECT
            {
                public int left;
                public int top;
                public int right;
                public int bottom;
            }

            [DllImport("user32.dll")]
            public static extern IntPtr GetDesktopWindow();
            [DllImport("user32.dll")]
            public static extern IntPtr GetWindowDC(IntPtr hWnd);
            [DllImport("user32.dll")]我们已经了解了Visual Basic或者Delphi等语言是如何来实现对屏幕图象捕获的。那么对于C#来说,是如何实现这种功能的?本文就来探讨一下这个问题。

  一. 程序设计开发及运行环境:

  (1).微软视窗2000服务器版

  (2)..Net FrameWork SDK Beta 2

  二. 程序设计的关键步骤以及具体的实现方法:

   (1).首先要创建一个和当前屏幕大小相同的Bitmap对象:

  要实现此操作,首先就要现获得当前显示器的DC,然后根据此DC来创建Graphic对象,再由此Graphic对象产生此位图对象。这样产生的位图对象才是和当前屏幕大小相一致的。由于要获得显示器的DC,利用.Net的类库是无法实现的,这需要调用一个Windows的API函数。我们知道视窗所有API都封装在"Kernel"、"User "和"GDI"三个库中文件中:其中"Kernel",他的库名为 "KERNEL32.DLL"。"User "这个类库在Win32中名叫 "USER32.DLL"。 它主要管理全部的用户接口。譬如:窗口 、菜单 、对话框 、图标等等。"GDI"(图象设备接口),它在Win32中的库名为:"GDI32.dll",要获得显示器的DC,所调用的API函数--CreateDC ( ),就被封装在此类库中。而要在C#中声明视窗的API函数需要使用.Net FrameWork SDK中的名字空间"System.Runtime.InteropServices",此名字空间提供了一系列的类来访问COM对象,和调用本地的API函数。下面是在C#中声明此函数:

[ System.Runtime.InteropServices.DllImportAttribute ( "gdi32.dll" ) ]
private static extern IntPtr CreateDC (
string lpszDriver , // 驱动名称
string lpszDevice , // 设备名称
string lpszOutput , // 无用,可以设定位"NULL"
IntPtr lpInitData // 任意的打印机数据
) ;
在C#中声明过此API函数,就可以创建和显示器大小一致的位图对象,具体实现语句如下:
IntPtr dc1 = CreateDC ( "DISPLAY" , null , null , ( IntPtr ) null ) ;
//创建显示器的DC
Graphics g1 = Graphics.FromHdc ( dc1 ) ;
//由一个指定设备的句柄创建一个新的Graphics对象
MyImage = new Bitmap ( Screen.PrimaryScreen.Bounds.Width , Screen.PrimaryScreen.Bounds.Height , g1 ) ;
//根据屏幕大小创建一个与之相同大小的Bitmap对象 

  (2).根据此位图创建一个和其一样的Graphic对象:

  通过下面代码就可以实现此功能:

Graphics g2 = Graphics.FromImage ( MyImage ) ; 

  (3).获得当前屏幕和位图的句柄:

  获得此二个对象的句柄是为了下一步实现对当前屏幕图象的捕获,程序中实现的具体捕获的方法是把当前屏幕捕获到已经创建的位图对象中。具体实现代码如下:

//获得屏幕的句柄
IntPtr dc3 = g1.GetHdc ( ) ;
//获得位图的句柄
IntPtr dc2 = g2.GetHdc ( ) ;
//把当前屏幕捕获到位图对象中 

  (4).捕获当前屏幕:

  我们是通过当前屏幕保存到创建的位图对象中来实现的,具体的实现过程中是通过Windows的一个API函数--Bitblt。我想大多数程序员对此API函数一定不陌生,因为在Windows的图象编程中,会在很多地方使用到此函数。这个API函数和上面介绍的那个API函数一样,也是被封装在"GDI32.dll"中的,下面是此函数在C#中的声明:

[ System.Runtime.InteropServices.DllImportAttribute ( "gdi32.dll" ) ]
private static extern bool BitBlt (
IntPtr hdcDest , // 目标设备的句柄
int nXDest , // 目标对象的左上角的X坐标
int nYDest , // 目标对象的左上角的X坐标
int nWidth , // 目标对象的矩形的宽度
int nHeight , // 目标对象的矩形的长度
IntPtr hdcSrc , // 源设备的句柄
int nXSrc , // 源对象的左上角的X坐标
int nYSrc , // 源对象的左上角的X坐标
System.Int32 dwRop // 光栅的操作值
) ;
知道了此声明就可以实现对当前屏幕的保存了,具体如下:
BitBlt ( dc2 , 0 , 0 , Screen.PrimaryScreen.Bounds.Width , Screen.PrimaryScreen.Bounds.Height , dc3 , 0 , 0 , 13369376 ) ; 

  (5).把当前屏幕保存到硬盘,并释放句柄:

g1.ReleaseHdc ( dc3 ) ;
//释放屏幕句柄
g2.ReleaseHdc ( dc2 ) ;
//释放位图句柄
MyImage.Save ( "c:\\MyJpeg.jpg" , ImageFormat.Jpeg ) ; 

  我们可以根据自己的要求把当前屏幕以不同的文件格式来保存,在本文中介绍的程序是以"jpg"文件来保存的,你可以通过修改"Save"方法的第二个参数来改变保存到硬盘的文件类型,譬如,如果第二个参数为"ImageFormat.Gif",那么你保存到硬盘的文件就为"GIF"文件了。对于其他文件格式可以参考.Net FrameWork SDK,里面有详细的介绍。

  三. 用C#做Screen Capture程序的代码和运行节目:

  在掌握了上面这些重要步骤后,可以得到用C#做Screen Capture程序的源代码(Capture.cs),具体如下:

using System ;
using System.Drawing ;
using System.Collections ;
using System.ComponentModel ;
using System.Windows.Forms ;
using System.Data ;
using System.Drawing.Imaging ;
using System.IO ;
//导入在程序中使用到的名称空间
public class Capture : Form
{
private System.ComponentModel.Container components = null ;
private Icon mNetTrayIcon = new Icon ( "Tray.ico" ) ;
private Bitmap MyImage = null ;
private NotifyIcon TrayIcon ;
private ContextMenu notifyiconMnu ;
public Capture ( )
{
//初始化窗体中使用到的组件
InitializeComponent ( ) ;

protected override void OnActivated ( EventArgs e )
{
this.Hide ( ) ;
}
[ System.Runtime.InteropServices.DllImportAttribute ( "gdi32.dll" ) ]
private static extern bool BitBlt (
IntPtr hdcDest , //目标设备的句柄
int nXDest , // 目标对象的左上角的X坐标
int nYDest , // 目标对象的左上角的X坐标
int nWidth , // 目标对象的矩形的宽度
int nHeight , // 目标对象的矩形的长度
IntPtr hdcSrc , // 源设备的句柄
int nXSrc , // 源对象的左上角的X坐标
int nYSrc , // 源对象的左上角的X坐标
System.Int32 dwRop // 光栅的操作值
) ;

[ System.Runtime.InteropServices.DllImportAttribute ( "gdi32.dll" ) ]
private static extern IntPtr CreateDC (
string lpszDriver , // 驱动名称
string lpszDevice , // 设备名称
string lpszOutput , // 无用,可以设定位"NULL"
IntPtr lpInitData // 任意的打印机数据
) ;
public void capture ( object sender , System.EventArgs e )
{
this.Visible = false ;
IntPtr dc1 = CreateDC ( "DISPLAY" , null , null , ( IntPtr ) null ) ;
//创建显示器的DC
Graphics g1 = Graphics.FromHdc ( dc1 ) ;
//由一个指定设备的句柄创建一个新的Graphics对象
MyImage = new Bitmap ( Screen.PrimaryScreen.Bounds.Width , Screen.PrimaryScreen.Bounds.Height , g1 ) ;
//根据屏幕大小创建一个与之相同大小的Bitmap对象
Graphics g2 = Graphics.FromImage ( MyImage ) ;
//获得屏幕的句柄
IntPtr dc3 = g1.GetHdc ( ) ;
//获得位图的句柄
IntPtr dc2 = g2.GetHdc ( ) ;
//把当前屏幕捕获到位图对象中
BitBlt ( dc2 , 0 , 0 , Screen.PrimaryScreen.Bounds.Width , Screen.PrimaryScreen.Bounds.Height , dc3 , 0 , 0 , 13369376 ) ;
//把当前屏幕拷贝到位图中
g1.ReleaseHdc ( dc3 ) ;
//释放屏幕句柄
g2.ReleaseHdc ( dc2 ) ;
//释放位图句柄
MyImage.Save ( "c:\\MyJpeg.jpg" , ImageFormat.Jpeg ) ;
MessageBox.Show ( "已经把当前屏幕保存到C:\\MyJpeg.jpg文件中!" ) ;
this.Visible = true ;
}
public void ExitSelect ( object sender , System.EventArgs e )
{
//隐藏托盘程序中的图标
TrayIcon.Visible = false ;
//关闭系统
this.Close ( ) ;
}
//清除程序中使用过的资源
public override void Dispose ( )
{
base.Dispose ( ) ;
if ( components != null )
components.Dispose ( ) ;
}
private void InitializeComponent ( )
{
//设定托盘程序的各个属性
TrayIcon = new NotifyIcon ( ) ;
TrayIcon.Icon = mNetTrayIcon ;
TrayIcon.Text = "用C#做Screen Capture程序" ;
TrayIcon.Visible = true ; 
//定义一个MenuItem数组,并把此数组同时赋值给ContextMenu对象
MenuItem [ ] mnuItms = new MenuItem [ 3 ] ;
mnuItms [ 0 ] = new MenuItem ( ) ;
mnuItms [ 0 ] .Text = "捕获当前屏幕!" ;
mnuItms [ 0 ] .Click += new System.EventHandler ( this.capture ) ;
mnuItms [ 1 ] = new MenuItem ( "-" ) ;
mnuItms [ 2 ] = new MenuItem ( ) ;
mnuItms [ 2 ] .Text = "退出系统" ;
mnuItms [ 2 ] .Click += new System.EventHandler ( this.ExitSelect ) ;
mnuItms [ 2 ] .DefaultItem = true ;
notifyiconMnu = new ContextMenu ( mnuItms ) ;
TrayIcon.ContextMenu = notifyiconMnu ;
//为托盘程序加入设定好的ContextMenu对象
this.SuspendLayout ( ) ;
this.AutoScaleBaseSize = new System.Drawing.Size ( 5 , 13 ) ;
this.ClientSize = new System.Drawing.Size ( 320 , 56 ) ;
this.ControlBox = false ;
this.MaximizeBox = false ;
this.MinimizeBox = false ;
this.WindowState = System.Windows.Forms.FormWindowState.Minimized ;
this.Name = "capture" ;
this.ShowInTaskbar = false ;
this.Text = "用C#做Screen Capture程序!" ;
this.ResumeLayout ( false ) ;
}
static void Main ( ) 
{
Application.Run ( new Capture ( ) ) ;
}
}
 


  下图是此代码编译后的运行界面:


图01:用C#做Screen Capture程序的源代码  

  四. 总结:

  虽然.Net FrameWork SDK的内容十分丰富,借助他所能够实现的功能也非常强大,但对于一些底层的操作,有时还是需要借助Windows的API函数才可以实现,而实现Screen Capture的关键也就在于掌握C#中调用API函数的方法。希望通过本文,能够对你掌握在C#中的API编程有所帮助。


            public static extern IntPtr ReleaseDC(IntPtr hWnd,IntPtr hDC);
            [DllImport("user32.dll")]
            public static extern IntPtr GetWindowRect(IntPtr hWnd,ref RECT rect);

        }


    }
}
 

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐