【问题标题】:Why is GetClientRect including the window border and title bar?为什么 GetClientRect 包括窗口边框和标题栏?
【发布时间】:2012-06-16 20:48:35
【问题描述】:

我正在用 C++ 编写一些代码,根据它的窗口句柄截取另一个应用程序的屏幕截图。我使用的方法是使用BitBlt。我的应用程序正在成功截取屏幕截图,并且我有一个函数可以将该图像数据保存到 bmp 文件中。

屏幕截图包含窗口的镶边。即边框和标题栏。根据我的理解,GetClientRect 应该排除窗口的边框和标题栏。我知道GetWindowRect 返回用户桌面内的坐标,GetClientRect 返回相对于应用程序本身的坐标。

我注意到在我的屏幕截图中,标题栏和左边框是可见的,但应用程序的右边框和底部被截断了。所以,我在想,如果我想排除标题和边框,那么我需要对GetWindowRectGetClientRect 进行某种组合,并使用有关窗口本身的信息来抵消GetClientRect 尺寸例如,无论窗口标题栏的高度是多少。

这听起来准确吗,还是我下面的代码做错了什么?

#include <Windows.h>
#include "ScreenshotManager.h"

namespace Managers {

    ScreenshotManager::ScreenshotManager(HWND gameHandle) {

        // get a device context for the window
        m_gameContext = GetWindowDC(gameHandle);

        // create a compatible device context for bitblt
        m_bitmapContext = CreateCompatibleDC(m_gameContext);

        // get window client area dimensions
        GetClientRect(gameHandle, &m_gameClientArea);

    }

    bool ScreenshotManager::TakeScreenshot() {

        // create a compatible bitmap for the game screenshots
        m_bitmap = CreateCompatibleBitmap(m_gameContext, m_gameClientArea.right, m_gameClientArea.bottom);

        // select the bitmap into the compatible device context
        SelectObject(m_bitmapContext, m_bitmap);

        // perform bit block transfer
        if (BitBlt(m_bitmapContext, 0, 0, m_gameClientArea.right, m_gameClientArea.bottom, m_gameContext, 0, 0, SRCCOPY) == false)
            return false;

        // get information about the taken screenshot
        GetObject(m_bitmap, sizeof(BITMAP), &m_bitmapInformation);

        return true;

    }

    void ScreenshotManager::SaveScreenshot(LPCWSTR outputPath) {

        BITMAPFILEHEADER   bmfHeader;    
        BITMAPINFOHEADER   bi;

        bi.biSize = sizeof(BITMAPINFOHEADER);
        bi.biWidth = m_bitmapInformation.bmWidth;
        bi.biHeight = m_bitmapInformation.bmHeight;
        bi.biPlanes = 1;    
        bi.biBitCount = 32;    
        bi.biCompression = BI_RGB;    
        bi.biSizeImage = 0;  
        bi.biXPelsPerMeter = 0;    
        bi.biYPelsPerMeter = 0;    
        bi.biClrUsed = 0;    
        bi.biClrImportant = 0;

        DWORD dwBmpSize = ((m_bitmapInformation.bmWidth * bi.biBitCount + 31) / 32) * 4 * m_bitmapInformation.bmHeight;

        // Starting with 32-bit Windows, GlobalAlloc and LocalAlloc are implemented as wrapper functions that 
        // call HeapAlloc using a handle to the process's default heap. Therefore, GlobalAlloc and LocalAlloc 
        // have greater overhead than HeapAlloc.
        HANDLE hDIB = GlobalAlloc(GHND,dwBmpSize);
        char *lpbitmap = (char *)GlobalLock(hDIB);

        // Gets the "bits" from the bitmap and copies them into a buffer which is pointed to by lpbitmap.
        GetDIBits(m_gameContext, m_bitmap, 0, (UINT)m_bitmapInformation.bmHeight, lpbitmap, (BITMAPINFO *)&bi, DIB_RGB_COLORS);

        // A file is created, this is where we will save the screen capture.
        HANDLE hFile = CreateFile(outputPath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);   

        // Add the size of the headers to the size of the bitmap to get the total file size
        DWORD dwSizeofDIB = dwBmpSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

        //Offset to where the actual bitmap bits start.
        bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER); 

        //Size of the file
        bmfHeader.bfSize = dwSizeofDIB; 

        //bfType must always be BM for Bitmaps
        bmfHeader.bfType = 0x4D42; //BM   

        DWORD dwBytesWritten = 0;
        WriteFile(hFile, (LPSTR)&bmfHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL);
        WriteFile(hFile, (LPSTR)&bi, sizeof(BITMAPINFOHEADER), &dwBytesWritten, NULL);
        WriteFile(hFile, (LPSTR)lpbitmap, dwBmpSize, &dwBytesWritten, NULL);

        //Unlock and Free the DIB from the heap
        GlobalUnlock(hDIB);    
        GlobalFree(hDIB);

        //Close the handle for the file that was created
        CloseHandle(hFile);

    }

}

【问题讨论】:

  • PrintWindow 可以为您节省一些工作,并且在最小化时也可以工作。
  • 我排除了PrintWindow 函数,因为根据文档,听起来它会将屏幕截图生成的实际处理推迟到您正在截取屏幕截图的程序。是不是比 BitBlt 方法兼容性差?
  • 我没有深入研究它,但它确实具有最小化窗口的优势。如果您担心,可以在TakeScreenshot 函数中检查它是否已最小化,然后选择使用哪种方法。
  • 你确定坐标有错吗?也许程序正在做一些奇怪的事情。使用 Spy++ 检查。

标签: c++ windows winapi screenshot


【解决方案1】:

GetClientRect() 不包括边框和标题栏。它所做的只是告诉您客户区的尺寸

BitBlt() 将像素的矩形区域从一个设备上下文复制到另一个设备上下文。在本例中,源 DC 是一个窗口 DC,因此原点坐标是相对于该窗口的。

您的代码正在做的是从 window 的原点复制一个 client 大小的矩形。 (这就是缺少右边缘和下边缘的原因。)

您可能对AdjustWindowRectEx() 感兴趣,以帮助确定您要复制的区域的坐标。

【讨论】:

  • 不错的答案。关于您的最后一句话,如何使用AdjustWindowRectEx 来识别这些坐标?
  • AdjustWindowRectEx 用于另一个方向,扩展客户端矩形以考虑非客户端区域(有时称为镶边)。一种方法是创建一个 POINT (0, 0) 并使用 ClientToScreen 对其进行转换。使用 GetWindowRect 并将转换后的点与窗口矩形的左侧和顶部进行比较。差异将告诉您将 Bi​​tBlt 的源偏移多远。
猜你喜欢
  • 2014-11-29
  • 1970-01-01
  • 2017-05-16
  • 2012-04-16
  • 2018-08-15
  • 2023-03-18
  • 1970-01-01
  • 2019-05-21
  • 2014-01-31
相关资源
最近更新 更多