屏幕抓圖程序DIY
發表于:2007-07-14來源:作者:點擊數:
標簽:
屏幕抓圖程序的關鍵有兩點:一是應該知道鼠標在屏幕上有準確位置;二是應該知道欲抓圖的窗口句柄;三是如何對抓到的圖片進行保存的問題。對于問題一,很簡單,利用SetCapture函數便能夠追蹤鼠標的移動(包括在屏幕抓圖程序窗口之外的窗口)。應該注意的是,S
屏幕抓圖程序的關鍵有兩點:一是應該知道鼠標在屏幕上有準確位置;二是應該知道欲抓圖的窗口句柄;三是如何對抓到的圖片進行保存的問題。對于問題一,很簡單,利用SetCapture函數便能夠追蹤鼠標的移動(包括在屏幕抓圖程序窗口之外的窗口)。應該注意的是,SetCapture的具體功能并不像MSDN中所說的那樣:調用SetCapture一次直到使用ReleaseCapture終止鼠標捕獲前都會起到對鼠標的捕獲作用,實際上,在使用過程中,你會發現,在進行了一次鼠標捕獲之后,SetCapture便失去了作用,這可能是SetCapture函數的一個
Bug。所以,在編程時,最好能夠不斷地調用SetCapture函數,以保證SetCapture能夠對鼠標進行正確無誤的捕獲。對于問題二,這時會用到一個平時我們不常用的函數:WindowFromPoint,這個函數能夠找出鼠標當前位置所對應的窗口句柄。具體用法可以參見MSDN,這里不做詳細敘述。同時,用過SnagIT有朋友都知道,在選擇抓圖窗口時,鼠標的在位置的窗口都會出現加粗的紅色邊框(如圖一所示),以提醒目前所選擇的窗口,這個功能實現起來有些復雜,下面仔細來看看詳細說明。
在GDI中,最常見的一個概念就設備環境(DC),每一個窗口都相應的具有自己的DC。如果能夠調用到窗口的DC,那么,我們便能夠在窗口上的任何位置繪圖。然而,在屏幕抓圖程序中,由于用戶所選擇的窗口是不固定的,所以,要想得到鼠標所在處的窗口的DC,不是很容易。能不能夠隨心所欲地在屏幕上進行繪圖但又不僅限于特定的窗口?可以,別忘了,GetDC能夠做到這點!GetDC我們都用得太多了,都快習慣了GetDC的函數申明:HDC GetDC( HWND hWnd),hWnd是DC對應的窗口句柄,當hWnd為空的,該函數返回的是整個屏幕的設備環境句柄!這就意味著我們可以在屏幕上進行任意繪圖。在鼠標所在處的窗口進行繪圖時,繪圖本身的目只是為了提醒用戶目前所選擇的窗口,所以,在進行繪圖時,必須得保證不會破壞窗口原有的畫面。這點很好辦,將窗口的DC繪圖模式設為R2_NOTXORPEN(將畫筆顏色與屏幕顏色進行異或后,再將屏幕顏取反),這樣,在同一個地方進行兩次繪圖后(對同一像素進行兩次異或運算,像素值并不會發生變化),窗口的畫面并不會發生任何變化!
從上面的說明可以看出,制作屏幕抓圖程序應分三步走:
1、 啟用鼠標捕獲
2、 在鼠標所在處的窗口進行繪圖,提醒抓圖的目標
3、 選定目標窗口時,將目標窗口的畫面保存為自定義的位圖(在本文中,我將畫面保存至剪貼板上),并終止鼠標捕獲
下面我們就按照上面的思路進行編程操作。首先新建一個基于對話框的項目ScreenCapture,然后準備好一個外形為相機的光標文件(*.cur),將之引入資源管理器(IDC_CAMERA)。接著在CscreenCaptureDlg類中加入兩個全局變量:
HWND hwndCapture;
CRect rectCapture;
然后通過類向導加入對WM_MOUSEMOVE及WM_LBUTTONUP事件的響應函數,基響應函數代碼如下:
void CScreenCaptureDlg::OnMouseMove(UINT nFlags, CPoint point)
{
//如果用戶將鼠標左鍵按住不放,則開始抓取圖片
if(nFlags==MK_LBUTTON)
{
//隱藏程序窗口,以免影響在抓取時的“視野”
ShowWindow(SW_HIDE);
//裝入“照相機”鼠標,開始追蹤鼠標的移動
HCURSOR cur=LoadCursor(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDC_CAMERA ));
SetCursor(cur);
SetCapture();
//獲得鼠標所在處的窗口句柄
ClientToScreen(&point);
hwndCapture=(HWND)::WindowFromPoint(point);
//取得屏幕的設備環境句柄,以便在屏幕的任何位置繪圖
HDC hDC=::GetDC(NULL);
//建立一紅色的畫筆
HPEN hPen =CreatePen(PS_INSIDEFRAME,6, RGB(255,0,0));
//將繪圖模式設為R2_NOTXORPEN,在繪圖時可以不破壞原有的背景
int nMode =SetROP2(hDC, R2_NOTXORPEN);
HPEN hpenOld= (HPEN)SelectObject(hDC, hPen);
//得到鼠標所在處的窗口的區域
::GetWindowRect(hwndCapture,&rectCapture);
//在鼠標所在處的窗口四周畫一紅色的矩形,做為選定時的提醒
POINT pt[5];
pt[0] = CPoint(rectCapture.left, rectCapture.top);
pt[1] = CPoint(rectCapture.right,rectCapture.top);
pt[2] = CPoint(rectCapture.right,rectCapture.bottom);
pt[3] = CPoint(rectCapture.left, rectCapture.bottom);
pt[4] = CPoint(rectCapture.left, rectCapture.top);
::Polyline(hDC,pt,5);
//延時后再重繪紅色的矩形,這樣就不會破壞原有內容
Sleep(100);
::Polyline(hDC,pt,5);
::SelectObject(hDC,hpenOld);
::ReleaseDC(NULL,hDC);
}
CDialog::OnMouseMove(nFlags, point);
}
void CScreenCaptureDlg::OnLButtonUp(UINT nFlags, CPoint point)
{
//得到鼠標所在處的窗口的區域寬、高
int nWidth=rectCapture.Width();
int nHeight=rectCapture.Height();
HDC hdcScreen, hMemDC;
HBITMAP hBitmap, hOldBitmap;
//建立一個屏幕設備環境句柄
hdcScreen = CreateDC("DISPLAY", NULL, NULL, NULL);
hMemDC = CreateCompatibleDC(hdcScreen);
//建立一個與屏幕設備環境句柄兼容、與鼠標所在處的窗口的區域等大的位圖
hBitmap = CreateCompatibleBitmap(hdcScreen, nWidth, nHeight);
// 把新位圖選到內存設備描述表中
hOldBitmap =(HBITMAP)SelectObject(hMemDC, hBitmap);
// 把屏幕設備描述表拷貝到內存設備描述表中
BitBlt(hMemDC, 0, 0, nWidth, nHeight, hdcScreen,rectCapture.left,rectCapture.top,SR
CCOPY);
//取回鼠標所在處的窗口屏幕位圖的句柄
hBitmap =(HBITMAP)SelectObject(hMemDC, hOldBitmap);
DeleteDC(hdcScreen);
DeleteDC(hMemDC);
// 返回位圖句柄
//打開剪貼板,并將位圖拷到剪貼板上
OpenClipboard() ;
EmptyClipboard();
SetClipboardData(CF_BITMAP, hBitmap);
//關閉剪貼板
CloseClipboard();
MessageBox("屏幕內容已經拷到剪貼板上!");
//終止鼠標捕獲
ReleaseCapture();
//恢復窗口顯示模式
ShowWindow(SW_NORMAL);
}
說明:在本程序中,如果用戶在對話框中按住鼠標左鍵不放,那么,程序便開始“抓圖”,當選擇好抓圖的目標后,松開鼠標左鍵,這時,抓圖的目標窗口的畫面就自動保存至剪貼板中了。如果你想將位置保存到一個BMP文件,你可以到http://www.cideguru.com中查找具體文檔。
瞧,屏幕抓圖程序的制作是不是良簡單?你也可以對該程序進行改良,加入個性化的界面或功能,我想其運用效果并比會比專業的屏幕抓圖程序差。在我的主頁 “國稅之家”(http://nationaltax.home.chinaren.com)的“個人世界”中,就有一個做好的屏幕抓圖程序,試一試?
原文轉自:http://www.anti-gravitydesign.com