1. 程式人生 > 實用技巧 >捕捉螢幕的各種方法

捕捉螢幕的各種方法

內容 介紹 用GDI方式捕獲它 還有DirectX方法 用Windows Media API捕獲螢幕 介紹 有時,我們希望通過程式設計捕獲整個螢幕的內容。下面解釋如何做到這一點。通常,我們可以直接使用GDI和/或DirectX。另一個值得考慮的選擇是Windows Media API。在這裡,我們將分別考慮它們,看看如何將它們用於我們的目的。在每種方法中,一旦我們將螢幕截圖放入應用程式定義的記憶體或點陣圖中,我們就可以使用它來生成電影。有關以程式設計方式從點陣圖序列建立電影的詳細資訊,請參閱文章“從HBitmap建立電影”。 用GDI方式捕獲它 當效能不是問題,當我們想要的只是桌面快照時,我們可以考慮GDI選項。這個機制基於桌面也是一個視窗的簡單原理——即它有一個視窗控制代碼(HWND)和一個裝置上下文(DC)。如果我們可以捕獲桌面的裝置上下文,我們可以用常規的方式將這些內容blit到我們的應用程式定義的裝置上下文。如果我們知道它的視窗控制代碼,那麼獲取桌面的裝置上下文非常簡單——可以通過函式GetDesktopWindow()來實現。因此,涉及的步驟是: 使用函式GetDesktopWindow()獲取桌面視窗控制代碼; 使用函式GetDC()獲取桌面視窗的DC; 為桌面DC建立一個相容的DC和一個相容的點陣圖,以選擇到該相容的DC。這些可以通過使用CreateCompatibleDC()和CreateCompatibleBitmap()來完成;選擇點陣圖到我們的DC可以通過SelectObject()完成; 當您準備捕捉螢幕時,只需將桌面DC的內容blit到已建立的相容DC—這就是全部—您就完成了。我們現在建立的相容點陣圖包含了捕獲時螢幕的內容。 不要忘記在完成操作後釋放物件。記憶體是寶貴的(對於其他應用程式)。 ExampleHide,複製Code

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|CAPTUREBLT); 
    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一起呼叫時返回螢幕高度。有關如何將捕獲的點陣圖儲存到磁碟以及如何將其傳送到剪貼簿的詳細資訊,請參閱附帶的原始碼。它非常簡單。原始碼實現了上述用於定期捕獲螢幕內容的技術,並從捕獲的影象序列中建立了一個電影。 還有DirectX方法 用DirectX捕捉螢幕截圖是一項非常簡單的任務。DirectX提供了一種簡單的方法。 每個DirectX應用程式都包含所謂的緩衝區或表面,用於儲存與該應用程式相關的視訊記憶體的內容。這被稱為應用程式的回緩衝。一些應用程式可能有多個回緩衝。另外,每個應用程式預設都可以訪問另一個緩衝區——前端緩衝區。這個緩衝區,即前緩衝區,儲存與桌面內容相關的視訊記憶體,因此本質上是螢幕影象。 通過從DirectX應用程式訪問前端緩衝區,我們可以捕獲此時螢幕的內容。 從DirectX應用程式訪問前端緩衝區非常簡單和直接。介面IDirect3DDevice9提供了GetFrontBufferData()方法,該方法採用IDirect3DSurface9物件指標並將前端緩衝區的內容複製到該表面上。可以使用方法IDirect3DDevice8::CreateOffscreenPlainSurface()來生成IDirect3DSurfce9物件。一旦螢幕被捕獲到圖面上,我們就可以使用D3DXSaveSurfaceToFile()函式以點陣圖格式將圖面直接儲存到磁碟上。因此,捕捉螢幕的程式碼如下所示:複製Code

extern IDirect3DDevice9* g_pd3dDevice;
Void CaptureScreen()
{
    IDirect3DSurface9* pSurface;
    g_pd3dDevice->CreateOffscreenPlainSurface(ScreenWidth, ScreenHeight,
        D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &pSurface, NULL);
    g_pd3dDevice->GetFrontBufferData(0, pSurface);
    D3DXSaveSurfaceToFile("Desktop.bmp",D3DXIFF_BMP,pSurface,NULL,NULL);
    pSurface->Release(); 
}

在上面的示例中,g_pd3dDevice是一個IDirect3DDevice9物件,並假設已正確初始化。此程式碼片段將捕獲的映像直接儲存到磁碟上。但是,如果我們只是想直接操作影象位,我們可以使用方法IDirect3DSurface9::LockRect(),而不是儲存到磁碟。這提供了一個指向表面記憶體的指標——本質上是一個指向捕獲影象的位的指標。我們可以將位元複製到應用程式定義的記憶體中,並對其進行操作。下面的程式碼片段展示瞭如何將surface內容複製到應用程式定義的記憶體中:複製Code

