用制作C#作屏幕捕获程序

我们已经了解了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编程有所帮助。

Contributors: FHL