屏幕捕获无法使用 C++ 和 GDI 捕获整个屏幕
Screen capture fails to capture whole screen using C++ and GDI
我通过网络进行了一些研究,找到了一些有用的代码。我稍微改变了它,试图捕获整个屏幕并生成一个缓冲区,我可以通过 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) 也是如此。
如果计算机处于高 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) 重建项目
我通过网络进行了一些研究,找到了一些有用的代码。我稍微改变了它,试图捕获整个屏幕并生成一个缓冲区,我可以通过 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) 也是如此。
如果计算机处于高 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) 重建项目