extern void* pBits;
extern IDirect3DDevice9* g_pd3dDevice;
IDirect3DSurface9* pSurface;
g_pd3dDevice->CreateOffscreenPlainSurface(ScreenWidth, ScreenHeight,
                                          D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, 
                                          &pSurface, NULL);
g_pd3dDevice->GetFrontBufferData(0, 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*。在複製到pBits之前,確保我們已經分配了足夠的記憶體。BITSPERPIXEL的典型值是每畫素32位。但是,它可能會根據您當前的監視器設定而有所不同。這裡要注意的重要一點是曲面的寬度是n與捕獲的螢幕影象寬度不同。由於涉及到記憶體對齊的問題(假定與字邊界對齊的記憶體訪問速度比非對齊的記憶體更快),surface可能會在每一行的末尾新增額外的內容,以使它們完全對齊到字邊界。lockedRect。Pitch表示連續兩行的起始點之間的位元組數。也就是說,要前進到下一行的正確位置,我們應該按音高前進,而不是按寬度前進。你可以用下面的方法反向複製表面位:隱藏複製Code

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);
}

當您在自頂向下和自底向上點陣圖之間進行轉換時,這可能非常方便。 雖然上面的LockRect()技術是訪問IDirect3DSurface9上捕獲的影象內容的一種方法,但是我們有另一個為IDirect3DSurface9定義的更復雜的方法,即GetDC()方法。我們可以使用IDirect3DSurface9::GetDC()方法為DirectX影象表面獲得一個GDI相容的裝置上下文,這使得直接將表面內容blit到我們的應用程式定義的DC成為可能。有興趣的讀者可以探索這種選擇。 本文提供的示例原始碼實現了將螢幕外平面的內容複製到使用者建立的點陣圖上的技術,以便定期捕獲螢幕內容,並根據捕獲的影象序列建立影片。 但是,在使用此技術進行螢幕捕獲時,值得注意的一點是文件中提到的注意事項:GetFrontBufferData()從設計上來說是一個很慢的操作,在效能關鍵的應用程式中不應該考慮使用它。因此,在這種情況下,GDI方法比DirectX方法更可取。 用於捕捉螢幕的Windows Media API Windows Media 9.0使用Windows Media Encoder 9 API支援螢幕截圖。它包括一個名為Windows Media Video 9 Screen的編解碼器,該編解碼器經過特別優化,可以對通過螢幕截圖生成的內容進行操作。Windows Media Encoder API提供了介面IWMEncoder2,可以使用該介面有效地捕獲螢幕內容。 使用Windows Media Encoder API進行螢幕捕獲非常簡單。首先,我們需要使用CoCreateInstance()函式建立IWMEncoder2物件。可以這樣做:隱藏複製Code

IWMEncoder2* g_pEncoder=NULL; 
CoCreateInstance(CLSID_WMEncoder,NULL,CLSCTX_INPROC_SERVER,
        IID_IWMEncoder2,(void**)&g_pEncoder);

因此建立的Encoder物件包含處理捕獲的螢幕資料的所有操作。然而,為了正確地執行它的操作,encoder物件依賴於在所謂的概要檔案中定義的設定。配置檔案只是一個包含控制編碼操作的所有設定的檔案。我們還可以在執行時使用各種定製選項建立自定義配置檔案,如編解碼器選項等,這取決於捕獲資料的性質。為了在螢幕捕獲應用程式中使用配置檔案,我們建立了一個基於Windows Media Video 9螢幕編解碼器的自定義配置檔案。IWMEncProfile2介面支援自定義概要檔案物件。我們可以使用CoCreateInstance()函式來建立一個定製的配置檔案物件,如下所示:複製Code

IWMEncProfile2* g_pProfile=NULL;
CoCreateInstance(CLSID_WMEncProfile2,NULL,CLSCTX_INPROC_SERVER,
        IID_IWMEncProfile2,(void**)&g_pProfile);

我們需要在配置檔案中為編碼器指定目標受眾。每個配置檔案可以儲存多個受眾配置,它們是介面IWMEncAudienceObj的物件。在這裡,我們使用一個受眾物件作為配置檔案。我們為概要檔案建立觀眾物件使用方法IWMEncProfile:: AddAudience(),它會返回一個指向IWMEncAudienceObj然後可以用於配置如視訊編解碼器設定(IWMEncAudienceObj: put_VideoCodec()),視訊幀大小設定(IWMEncAudienceObj: put_VideoHeight()和IWMEncAudienceObj:: put_VideoWidth())等。例如,我們將視訊編解碼器設定為Windows Media video 9螢幕編解碼器:Hide 複製Code

extern IWMEncAudienceObj* pAudience;
#define VIDEOCODEC MAKEFOURCC('M','S','S','2') 
    //MSS2 is the fourcc for the screen codec

long lCodecIndex=-1;
g_pProfile->GetCodecIndexFromFourCC(WMENC_VIDEO,VIDEOCODEC,
    &lCodecIndex); //Get the Index of the Codec
pAudience->put_VideoCodec(0,lCodecIndex);

