你好,欢迎来到电脑编程技巧与维护杂志社! 杂志社简介广告服务读者反馈编程社区  
合订本订阅
 
 
您的位置:杂志经典 / 编程语言
用VC6编写抓图程序
 

  :本文给出了设计一个抓图程序的完整过程,对一个抓图程序所应拥有的最基本的三大功能即:热键激活、屏幕抓图及存储所抓取的图形等功能的实现作了详细的介绍。

关键词:热键      屏幕抓图  DDB  DIB

   

在游戏程序的设计中,抓图功能是比较重要的一个功能。其实抓图程序的设计并不太复杂,仔细分析一个典型的抓图程序,它主要有以下功能:①具有热键激活的功能;②屏幕抓图的功能;③存储所抓取的图形。以下介绍在VC6中抓图程序的实现过程。

    VC6中创建一单文档工程,可命名为BmpCapture,为简化编程不选取工具条、状态条、ActiveX支持、3D控件和打印及打印预览等选项。注意在AppWizard对话框的Step6中,选取视图类的基类为CScrollView,因为不同的机器上,屏幕分辨率有可能不一样,所以应该建立一滚动视图,以适应不同分辨率大小的图形。

    当然要完全支持滚动视图并没有这么简单,我们还必须手工加入代码。

    在文档类中加入一保护型成员变量protected: CSize m_SizeDoc,该成员变量用于保存实际图形的分辨率的大小,也就是文档的尺寸;因为该成员被设定为保护型成员,故不能由与该文档相连的视图直接处理,而为能够根据实际图形分辨率大小改变该成员变量,应在文档类中相应定义两成员函数:

CSize GetDocSize() {return m_SizeDoc;}

该函数用以获取实际图形分辨率大小;

    void SetDocSize(CSize size) {m_SizeDoc=size;}

    该函数用于根据实际图形分辨率大小来设置m_SizeDoc成员变量的值;

    那么如何根据不同窗口对象客户区的大小来改变m_sizeDoc的大小呢?

    GetClientRect函数来获得窗口对象客户区的大小

    CWnd *m_pWndShow=GetDesktopWindow();//得到桌面窗口

    CRect rect;

    m_pWndShow->GetClientRect(rect);

    size.cx=rect.Width();

    size.cy=rect.Height();

    然后进行修改如下:

CBmpCaptureDoc* pDoc = GetDocument();

    ASSERT_VALID(pDoc);

    pDoc->SetDocSize(size);

    记得在每次修改了m_sizeDoc之后要调用视图类的SetScrollSizes成员函数以传递该尺寸。

    SetScrollSizes(MM_TEXT,pDoc->GetDocSize());

    除了这些工作以外,还需要重载CBmpCaptureView类的成员函数OnInitialUpdate,该函数在视图第一次与文档相连时被调用。通过重载这个函数,能把文档的尺寸在最初更新视图前通知给视图。

    void CBmpCaptureView::OnInitialUpdate()

{

        CScrollView::OnInitialUpdate();

 

        CSize sizeTotal;

        // TODO: calculate the total size of this view

        sizeTotal.cx = sizeTotal.cy = 100;

        SetScrollSizes(MM_TEXT, sizeTotal);

}

 

所抓取的图形应首先存于一内存DCCBitmap对象中,然后在OnDraw函数中重绘。为此,在CBmpCaptureDoc类中加入一CBitmap型成员变量,如下:

public:

    CBitmap m_bitmap;

    完成了以上的工作以后,现在开始在程序中加入热键激活的功能。在抓图程序中所谓热键激活是指当程序被最小化后,按下热键后即可完成抓图工作,同时恢复抓图程序界面,并在框架窗口的客户区显示所抓取的图形。

    由于在ClassWizard中并没有封装热键处理消息(WM_HOTKEY),所以必须手工加入所有的代码。

    ①首先在BmpCaptureView.h文件中,加入热键消息响应函数的声明:

    protected:

    //{{AFX_MSG(CBmpCaptureView)

    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);

    afx_msg LRESULT OnHotKey(WPARAM wParam,LPARAM lParam);

    //}}AFX_MSG

    DECLARE_MESSAGE_MAP()

    ②然后在BmpCaptureView.cpp文件中,找到消息映射的定义处,加入以下语句:

    BEGIN_MESSAGE_MAP(CBmpCaptureView, CScrollView)

    //{{AFX_MSG_MAP(CBmpCaptureView)

    ON_WM_CREATE()

    //}}AFX_MSG_MAP

    ON_MESSAGE(WM_HOTKEY,OnHotKey) //消息和函数发生关联

