【问题标题】:Screen capture fails to capture whole screen using C++ and GDI屏幕捕获无法使用 C++ 和 GDI 捕获整个屏幕
【发布时间】:2017-03-15 00:32:24
【问题描述】:

我通过网络进行了一些研究,并找到了一些有用的代码。我稍微改了一下,试图捕获整个屏幕并生成一个可以通过 udp 数据包发送的缓冲区:

#include <iostream>
#include <Windows.h>
#include <fstream>

void CapruteScreenAndSaveToFile()
{
    uint16_t BitsPerPixel = 24;
    uint32_t Width = GetSystemMetrics(SM_CXSCREEN);
    uint32_t Height = GetSystemMetrics(SM_CYSCREEN);

    // Create Header
    BITMAPFILEHEADER Header;
    memset(&Header, 0, sizeof(Header));
    Header.bfType = 0x4D42;
    Header.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

    // Create Info
    BITMAPINFO Info;
    memset(&Info, 0, sizeof(Info));
    Info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    Info.bmiHeader.biWidth = Width;
    Info.bmiHeader.biHeight = Height;
    Info.bmiHeader.biPlanes = 1;
    Info.bmiHeader.biBitCount = BitsPerPixel;
    Info.bmiHeader.biCompression = BI_RGB;
    Info.bmiHeader.biSizeImage = Width * Height * (BitsPerPixel > 24 ? 4 : 3);

    // Capture screen and save to Pixels
    char* Pixels = NULL;
    HDC MemDC = CreateCompatibleDC(0);//Context);
    HBITMAP Section = CreateDIBSection(MemDC, &Info, DIB_RGB_COLORS, (void**)&Pixels, 0, 0);
    DeleteObject(SelectObject(MemDC, Section));
    BitBlt(MemDC, 0, 0, Width, Height, GetDC(0), 0, 0, SRCCOPY);
    DeleteDC(MemDC);

    // Concatenate everything
    char * buffer = (char*)malloc(sizeof(Header) + sizeof(Info.bmiHeader) + (((BitsPerPixel * Width + 31) & ~31) / 8) * Height);

    memcpy(buffer, (char*)&Header, sizeof(Header));
    memcpy(buffer + sizeof(Header), (char*)&Info.bmiHeader, sizeof(Info.bmiHeader));
    memcpy(buffer + sizeof(Header) + sizeof(Info.bmiHeader), Pixels, (((BitsPerPixel * Width + 31) & ~31) / 8) * Height);

    // Save to file
    std::fstream hFile("Foo.bmp", std::ios::out | std::ios::binary);

    hFile.write(buffer, sizeof(Header) + sizeof(Info.bmiHeader) + (((BitsPerPixel * Width + 31) & ~31) / 8) * Height);

    // Clean up
    hFile.close();
    DeleteObject(Section);
    free(buffer);
}


int main()
{
    CapruteScreenAndSaveToFile();

    return 0;
}

但它似乎只捕获了我桌面的这一部分:

即使我使用的是 CreateCompatibleDC(0)。

【问题讨论】:

  • 我在我的电脑上测试了你的代码。它工作正常。生成的“bmp”有全屏截图。
  • 别忘了还有SetWindowDisplayAffinity,还有OpenGL。两者都可能成为您代码的障碍,并使其失败。不是您要在这里解决的问题,而是您将不可避免地遇到的问题。

标签: c++ winapi gdi


【解决方案1】:

如果计算机处于高 DPI 设置,而应用程序无法识别 DPI,那么系统将欺骗应用程序并提供错误的屏幕尺寸。

您会发现以下代码显示的宽度和高度小于实际屏幕尺寸:

uint32_t Width = GetSystemMetrics(SM_CXSCREEN);
uint32_t Height = GetSystemMetrics(SM_CYSCREEN);
std::cout << Width << " x " << Height << "\n";

解决方案是在应用程序中添加DPI awareness

添加 DPI 兼容性:

在 Visual Studio 2015 中,转到项目属性 -> 清单工具,将 DPI 感知设置为“Per Monitor High DPI Aware”或“High DPI Aware”

如果你使用的是一些旧的编译器...

1) 创建一个包含以下内容的文件“myapp.manifest”:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
      <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True/PM</dpiAware>
    </windowsSettings>
  </application>
</assembly>

2) 使用以下内容将 *.rc 文件添加到您的项目中:

1 24 "myapp.manifest"

3) 重建项目

【讨论】:

  • 这可能是正确的答案,但它不适用于我的 Windows 8.1,它显然比 Vista 更新 .. 因此我仍然需要进一步的帮助。它说 SetProcessDPIAware 没有在这个范围内声明,即使我包含了 Windows.h 并链接了 User32.lib 。
  • SetProcessDPIAware 不起作用,因为您没有定义 _WIN32_WINNT 或者您使用的是不兼容的 MinGW-32。无论如何,添加 DPI 感知的正确方法是通过清单文件。不要把它变成一个我必须猜测一切的谜题,提供一些最少的信息,这样你得到的错误是可重现的。
  • 是的,我正在使用 MinGW-32。那我应该用什么?非常感谢!
  • 我认为您只阅读了部分答案和部分评论。您必须使用清单文件,如答案所示。
  • 对不起,我没有注意到您编辑了答案,但它不起作用。我在项目文件夹中创建了一个 dpi.manifest 文件,其中包含您提供的内容,并添加了一个“ resource.rc" 文件,内容为 1 24 "dpi.manifest",但编译器仍然无法识别 SetProcessDPIAware ..
猜你喜欢
  • 2012-04-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-03
  • 1970-01-01
  • 2014-02-26
  • 2015-04-06
相关资源
最近更新 更多