Delphi螢幕截圖完美解決方案
可以擷取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獲得IDirectDraw2WMP則是IDirectDraw7Hook了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個格式