最近在尝试为软件增加截取屏幕的功能,为此学习了System.Drawing命名空间的Graphics、Image、Bitmap等GDI+类,这些类都很方便使用。但是它们大多都是对原有GDI API的封装,也增加了一些新的东西;不过封装得并不彻底,有些功能还是需要调用GDI API才能实现。我武断的认为Image、Bitmap、Metafile跟HBITMAP对应,Graphics跟HDC对应。
在GDI+中,我们可以很方便的用Graphics.FromImage方法来操作Image中的内容,并将其保存回图片文件。那么,我们怎么才能保存Graphics到图片文件呢?创建一个Bitmap对象,复制Graphics g1的内容到Bitmap的Graphics g2,然后保存Bitmap对象到文件。复制过程我们必须通过PINVOKE调用BitBlt函数来实现。下面是该函数的声明:
[DllImport("gdi32.dll", CharSet=CharSet.Auto, SetLastError=true, ExactSpelling=true)]
public static extern int BitBlt(HandleRef hDC, int x, int y, int nWidth, int nHeight, HandleRef hSrcDC, int xSrc, int ySrc, int dwRop);
参数中的各种DC可以用Graphics.GetHdc得到;最后一个参数光栅操作码很多,截取屏幕用的SRCCOPY值是0xcc0020,完整的光栅操作码可以查看MSDN的“Ternary Raster Operations”部分。
示例代码如下:
//这里假设要保存一个窗体的内容
int width=800; //获取宽度
int height=600; //获取高度
const int SRCCOPY=0xcc0020; //复制图块的光栅操作码
Bitmap bmSave=new Bitmap(width,height); //用于保存图片的位图对象
Graphics gSave=Graphics.FromImage(bmSave); //创建该位图的Graphics对象
HandleRef hDcSave=new HandleRef(null,gSave.GetHdc()); //得到句柄
Graphics gSrc=formMain.CreateGraphics(); //创建窗体的Graphics对象
HandleRef hDcSrc=new HandleRef(null,gSrc.GetHdc());
BitBlt(hDcSave,0,0,width,height,hDcSrc,0,0,SRCCOPY);
gSrc.ReleaseHdc();
gSave.ReleaseHdc();
bmSave.Save(@"C:/test.bmp");
gSrc.Dispose();
gSave.Dispose();
bmSave.Dispose();
关于Graphics.CopyFromScreen方法
该方法在内部其实也使用BitBlt来进行图块复制,但是它只是固定的复制屏幕的内容,我们不能指定复制的源。
如果您需要截取屏幕的内容,可以使用以下代码:
int screenWidth=System.Windows.Forms.SystemInformation.VirtualScreen.Width; //屏幕宽度
int screenHeight=System.Windows.Forms.SystemInformation.VirtualScreen.Height; //屏幕高度
Bitmap bmSave=new Bitmap(screenWidth,screenHeight);
Graphics g=Graphics.FromImage(bmSave);
g.CopyFromScreen(0,0,0,0,new Size(screenWidth,screenHeight),CopyPixelOperation.SourceCopy);
bmSave.Save(@"C:/test.bmp");
g.Dispose();
bmSave.Dispose();
如果需要复制顶层窗体的可见部分,也可以使用Graphics.CopyFromScreen,但是需要用PointToScreen方法先得到屏幕坐标。
如果需要复制任意的窗体或者控件,先用Control.Handle得到控件句柄,然后再用GetDC()函数得到HDC,再用BitBlt进行图块复制。