作者: 馬金虎
我們已經了解了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 ( "-"