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

王朝other·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

本文地址 : 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() 被设计为一个较慢的操作,在性能较差的机器的应用程序中不与考虑。

独子饿了! ……

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

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
 
 
© 2005- 王朝網路 版權所有  導航