fourcc是世界上每個編解碼器的一種唯一識別符號。用於Windows Media Video 9螢幕編解碼器的fourcc是MSS2。IWMEncAudienceObj::put_VideoCodec()接受概要檔案索引作為輸入,以識別特定的概要檔案——可以通過使用方法IWMEncProfile::GetCodecIndexFromFourCC()來獲得。 一旦完成配置概要檔案物件,我們就可以通過使用在編碼器的源組物件上定義的方法IWMEncSourceGroup:: put_Profile()來選擇這個概要檔案到我們的編碼器中。源組是源的集合,其中每個源可能是視訊流、音訊流或HTML流等。每個encoder物件都可以與許多源組一起工作,從這些源組中獲取輸入資料。因為我們的螢幕捕獲應用程式只使用一個視流,所以我們的編碼器物件需要有一個源組,其中包含一個單獨的源,即視訊源。這個單一的視訊源需要配置為使用螢幕裝置作為輸入源,這可以通過使用IWMEn方法完成cVideoSource2: SetInput(型):隱藏,複製Code

extern IWMEncVideoSource2* pSrcVid;
pSrcVid->SetInput(CComBSTR("ScreenCap://ScreenCapture1");

通過使用IWMEncFile::put_LocalFileName()方法,可以將目標輸出配置為儲存到視訊檔案(wmv movie)中,該方法需要一個IWMEncFile物件。這個IWMEncFile物件可以通過使用方法IWMEncoder::get_File()來獲得:Hide 複製Code

IWMEncFile* pOutFile=NULL;
g_pEncoder->get_File(&pOutFile);
pOutFile->put_LocalFileName(CComBSTR(szOutputFileName);

現在,在對encoder物件完成所有必要的配置之後,我們可以使用IWMEncoder::Start()方法開始捕捉螢幕。方法IWMEncoder::Stop()和IWMEncoder::Pause可以用於停止和暫停捕獲。 在處理全屏捕獲時,我們可以通過調整輸入視訊源流的屬性來交替地選擇捕獲區域。為此,我們需要使用IWmEnVideoSource2物件的IPropertyBag介面,如下所示:複製Code

#define WMSCRNCAP_WINDOWLEFT CComBSTR("Left")
#define WMSCRNCAP_WINDOWTOP CComBSTR("Top")
#define WMSCRNCAP_WINDOWRIGHT CComBSTR("Right")
#define WMSCRNCAP_WINDOWBOTTOM CComBSTR("Bottom")
#define WMSCRNCAP_FLASHRECT CComBSTR("FlashRect")
#define WMSCRNCAP_ENTIRESCREEN CComBSTR("Screen")
#define WMSCRNCAP_WINDOWTITLE CComBSTR("WindowTitle")
extern IWMEncVideoSource2* pSrcVid;
int nLeft, nRight, nTop, nBottom;
pSrcVid->QueryInterface(IID_IPropertyBag,(void**)&pPropertyBag);
CComVariant varValue = false;
pPropertyBag->Write(WMSCRNCAP_ENTIRESCREEN,&varValue);
varValue = nLeft;
pPropertyBag->Write( WMSCRNCAP_WINDOWLEFT, &varValue );
varValue = nRight;
pPropertyBag->Write( WMSCRNCAP_WINDOWRIGHT, &varValue );
varValue = nTop;
pPropertyBag->Write( WMSCRNCAP_WINDOWTOP, &varValue );
varValue = nBottom;
pPropertyBag->Write( WMSCRNCAP_WINDOWBOTTOM, &varValue );

附帶的原始碼實現了這種捕捉螢幕的技術。有趣的一點是,除了輸出的電影質量不錯之外,滑鼠游標也被捕獲了。(預設情況下,GDI和DirectX不太可能捕獲滑鼠游標)。 注意,您的系統需要安裝Windows Media 9.0 SDK元件來建立使用Windows Media 9.0 API的應用程式。 要執行應用程式,終端使用者必須安裝Windows Media Encoder 9系列。當您釋出基於Windows Media Encoder SDK的應用程式時,您還必須包括Windows Media Encoder軟體,通過在您的設定中重新發布Windows Media Encoder,或者要求您的使用者自己安裝Windows Media Encoder。 windowsmediaencoder 9.0可從以下網站下載: Windows媒體編碼器 結論 上面討論的所有技術都針對一個目標——捕捉螢幕的內容。然而,正如很容易猜到的那樣,結果會因程式中使用的特定技術而有所不同。如果我們想要的只是一個偶然的隨機快照,那麼GDI方法是一個不錯的選擇,因為它很簡單。然而,如果我們想要更專業的結果,使用Windows Media會是一個更好的選擇。值得注意的一點是,通過這些機制捕獲的內容的質量可能取決於系統的設定。例如,禁用硬體加速(桌面屬性|設定|高階|故障排除)可能會極大地提高捕獲應用程式的總體質量和效能。 本文轉載於:http://www.diyabc.com/frontweb/news5251.html