可以截取layered窗口(包括透明窗口)的代码:
procedure CaptureScreen(AFileName: string); const CAPTUREBLT = $40000000; var hdcScreen: HDC; hdcCompatible: HDC; bmp: TBitmap; hbmScreen: HBITMAP; begin hdcScreen := CreateDC('DISPLAY', nil, nil, nil); hdcCompatible := CreateCompatibleDC(hdcScreen); hbmScreen := CreateCompatibleBitmap(hdcScreen, GetDeviceCaps(hdcScreen, HORZRES), GetDeviceCaps(hdcScreen, VERTRES)); SelectObject(hdcCompatible, hbmScreen); bmp := TBitmap.Create; bmp.Handle := hbmScreen; BitBlt(hdcCompatible, 0, 0, bmp.Width, bmp.Height, hdcScreen, 0, 0, SRCCOPY or CAPTUREBLT);
bmp.SaveToFile(AFileName); bmp.Free; DeleteDC(hdcScreen); DeleteDC(hdcCompatible); end;
DX Primary Surface截图代码!包含DX8与DX9两个版本
... interface
{$DEFINE D3D9}
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons, {$IFDEF D3D9} // D3DX9, // use D3D to save surface Direct3D9 {$ELSE} // D3DX8, // use D3D to save surface Direct3D8 {$ENDIF}; ... procedure TForm1.BitBtn1Click(Sender: TObject); // Capture screen through D3D. var BitsPerPixel: Byte; {$IFDEF D3D9} pD3D: IDirect3D9; pSurface: IDirect3DSurface9; g_pD3DDevice: IDirect3DDevice9; {$ELSE} pD3D: IDirect3D8; pSurface: IDirect3DSurface8; g_pD3DDevice: IDirect3DDevice8; {$ENDIF} D3DPP: TD3DPresentParameters; ARect: TRect; LockedRect: TD3DLockedRect; BMP: TBitmap; i, p: Integer; begin BitsPerPixel := GetDeviceCaps(Canvas.Handle, BITSPIXEL); FillChar(d3dpp, SizeOf(d3dpp), 0); D3DPP.Windowed := True; D3DPP.Flags := D3DPRESENTFLAG_LOCKABLE_BACKBUFFER; D3DPP.SwapEffect := D3DSWAPEFFECT_DISCARD; D3DPP.BackBufferWidth := Screen.Width; D3DPP.BackBufferHeight := Screen.Height; D3DPP.BackBufferFormat := D3DFMT_X8R8G8B8; {$IFDEF D3D9} pD3D := Direct3DCreate9(D3D_SDK_VERSION); pD3D.CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, GetDesktopWindow, D3DCREATE_SOFTWARE_VERTEXPROCESSING, @D3DPP, g_pD3DDevice); g_pD3DDevice.CreateOffscreenPlainSurface(Screen.Width, Screen.Height, D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, pSurface, nil); g_pD3DDevice.GetFrontBufferData(0, pSurface); {$ELSE} pD3D := Direct3DCreate8(D3D_SDK_VERSION); pD3D.CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, GetDesktopWindow, D3DCREATE_SOFTWARE_VERTEXPROCESSING, D3DPP, g_pD3DDevice); g_pD3DDevice.CreateImageSurface(Screen.Width, Screen.Height, D3DFMT_A8R8G8B8, pSurface); g_pD3DDevice.GetFrontBuffer(pSurface); {$ENDIF} // use D3D to save surface. Notes: D3DX%ab.dll is required! // D3DXSaveSurfaceToFile('Desktop.bmp', D3DXIFF_BMP, pSurface, nil, nil); // use Bitmap to save surface ARect := Screen.DesktopRect; pSurface.LockRect(LockedRect, @ARect, D3DLOCK_NO_DIRTY_UPDATE or D3DLOCK_NOSYSLOCK or D3DLOCK_READONLY); BMP := TBitmap.Create; BMP.Width := Screen.Width; BMP.Height := Screen.Height; case BitsPerPixel of 8: BMP.PixelFormat := pf8bit; 16: BMP.PixelFormat := pf16bit; 24: BMP.PixelFormat := pf24bit; 32: BMP.PixelFormat := pf32bit; end; p := Cardinal(LockedRect.pBits); for i := 0 to Screen.Height - 1 do begin CopyMemory(BMP.ScanLine[i], Ptr(p), Screen.Width * BitsPerPixel div 8); p := p + LockedRect.Pitch; end; BMP.SaveToFile('Desktop.bmp'); BMP.Free; pSurface.UnlockRect; end;
以上DX截图代码,不需要额外的DLL支持,有DirectX 9.0即可
采用上面的2个方案以外,还有些视频播放器的图像不能截取吧,呵呵
怎么解决呢?
它们使用的,是称为"覆盖表面"的技术,截取覆盖表面,需要Hook的手段才行
思路是: 通过Hook DDraw的DirectDrawCreate(RealOne用)同DirectDrawCreateEx(WMP用) 获得IDirectDraw(7) 再COM Hook CreateSurface,注意RealOne使用的是通过QueryInterface获得IDirectDraw2 WMP则是IDirectDraw7
Hook了CreateSurface后,就能获得OverlaySurface
所以必须在软件使用前,启动全局Hook,才有效
在需要截图的时候 Lock Overlay Surface,读取数据,马上Unlock,以免损失性能
解码读出来的数据,即可,但是由于获得的数据是显卡硬件VRAM的数据,一般是YUY2,YV12等格式,需要转换为RGB格式
例如,在我的GF6600上,RealOne(RMVB)用的是YUY2,而WMP(AVI)用的是YV12,还与当前播放的文件格式有关
提供主表面截图源码和覆盖表面截图的测试程序http://lysoft.lz169.com/projects/DXCapture.rar 现在支持YV12,NV12,YUY2,UUVY 4个格式
|
请发表评论