END_MESSAGE_MAP()

    ③下一步在OnCreate函数中加入初始化代码并用RegisterHotKey函数向系统登记热键。初始化部分包括将m_bitmap对象初始化为合适的大小,并与视图窗口相兼容同时将位图对象清空。重载OnCreate函数如下:

int CBmpCaptureView::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

    if (CScrollView::OnCreate(lpCreateStruct) == -1)

        return -1;

RegisterHotKey(m_hWnd,1001,MOD_CONTROL|MOD_ALT,’Y’);//此处登记热键                                                  

   //”Ctrl+Alt+Y”

    // TODO: Add your specialized creation code here

    CWnd *m_pWndShow=GetDesktopWindow();//得到桌面窗口

    CClientDC windowDC(this);

    CBmpCaptureDoc* pDoc = GetDocument();

    CRect rect;

    m_pWndShow->GetClientRect(rect);

    int cx=rect.Width(),cy=rect.Height();

    pDoc->m_bitmap.CreateCompatibleBitmap(&windowDC, cx,cy);

    CDC memoryDC;

    memoryDC.CreateCompatibleDC(&windowDC);

    CBitmap* pOldBitmap =

        memoryDC.SelectObject(&pDoc->m_bitmap);

    CBrush* pWhiteBrush = new CBrush(RGB(255,255,255));

    CRect rect1(0, 0, cx-1, cy-1);

    memoryDC.FillRect(rect1, pWhiteBrush);//将位图清空

    memoryDC.SelectObject(pOldBitmap);

    delete pWhiteBrush;

   

    return 0;

}

④接下来当然得编写响应热键消息的函数OnHotKey了,在这里首先通过检查参数wParam来判断是否是所期望的热键,然后获取桌面窗口DC并使用BitBlt函数将其内容拷至m_bitmap对象中,最后还有一段将被最小化的窗口恢复显示的代码。因为代码中用到了CMainFrame类指针,所以需要在BmpCaptureView.cpp文件中加入语句#include "MainFrm.h"

LRESULT CBmpCaptureView::OnHotKey(WPARAM wParam,LPARAM lParam)

{

    if (wParam==1001)

    {

        CWnd *m_pWndShow=GetDesktopWindow();//得到桌面窗口

        ASSERT(m_pWndShow!=NULL);

        CDC *pdc_Showed=m_pWndShow->GetDC();

        CRect rect;

        m_pWndShow->GetClientRect(rect);

        CSize size;

        size.cx=rect.Width();

        size.cy=rect.Height();

        CBmpCaptureDoc* pDoc = GetDocument();

        ASSERT_VALID(pDoc);

        pDoc->SetDocSize(size);

        SetScrollSizes(MM_TEXT,pDoc->GetDocSize());

        CClientDC cdc(this);

        CDC memoryDC;

        memoryDC.CreateCompatibleDC(pdc_Showed);

        CBitmap* pOldBitmap =

            memoryDC.SelectObject(&pDoc->m_bitmap);

        size=pDoc->GetDocSize();

        memoryDC.BitBlt (0,0,size.cx,size.cy,pdc_Showed,0,0,SRCCOPY);

        Invalidate();//使窗口重画

        memoryDC.SelectObject(pOldBitmap);

        //以下重新显示被最小化的窗口

        CMainFrame* pwnd=(CMainFrame*)AfxGetApp()->m_pMainWnd;

        if(pwnd->SetForegroundWindow())

        {

        pwnd->ShowWindow(SW_SHOWNORMAL);

        pwnd->UpdateWindow();

        }

   

    }

    return 0;

}

    以上OnHotKey函数中对 Invalidate() 的调用将迫使视图窗口重画,使视图类调用OnDraw函数,以下为OnDraw函数的代码,其功能为在视图客户区内显示m_bitmap对象:

void CBmpCaptureView::OnDraw(CDC* pDC)

