Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Screen capture fails to capture whole screen using C++ and GDI

Tags:

c++

winapi

gdi

I made some research through the web, and found some useful code. I changed it a bit, in attempt to capture the whole screen and generate a buffer that I can send through udp packets:

#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;
}

But it only seems to capture this part of my desktop:

enter image description here

And that's even though I use CreateCompatibleDC(0).

like image 591
W2a Avatar asked Nov 01 '16 19:11

W2a


1 Answers

If the computer is on high DPI settings, and the application is not DPI aware, then the system will lie to the application and give the wrong screen size.

You will find the following code shows width and height which are smaller than the actual screen size:

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

The solution is to add DPI awareness to application.

To add DPI compatibility:

In Visual Studio 2015, go to Project Properties -> Manifest tools, set DPI awareness to "Per Monitor High DPI Aware" or "High DPI Aware"

If you are using some old compiler...

1) create a file "myapp.manifest" with this content:

<?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) Add *.rc file to your project with this content:

1 24 "myapp.manifest"

3) Rebuild the project

like image 152
Barmak Shemirani Avatar answered Sep 20 '22 08:09

Barmak Shemirani