[fy] 截取屏幕的3种实现

 本文地址  : http://www.codeproject.com/dialog/screencap.asp

    有时候我们需要在程序中截取屏幕,下面我将解释如何来实现。其中最直接的方式是使用 GDI 或者 DX,除此之外,还可以考虑 Windows Media API。 在这里我们会分别介绍他们的实现方法。


GDI 方式:

    如果不考虑效果或者仅是随便的截图,我们可以考虑GDI。他的机制是利用了桌面也是一个窗口,因此都会有窗口句柄以及设备环境(DC)
如果我们获取了该DC,只需要把他的内容 blit 到我们的程序DC中即可,而知道了窗口句柄后,DC则是探囊取物。桌面的窗口句柄可以通过函数 GetDesktopWindow() 获取。
    因此所涉及的步骤有:
    1.  通过XX 函数获得窗口句柄
    2. 通过句柄获得设备环境
    3. 为桌面DC创建一个兼容DC以及创建兼容位图到该DC。可以使用CreateCompatibleDC() 和 CreateCompatibleBitmap();SelectObject()实现
    4. 准备好了? OK, 把桌面DC BLIT 到 刚创建的兼容DC中 就搞定了(刚才创建的位图便是当前的桌面了)
    5  记得释放内存。



Example

Void CaptureScreen()
{
    int nScreenWidth = GetSystemMetrics(SM_CXSCREEN);
    int nScreenHeight = GetSystemMetrics(SM_CYSCREEN);
    HWND hDesktopWnd = GetDesktopWindow();
    HDC hDesktopDC = GetDC(hDesktopWnd);
    HDC hCaptureDC = CreateCompatibleDC(hDesktopDC);
    HBITMAP hCaptureBitmap =CreateCompatibleBitmap(hDesktopDC,
        nScreenWidth, nScreenHeight);
    SelectObject(hCaptureDC,hCaptureBitmap);
    BitBlt(hCaptureDC,0,0,nScreenWidth,nScreenHeight,hDesktopDC,0,0,SRCCOPY);
    SaveCapturedBitmap(hCaptureBitmap); //Place holder - Put your code
                                //here to save the captured image to disk
    ReleaseDC(hDesktopWnd,hDesktopDC);
    DeleteDC(hCaptureDC);
    DeleteObject(hCaptureBitmap);
}

    上面的代码段中,函数GetSystemMetrics() 使用参数SM_CXSCREEN 返回屏幕宽,SM_CYSCREEN返回屏幕高。 至于如何保存所获取的位图以及如何发送到
粘贴板,没什么好说的 HOHO。



DX :

    通过DX截取屏幕是一件非常简单的事情。
    每个 DX 应用程序都有一个缓冲来存放本程序相关的显存,也叫做背面缓冲。有些可能不止一个背面缓冲。还有另外一个可以直接存取的
缓冲——前台缓冲,他包含了于桌面相关的显存——实际上就是桌面的图片。
    通过读取前台缓冲,我们便可以截取桌面了,而DX的内部机制可以确保得到优化的效果——至少比GDI强。
    通过DX我们可以很轻松的对前台缓冲进行操作,接口 IDirect3DDevice8 提供了函数 GetFrontBuffer() ,带一个参数(IDirect3DSurface8),
该参数用于获得前台缓冲的内容。而 IDirect3DSurface8 对象可以通过函数 CreateImageSurface() 创建,一旦屏幕被截取到 surface 中,我们可以
用 D3DXSaveSurfaceFile() 把 surface 保存为位图格式,下面是截图代码:

extern IDirect3DDevice8* g_pd3dDevice;
Void CaptureScreen()
{
    IDirect3DSurface8 * pSurface;
    g_pd3dDeviceàCreateImageSurface(ScreenWidth,ScreenHeight,
        D3DFMT_A8R8G8B8,&pSurface);
    g_pd3dDevice->GetFrontBuffer(pSurface);
    D3DXSaveSurfaceToFile("Desktop.bmp",D3DXIFF_BMP,pSurface,
        NULL,NULL);
    pSurface->Release();
}

g_pd3dDevice 是IDirect3DDevice的对象,并假设已经被初始化。 上面的代码是直接保存图片到本地磁盘,如果我们只是想对图片的象素进行操作,我们
可以使用 IDirect3DSurface8::LockRect(),  他让一个指针指向所截取图片的内存单元,我们可以把他拷贝到我们的程序定义的内存并进行操作,下面是
实现拷贝操作的代码段:


extern void* pBits;
extern IDirect3DDevice8* g_pd3dDevice;
IDirect3DSurface8 * pSurface;
g_pd3dDeviceàCreateImageSurface(ScreenWidth,ScreenHeight,
                                D3DFMT_A8R8G8B8,&pSurface);
g_pd3dDevice->GetFrontBuffer(pSurface);
D3DLOCKED_RECT lockedRect;
pSurfaceàLockRect(&lockedRect,NULL,
                  D3DLOCK_NO_DIRTY_UPDATE|D3DLOCK_NOSYSLOCK|
                  D3DLOCK_READONLY)));
for( int i=0 ; i < ScreenHeight ; i++)
{
    memcpy( (BYTE*) pBits + i * ScreenWidth * BITSPERPIXEL / 8 ,
        (BYTE*) lockedRect.pBits + i* lockedRect.Pitch ,
        ScreenWidth * BITSPERPIXEL / 8);
}
g_pSurface->UnlockRect();
pSurface->Release();



上面的 pBits 是一个 void 指针,在拷贝之前要却被他有足够的内存。 BITSPERPIXEL 典型的值是每个象素32位,但他可能因显示器的设置而改变。
这里特别需要注意的是,surface 的宽度并不等同于截取的屏幕图片的宽度。 由于内存对齐处理(以字对齐的方式存取快于非对齐内存),surface 可能会在每行的后面进行多余的填充。 不过 lockedRect.Pitch 确切的告诉我们两行之间的位数,也就是说我们可以通过 pitch 而非 width 获得正确的结果。
字对齐。

下面的代码段是以反序拷贝 surface :
当你想反转一个位图时可以使用。
for( int i=0 ; i < ScreenHeight ; i++)
{
    memcpy((BYTE*) pBits +( ScreenHeight - i - 1) *
        ScreenWidth * BITSPERPIXEL/8 ,
        (BYTE*) lockedRect.pBits + i* lockedRect.Pitch ,
        ScreenWidth* BITSPERPIXEL/8);
}

在 DX8 中, LockRect 是唯一的方式可以对截取的图片进行存取操作,而在 DX9 中,可以使用 GetDC() 来获得该图片的GDI兼容设备环境,
这样问题又回归到了第一种方式……

最后一点废话(文档的注意信息):GetFrontBuffer() 被设计为一个较慢的操作,在性能较差的机器的应用程序中不与考虑。


独子饿了! ……

有错误的理解请指正!谢谢

保存多帧Gif图像的程序 前面那个1.0版上传后才发现有一个严重的GDI资源泄漏问题,于是火速上传此1.1版纠正此问题! 压缩包内“CxImageDemo.rar”是源码,“CxImageDemo.exe”是对应的Release版主程序,“截图6.0版.exe”是一个不相关的程序,放在压缩包里的原因在下面给出。 该程序(CxImageDemo.exe)使用VC6,使用CxImage类,UNICODE编译。 具有功能: 1、主打功能为录像屏幕的指定区域并保存为Gif图像; 2、浏览主流图片,但是gif图像不支持动态播放,只显示第一帧; 3、支持拖放图像文件到界面进行打开; 4、由于程序主打功能是录像指定区域并保存为Gif图像,所以菜单栏的保存和另存为功能没做; 屏幕录像操作过程: 详见程序主界面工具栏最右面的三个小图标tips提示。 值得注意的是,选择的矩形区域支持镜像操作,即如果是从右上角到左下角拉取矩形框,则录制出来的Gif图像会左右翻转;另外保存gif多帧图像是使用的CxImage类,该类保存的Gif彩色图像质量较差且占用内存较大(不关我的事),因此请不要录制太长时间! 录制出来的Gif图像显示效果怎么样?由于该程序没做Gif多帧图像的显示功能(主打功能是录制),因此你可以用附带的另一个“截图6.0版.exe”程序进行打开测试(该程序基于GDI+库,也支持拖放文件进行打开操作,点击鼠标中键自动调整窗口大小)。 如想编译此源码,请确保已配置好CxImage环境。 有问题联系:hastings1986@163.com
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值