{

    CBmpCaptureDoc* pDoc = GetDocument();

    ASSERT_VALID(pDoc);

    // TODO: add draw code for native data here

    CDC memoryDC;

    memoryDC.CreateCompatibleDC(pDC);

    CBitmap* pOldBitmap =

    memoryDC.SelectObject(&pDoc->m_bitmap);

    CSize size=pDoc->GetDocSize();

    pDC->BitBlt(0, 0,size.cx, size.cy,

           &memoryDC, 0, 0, SRCCOPY);

    memoryDC.SelectObject(pOldBitmap);

}

 

    到这里已经实现了:①热键激活的功能;②屏幕抓图的功能。剩下的工作是将所抓取的图形保存起来。

 

    由于CBitmap类对象是一个DDB(设备相关位图),而我们将要保存的BMP位图则是DIB(设备无关位图)。所以首先得将DDB格式转换为DIB格式,然后再进行保存。

    CBmpCaptureDoc类中增加一个SaveBmp成员函数,代码如下:

BOOL CBmpCaptureDoc::SaveBmp(HBITMAP hBitmap, CFile& file)

{

    if (hBitmap == NULL)

        return FALSE;

    HDC hDC;

    int iBits;     

    //当前显示分辨率下每个像素所占字节数

    WORD wBitCount;  

   //位图中每个像素所占字节数

    //定义调色板大小, 位图中像素字节大小 ,

    //位图大小

    DWORD dwPaletteSize=0, dwBmBitsSize, dwDIBSize;

    BITMAP Bitmap;       

    HANDLE hDib;

    HPALETTE hPal,hOldPal=NULL;

    //位图属性结构

 

    BITMAPFILEHEADER bmfHdr; // 位图文件头

    LPBITMAPINFOHEADER lpBI;   // 位图头指针

    BITMAPINFOHEADER   bi;           

    //位图信息头结构

 

    //DWORD dwDIBSize;

    //计算位图文件每个像素所占字节数

    hDC = CreateDC("DISPLAY",NULL,NULL,NULL);

    iBits = GetDeviceCaps(hDC, BITSPIXEL) *

    GetDeviceCaps(hDC, PLANES);

    DeleteDC(hDC);

    if (iBits<=1)

      wBitCount=1;

    else if (iBits<=4)

      wBitCount=4;

    else if (iBits<=8)

      wBitCount=8;

    else if (iBits<=16)

      wBitCount=16;

    else if (iBits<=24)

      wBitCount=24;

   //计算调色板大小

    if (wBitCount<=8)

      dwPaletteSize=(1<<wBitCount) *

      sizeof(RGBQUAD);

    GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&Bitmap);

    bi.biSize= sizeof(BITMAPINFOHEADER);

    bi.biWidth= Bitmap.bmWidth;

    bi.biHeight= Bitmap.bmHeight;

    bi.biPlanes= 1;

    bi.biBitCount= wBitCount;

    bi.biCompression= BI_RGB;

    bi.biSizeImage= 0;

    bi.biXPelsPerMeter= 0;

    bi.biYPelsPerMeter= 0;

    bi.biClrUsed= 0;

    bi.biClrImportant= 0;

 

   dwBmBitsSize=((Bitmap.bmWidth *

    wBitCount+31)/32)* 4

     *Bitmap.bmHeight ;

   hDib=GlobalAlloc(GHND,dwBmBitsSize+

    dwPaletteSize+sizeof(BITMAPINFOHEADER));

   lpBI=(LPBITMAPINFOHEADER)GlobalLock(hDib);

   *lpBI=bi;

   // 处理调色板  

   hPal=(HPALETTE)GetStockObject(DEFAULT_PALETTE);

   if (hPal)

   {

      hDC=GetDC(NULL);

      hOldPal=(HPALETTE)SelectPalette(hDC,hPal,FALSE);

      RealizePalette(hDC);

   }

   // 获取该调色板下新的像素值

   GetDIBits(hDC, hBitmap, 0, (UINT) Bitmap.bmHeight,

        (LPSTR)lpBI+sizeof(BITMAPINFOHEADER)

        +dwPaletteSize,(BITMAPINFO*)

        lpBI, DIB_RGB_COLORS);

   //恢复调色板  

   if (hOldPal)

   {

      SelectPalette(hDC,hOldPal, TRUE);

      RealizePalette(hDC);

      ReleaseDC(NULL, hDC);

   }

  

    /*

     * 填写文件头

     */

 

    /* 给文件类型成员赋值:BMP文件前两个字节必须为"BM"*/

    bmfHdr.bfType = 0x4d42;  // "BM"

    dwDIBSize    = sizeof(BITMAPINFOHEADER)

                    + dwPaletteSize + dwBmBitsSize; 

    bmfHdr.bfSize = dwDIBSize+sizeof(BITMAPFILEHEADER);

    bmfHdr.bfReserved1 = 0;

    bmfHdr.bfReserved2 = 0;

    bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER)

                        + (DWORD)sizeof(BITMAPINFOHEADER)

                        + dwPaletteSize;

                       

    TRY

    {

        // 写文件头

        file.Write((LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER));

        //

        // DIB头及数据位

        //

        file.WriteHuge((LPSTR)lpBI, dwDIBSize);

    }

    CATCH (CFileException, e)

    {

        ::GlobalUnlock((HGLOBAL) hDib);

        THROW_LAST();

    }

    END_CATCH

    ::GlobalUnlock((HGLOBAL) hDib);

    return TRUE;

}

