在 Windows 下抓取螢幕畫面的方法


Heresy 之前因為工作上的需要,有必要寫程式去抓螢幕的畫面,拿來做後續的應用,所以就針對 Windows 環境下、擷取螢幕的方法、去找了一些資料。

而這一篇文章就是參考 MSDN 上的《Ways to capture the screen》,在該文中整理了幾種在 Windows 環境下擷取螢幕的方法。這邊 Heresy 算是稍微紀錄一下。

在該文章中,基本上有下面五種方法:

  1. 使用 Desktop Duplication API

    這個方法是使用 DXGI(DirectX Graphics Infrastructure)來做螢幕的擷取,他的說明可以參考 MSDN 上的《Desktop Duplication API》。

    這個方法的效率算是相當好的!不過,他最大的缺點,在於只適用於 Windows 8 以後的作業系統,在之前的作業系統是無法使用的。

    此外,雖然他可以捕捉到 OpenGL 或 DirectX 這類的 3D 程式的內容,但是如果這類的程式是在全螢幕的排外模式(exclusive mode)下運作的話,是很有可能沒辦法被擷取的。

    Heresy 目前是採用這種方法來做螢幕的擷取,在網路上也有人有分享相對完整的範例程式。Heresy 是參考了 GitHub 上的「DXGICaptureSample」這個範例、把它改成函式庫的型態,有興趣的話可以參考:

    https://github.com/KHeresy/DXGICapture

  2. 使用 GDI

    這個方法是比較老式的方法,使用 Windows GDI(graphics device interface、參考)來做螢幕的擷取,一般來說會用到 BitBlt() 這個函式(MSDN)。

    這個的缺點在於他可能沒辦法捕捉到畫面中的某些東西,例如多媒體的內容。

    這部分可以參考 MSDN 的《Capturing an Image》這篇文章。

  3. 使用 Direct 3D

    由於現在的 Windows 基本上應該底層都是用 DirectX 來實作的,所以也可以使用 Direct 3D 來做截圖。

    基本的概念,就是使用 GetRenderTargetData() 這個函式、來複製透過 GetRenderTarget() 取得的 rendering surface,並把他複製、儲存。

  4. 使用 Windows Media Video 9 Screen Codec

    如果只是要一個現成的工具來擷取桌面的話,可以直接使用 Expression Encoder(官網)來做。

    而如果是要寫程式的話,則是可以使用 Windows Media Video 9 Screen codec

  5. 透過自己寫的驅動程式來做

    在 Windows 8 以前的作業系統,微軟有提供「Mirror Driver」的架構,可以用來寫一個用來複製螢幕的驅動程式。

    而從 Windows 8 開始,微軟則是提供了「Remote Display Drivers」來做同樣的事。

如果不是要抓全螢幕、而是要抓單一視窗的話,其實 Windows SDK 就有 PrintWindow() 這個 API 可以拿來用;此外,上面第二個、第三個方法,也都可以只抓單一視窗。

由於 Heresy 最後是決定使用 DXGI 來做,所以其他方法就沒研究怎麼用了。但是在網路上,大多都還是可以找到範例的!

像是 CodeProject 的《Various methods for capturing the screen》一文中,就有針對 2 – 4 的方法,做比較詳細的說明、並也有提供範例程式,如果有需要的話應該可以參考。

不過像是「Remote Display Drivers」,就好像找不到什麼資料可以參考了…


另外,其實 NVIDIA 也有 NvFBC 這種可以直接透過顯示卡驅動程式來做螢幕擷取的方法,不過很可惜地被鎖在 NVIDIA GRID 上,雖然一般新的 GeForce 卡也做得到,但是沒有 API 可以用…(GRID SDK

此外,在 Mac OS X 的環境下,似乎因為已經都是用 OpenGL 來繪製的了,所以也可以用 OpenGL 的函式,來進行桌面的擷取。

對「在 Windows 下抓取螢幕畫面的方法」的想法

      • 感謝樓主提供這好用的資源
        少寫一個方法 – API hook
        Fraps就是用這個原理
        但我因為這個方式太不portable 想找別的low overhead的方法

        其實我覺得這函數介面設計有點讓人誤會
        GetOutputBits的參數 rcDest 是目標大小 而不是擷取用的RECT
        rcDest如果比原本小 就會被縮放 然而縮放是用nearest downsampling
        品質很差

        回答chris的問題 其實抓特定視窗不是很困難
        大概看一下GetOutputBits內部
        先將螢幕畫面 map到系統記憶體
        434行將之複製到目標記憶體 (順便downsampling 如果需要的話)
        改這裡就可以了! 只要你螢幕沒改解析度 速度不會變

回覆給Heresy 取消回覆

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料