C#实现窗体截图(代码+效果)
2009/08/12 - 17:12 Post by cutey
3,376 views
最近在做一个QQ找茬游戏的外挂,第一步便是窗体截图,因为以前从来没有涉及过图像处理方面的知识,感觉还是有点小难的。理论上讲做这个东西用C++会比较简单,但由于我一看到MFC就眼晕,所以还是选择了熟悉的C#语言。
C#写截图要用到传说中的.net没有提供的叫做BitBlt的函数,而BitBlt函数要求有被截窗体的设备驱动器句柄,需要用GetDC(hwnd)函数获得,当然之后还要用ReleaseDC(hdc)释放。但是要得到窗体的设备驱动器句柄又要求有被截窗体的窗体句柄,需要调用FindWindow()函数,有了窗体句柄再用GetClientRect()同时可以得到窗体的大小。看到这么多API,给我的第一感觉是头大。
然后我想到最简单的方法是用google搜到代码,直接考上去就得了,搜了半天却发现网上鲜有C#窗体截图的东西,铺天盖地都是屏幕截图。当然用屏幕截图也可以做,但是局限性太大,我又不愿意走弯路,所以宁愿麻烦点自己写窗体截图的代码。查了很多资料又调试了N次今天下午终于把窗体截图给搞定了,哈哈,虽然只是写出来了窗体截图,但是学到了好多知识,好高兴啊,吃饭都比前几天香了。
不多说了,上代码和我截出来的图吧,鼓励下自己,加油,后续工作更重要呢。
调用API相关:
[System.Runtime.InteropServices.DllImportAttribute("gdi32.dll")]
private static extern bool BitBlt
(
IntPtr hdcDest, //目标DC的句柄
int nXDest, //目标DC的矩形区域的左上角的x坐标
int nYDest, //目标DC的矩形区域的左上角的y坐标
int nWidth, //目标DC的句型区域的宽度值
int nHeight, //目标DC的句型区域的高度值
IntPtr hdcSrc, //源DC的句柄
int nXSrc, //源DC的矩形区域的左上角的x坐标
int nYSrc, //源DC的矩形区域的左上角的y坐标
System.Int32 dwRo //光栅的处理数值
);
[System.Runtime.InteropServices.DllImportAttribute("user32.dll")]
public extern static IntPtr FindWindow(string lpClassName, string lpWindowName);
[System.Runtime.InteropServices.DllImportAttribute("user32.dll")]
public static extern int GetWindowRect(IntPtr hWnd, out Rectangle lpRect);
[System.Runtime.InteropServices.DllImportAttribute("user32.dll")]
public extern static IntPtr GetDC(IntPtr hWnd);
[System.Runtime.InteropServices.DllImportAttribute("user32.dll")]
public extern static int ReleaseDC(IntPtr hWnd, IntPtr hDC);
截图部分代码:
IntPtr hwnd1 = FindWindow(null, "**");
if (!hwnd1.Equals(IntPtr.Zero))
{
GetWindowRect(hwnd1, out rect); //获得目标窗体的大小
Bitmap QQPic = new Bitmap(rect.Width, rect.Height);
Graphics g1 = Graphics.FromImage(QQPic);
IntPtr hdc1 = GetDC(hwnd1);
IntPtr hdc2 = g1.GetHdc(); //得到Bitmap的DC
BitBlt(hdc2, 0, 0, rect.Width, rect.Height, hdc1, 0, 0, 13369376);
g1.ReleaseHdc(hdc2); //释放掉Bitmap的DC
QQPic.Save("QQpic.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
//以JPG文件格式保存
}
截图效果:

我是看mm的啊 。。。
…..截图上的MM吗?呵呵~~
哎,撒谎了,其实我是找截图的资料点进来的!
由于BitBlt()函数对被遮盖的窗口不能截图,PrintWindow()可以,但在WindowXP下是有bug,所有到处找找资料了.
赞一个,mm搞计算机的很少……..
对的,BitBlt()虽然可以靠窗体句柄直接定位,但是不能截被遮盖的,我当时也为这个问题纠结了下。
看来也是同行啊,哈哈,一开始差点以为你认识我呢O(∩_∩)O~~~
为什么我执行这段代码,不过多少次,截图都和第一次截得一摸一样?
在下很着急,请mm务必回答,期待。(qq:88271180)
不好意思,刚刚看到留言,应该每执行一次BitBlt函数就绘一次的呀,你会不会是一次绘完之后没有释放句柄啊~
不是的,应该是系统Api不兼容的问题了。。
那段代码在我的系统上面运行不正常,我的是Win7 Professional。拿到别的机器上,Win7 RTM 和Xp sp2都没问题。。郁闷了
这样啊,我还没在Win 7上用过呢,下次在笔记本上试下,谢谢提醒哈O(∩_∩)O~
呵呵,搜索资料进来的,下面的代码可以截被遮挡的窗口
public Bitmap GetWindowImage(IntPtr hWnd)
{
IntPtr hscrdc = Win32API.User32.GetWindowDC(hWnd);
// Control control = Control.FromHandle(hWnd);
Win32API.User32.RECT windowRect = new Win32API.User32.RECT();
Win32API.User32.GetWindowRect(hWnd, ref windowRect);
int width = windowRect.right – windowRect.left;
int height = windowRect.bottom – windowRect.top;
IntPtr hbitmap = Win32API.GDI32.CreateCompatibleBitmap(hscrdc, width, height);
IntPtr hmemdc = Win32API.GDI32.CreateCompatibleDC(hscrdc);
Win32API.GDI32.SelectObject(hmemdc, hbitmap);
Win32API.User32.PrintWindow(hWnd, hmemdc, 0);
Bitmap bmp = Bitmap.FromHbitmap(hbitmap);
Win32API.GDI32.DeleteDC(hscrdc);//删除用过的对象
Win32API.GDI32.DeleteDC(hmemdc);//删除用过的对象
return bmp;
}
如果愿意,楼主共享一份代码给我,9049021@qq.com省得我再折腾