只要熟悉了BMP位图的文件格式,以上的代码就不难理解。由于在很多书籍中对BMP位图的文件格式都有详细的介绍,故本文不再赘述。函数的最后是一段异常处理代码。

最后重载CBmpCaptureDoc类的OnSaveDocument成员函数,在该函数中调用以上的SaveBmp函数。代码如下:

BOOL CBmpCaptureDoc::OnSaveDocument(LPCTSTR lpszPathName)

{

    // TODO: Add your specialized code here and/or call the base class

    CFile file;

    CFileException fe;

    if (!file.Open(lpszPathName, CFile::modeCreate |

      CFile::modeReadWrite | CFile::shareExclusive, &fe))

    {

        ReportSaveLoadException(lpszPathName, &fe,

            TRUE, AFX_IDP_INVALID_FILENAME);

        return FALSE;

    }

 

    BOOL bSuccess = FALSE;

    TRY

    {

        BeginWaitCursor();

        HBITMAP m_hBMP=(HBITMAP)m_bitmap;

        bSuccess = SaveBmp(m_hBMP,file);

        file.Close();

    }

    CATCH (CException, eSave)

    {

        file.Abort();

        EndWaitCursor();

        ReportSaveLoadException(lpszPathName, eSave,

            TRUE, AFX_IDP_FAILED_TO_SAVE_DOC);

        return FALSE;

    }

    END_CATCH

 

    EndWaitCursor();

    SetModifiedFlag(FALSE);    

 

    if (!bSuccess)

    {

        CString strMsg;

        strMsg.LoadString(IDS_CANNOT_SAVE_BMP);

        MessageBox(NULL, strMsg, NULL, MB_ICONINFORMATION | MB_OK);

    }

 

    return bSuccess;

}

其中IDS_CANNOT_SAVE_BMP是字符串”Can not save bmp”ID。可以看到OnSaveDocument中除了对SaveBmp函数的调用外,还有许多异常处理代码。

好了,现在可以Build这个工程来看看这个程序的功能了。

先将生成的执行程序最小化,然后按下热键”Ctrl+Alt+Y”,你会发现你的桌面已经被抓到BmpCapture中了,从“文件”菜单中选取“保存”子菜单就可以将你的桌面保存为Bmp位图了。

以上程序在VC6WIN98中调试通过。

  推荐精品文章

·2024年12月目录 
·2024年11月目录 
·2024年10月目录 
·2024年9月目录 
·2024年8月目录 
·2024年7月目录 
·2024年6月目录 
·2024年5月目录 
·2024年4月目录 
·2024年3月目录 
·2024年2月目录 
·2024年1月目录
·2023年12月目录
·2023年11月目录

  联系方式
TEL:010-82561037
Fax: 010-82561614
QQ: 100164630
Mail:gaojian@comprg.com.cn

  友情链接
 
Copyright 2001-2010, www.comprg.com.cn, All Rights Reserved
京ICP备14022230号-1,电话/传真:010-82561037 82561614 ,Mail:gaojian@comprg.com.cn
地址:北京市海淀区远大路20号宝蓝大厦E座704,邮编:100089