Скриншот с помощью С++

276
19 ноября 2017, 16:11

Делаю параграмму на С++ которая через интернет будет передавать скриншот рабочого стола и сохранять на другом компе.

Пока что питаюсь просто сделать скриншот и сохранить в форматах bmp и tga, но изображение в перфом формате выходит какое то волнистое, а во втором косое, помогите пожалуйста исправить.

#include<Windows.h>
#include<stdio.h>
#include <fstream>
#include <iostream>
using namespace std;

int main()
{
// Определение контекстов
HDC ScreenDC = GetDC(0);
HDC MemoryDC = CreateCompatibleDC(ScreenDC);
// Фиксация размеров экрана
int ScreenWidth = GetSystemMetrics(SM_CXSCREEN);
int ScreenHeight = GetSystemMetrics(SM_CYSCREEN);
// Создание и частичное заполнение структуры формата
BITMAPINFO BMI;
BMI.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
BMI.bmiHeader.biWidth = ScreenWidth;
BMI.bmiHeader.biHeight = -ScreenHeight; // Отрицательное значение высоты, чтобы изображение не было перевёрнутым
BMI.bmiHeader.biSizeImage = ScreenWidth * ScreenHeight * 3; // Ширина * Высота * Количество_цветов_на_пиксель
BMI.bmiHeader.biCompression = BI_RGB;
BMI.bmiHeader.biBitCount  = 24;
BMI.bmiHeader.biPlanes = 1;
DWORD ScreenshotSize;
ScreenshotSize = BMI.bmiHeader.biSizeImage; // ScreenshotSize - глобальная переменная типа int, может меняться в ходе выполнения программы
unsigned char *ImageBuffer; // Указатель на блок данных BGR, управляемый HBITMAP (да, именно BGR - не RGB)
HBITMAP hBitmap = CreateDIBSection(ScreenDC,&BMI,DIB_RGB_COLORS,(void**)&ImageBuffer,0,0);
SelectObject(MemoryDC, hBitmap);
BitBlt(MemoryDC, 0, 0, ScreenWidth, ScreenHeight, ScreenDC , 0, 0, SRCCOPY);
// Контексты больше не нужны
DeleteDC(MemoryDC);
ReleaseDC(NULL, ScreenDC);

// Если требуется RGB вместо BGR - следующий цикл перевернёт нужные байты
for(int i = 0; i < ScreenshotSize; i += 3){
    unsigned char ColorValue = ImageBuffer[i];
    ImageBuffer[i] = ImageBuffer[i + 2];
    ImageBuffer[i + 2] = ColorValue;
}

//bmp========================================================
BITMAPINFO bmi;
memset(&bmi, 0, sizeof(BITMAPINFOHEADER));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biHeight = GetSystemMetrics(SM_CYSCREEN);
bmi.bmiHeader.biWidth =  GetSystemMetrics(SM_CXSCREEN);
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 24;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage =ScreenWidth * ScreenHeight * 3 ;
FILE *F=fopen("screen.bmp","w");
int nBitsOffset = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); 
LONG lImageSize = ScreenWidth * ScreenHeight * 3;
LONG lFileSize = nBitsOffset + lImageSize;
BITMAPFILEHEADER bmfh;
bmfh.bfType = 'B'+('M'<<8);
bmfh.bfOffBits = nBitsOffset;
bmfh.bfSize = lFileSize;
bmfh.bfReserved1 = bmfh.bfReserved2 = 0;
UINT nWrittenFileHeaderSize = fwrite(&bmfh,1,sizeof(BITMAPFILEHEADER),F);
UINT nWrittenInfoHeaderSize = fwrite(&bmi,1,sizeof(BITMAPINFOHEADER),F);
UINT nWrittenDIBDataSize =fwrite(ImageBuffer,1,ScreenWidth * ScreenHeight * 3,F);
UINT total=nWrittenDIBDataSize +nWrittenInfoHeaderSize+nWrittenFileHeaderSize ;
fclose(F);


//tga====================================================================================================8888888888888888888888888888888

    //
    // Теперь нужно записать полученные данные в файл Screen.tga.
    //
    FILE *sFile = 0;        // Дескриптор файла
    // Обьявляем переменные, которые понадобятся нам в дальнейшем:
    unsigned char tgaHeader[12] = {0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0};
    unsigned char header[6];
    unsigned char tempColors = 0;
    // Открываем файл скриншота
    sFile = fopen("Screen.tga", "wb");
    // Проверяем, правильно ли произошло открытие
    if(!sFile){
         return 0;
    }

    // Записываем ширину и высоту:
    header[0] = ScreenWidth % 256;
    header[1] = ScreenWidth / 256;
    header[2] = ScreenHeight % 256;
    header[3] = ScreenHeight / 256;
    header[4] = BMI.bmiHeader.biBitCount;
    header[5] = 0;
    // Записываем хидеры в начало файла:
    fwrite(tgaHeader, sizeof(tgaHeader), 1, sFile);
    fwrite(header, sizeof(header), 1, sFile);
    // Записываем данные изображения:
    fwrite(ImageBuffer, BMI.bmiHeader.biSizeImage, 1, sFile);
    // Закрываем файл
    fclose(sFile);
    // Удаляем ненужные теперь данные


// Используем ImageBuffer как нам хочется... после удаляем его хендлер (HBITMAP) для избежания утечки памяти
DeleteObject(hBitmap);

}

bmp в этот раз не волнистое

tga

Новая ошибка

Answer 1

В HBITMAP каждая строка пикселей выравнивается до границы, кратной 4-м байтам. Поэтому нельзя просто так брать и писать

fwrite(ImageBuffer,1,ScreenWidth * ScreenHeight * 3,F)

Если вам не критично, можете принудительно уменьшать ScreenWidth до величины, кратной 4-м, а потом делать скриншот:

int ScreenWidth = GetSystemMetrics(SM_CXSCREEN);
ScreenWidth = ((ScreenWidth - 1) / 4 + 1) * 4;

Либо сохранять по одной линии, отступая каждый раз необходимое количество байт.

P.S. Чтобы убедиться в том, что я говорю, сделайте разрешение экрана кратное 4-м.

P.P.S. У Вас там картинка перевернута

Answer 2
  • Вы не проверяете результаты вызова большинства функций.

  • После BitBlt необходимо вернуть контекст в первоначальное состояние, выбрав в него исходный битмап.

::HBITMAP const initial_bitmap_handle(::SelectObject(MemoryDC, hBitmap));
assert(NULL != blt_result);
::BOOL const blt_result(::BitBlt(MemoryDC, 0, 0, ScreenWidth, ScreenHeight, ScreenDC , 0, 0, SRCCOPY));
assert(FALSE != blt_result);
::HBITMAP const deselected_bitmap_handle(::SelectObject(MemoryDC, old_bitmap_handle));
assert(deselected_bitmap_handle == hBitmap);
::BOOL const delete_dc_result(::DeleteDC(MemoryDC));
assert(FALSE != delete_dc_result);
  • Вы открываете битмап файл на запись в текстовом режиме, чтобы записывать бинарные данные следует добавить флажок b:
  FILE *F=fopen("screen.bmp","wb");
  • Также битмапы обычно имеют выравнивание в 4 байта и к каждой строке может добавляться несколько байт чтобы ее размер был кратен 4 байтам. Когда ширина экрана 1366 будет добавляться 2 байта.
Answer 3

Вот результат с bmp то же что и сначала, а tga всё идиально

#include<Windows.h>
#include<stdio.h>
#include <fstream>
#include <iostream>
using namespace std;

int main()
{
// Определение контекстов
HDC ScreenDC = GetDC(0);
HDC MemoryDC = CreateCompatibleDC(ScreenDC);
// Фиксация размеров экрана
//int ScreenWidth = GetSystemMetrics(SM_CXSCREEN);
int ScreenHeight = GetSystemMetrics(SM_CYSCREEN);
int ScreenWidth = GetSystemMetrics(SM_CXSCREEN);
ScreenWidth = ((ScreenWidth - 1) / 4 + 1) * 4;
// Создание и частичное заполнение структуры формата
BITMAPINFO BMI;
BMI.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
BMI.bmiHeader.biWidth = ScreenWidth;
BMI.bmiHeader.biHeight = ScreenHeight; // Отрицательное значение высоты, чтобы изображение не было перевёрнутым
BMI.bmiHeader.biSizeImage = ScreenWidth * ScreenHeight * 3; // Ширина * Высота * Количество_цветов_на_пиксель
BMI.bmiHeader.biCompression = BI_RGB;
BMI.bmiHeader.biBitCount  = 24;
BMI.bmiHeader.biPlanes = 1;
DWORD ScreenshotSize;
ScreenshotSize = BMI.bmiHeader.biSizeImage; // ScreenshotSize - глобальная переменная типа int, может меняться в ходе выполнения программы
unsigned char *ImageBuffer; // Указатель на блок данных BGR, управляемый HBITMAP (да, именно BGR - не RGB)
HBITMAP hBitmap = CreateDIBSection(ScreenDC,&BMI,DIB_RGB_COLORS,(void**)&ImageBuffer,0,0);
SelectObject(MemoryDC, hBitmap);
BitBlt(MemoryDC, 0, 0, ScreenWidth, ScreenHeight, ScreenDC , 0, 0, SRCCOPY);
// Контексты больше не нужны
DeleteDC(MemoryDC);
ReleaseDC(NULL, ScreenDC);

// Если требуется RGB вместо BGR - следующий цикл перевернёт нужные байты
//for(int i = 0; i < ScreenshotSize; i += 3){
//    unsigned char ColorValue = ImageBuffer[i];
//    ImageBuffer[i] = ImageBuffer[i + 2];
//    ImageBuffer[i + 2] = ColorValue;
//}

//bmp========================================================
BITMAPINFO bmi;
memset(&bmi, 0, sizeof(BITMAPINFOHEADER));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biHeight = ScreenHeight;
bmi.bmiHeader.biWidth =  ScreenWidth;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 24;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage =ScreenWidth * ScreenHeight * 3 ;
FILE *F=fopen("screen.bmp","w");
int nBitsOffset = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); 
LONG lImageSize = ScreenWidth * ScreenHeight * 3;
LONG lFileSize = nBitsOffset + lImageSize;
BITMAPFILEHEADER bmfh;
bmfh.bfType = 'B'+('M'<<8);
bmfh.bfOffBits = nBitsOffset;
bmfh.bfSize = lFileSize;
bmfh.bfReserved1 = bmfh.bfReserved2 = 0;
UINT nWrittenFileHeaderSize = fwrite(&bmfh,1,sizeof(BITMAPFILEHEADER),F);
UINT nWrittenInfoHeaderSize = fwrite(&bmi,1,sizeof(BITMAPINFOHEADER),F);
UINT nWrittenDIBDataSize =fwrite(ImageBuffer,1,ScreenWidth * ScreenHeight * 3,F);
UINT total=nWrittenDIBDataSize +nWrittenInfoHeaderSize+nWrittenFileHeaderSize ;
fclose(F);


//tga====================================================================================================8888888888888888888888888888888

    //
    // Теперь нужно записать полученные данные в файл Screen.tga.
    //
    FILE *sFile = 0;        // Дескриптор файла
    // Обьявляем переменные, которые понадобятся нам в дальнейшем:
    unsigned char tgaHeader[12] = {0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0};
    unsigned char header[6];
    unsigned char tempColors = 0;
    // Открываем файл скриншота
    sFile = fopen("Screen.tga", "wb");
    // Проверяем, правильно ли произошло открытие
    if(!sFile){
         return 0;
    }

    // Записываем ширину и высоту:
    header[0] = ScreenWidth % 256;
    header[1] = ScreenWidth / 256;
    header[2] = ScreenHeight % 256;
    header[3] = ScreenHeight / 256;
    header[4] = BMI.bmiHeader.biBitCount;
    header[5] = 0;
    // Записываем хидеры в начало файла:
    fwrite(tgaHeader, sizeof(tgaHeader), 1, sFile);
    fwrite(header, sizeof(header), 1, sFile);
    // Записываем данные изображения:
    fwrite(ImageBuffer, BMI.bmiHeader.biSizeImage, 1, sFile);
    // Закрываем файл
    fclose(sFile);
    // Удаляем ненужные теперь данные


// Используем ImageBuffer как нам хочется... после удаляем его хендлер (HBITMAP) для избежания утечки памяти
DeleteObject(hBitmap);

}

Запечатал всё в функции , подозреваю что параграмма может не работать если глубина цвета экрана будет изменена на экране пользователя. Потерь памяти нет.

   #include<Windows.h>
#include<stdio.h>
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
struct ScrenData
{
    int ScreenWidth,
        ScreenHeigh;
    unsigned char *ImageBuffer;
};
bool Scren(bool (*OBR)(ScrenData &SDt,char* url),char* url)
{
ScrenData SDt;
    // Определение контекстов
HDC ScreenDC = GetDC(0);
HDC MemoryDC = CreateCompatibleDC(ScreenDC);
// Фиксация размеров экрана
//int ScreenWidth = GetSystemMetrics(SM_CXSCREEN);
SDt.ScreenHeigh = GetSystemMetrics(SM_CYSCREEN);
SDt.ScreenWidth = GetSystemMetrics(SM_CXSCREEN);
SDt.ScreenWidth = ((SDt.ScreenWidth - 1) / 4 + 1) * 4;
// Создание и частичное заполнение структуры формата
BITMAPINFO BMI;
BMI.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
BMI.bmiHeader.biWidth = SDt.ScreenWidth;
BMI.bmiHeader.biHeight = SDt.ScreenHeigh; // Отрицательное значение высоты, чтобы изображение не было перевёрнутым
BMI.bmiHeader.biSizeImage = SDt.ScreenWidth * SDt.ScreenHeigh * 3; // Ширина * Высота * Количество_цветов_на_пиксель
BMI.bmiHeader.biCompression = BI_RGB;
BMI.bmiHeader.biBitCount  = 24;
BMI.bmiHeader.biPlanes = 1;
DWORD ScreenshotSize;
ScreenshotSize = BMI.bmiHeader.biSizeImage; // ScreenshotSize - глобальная переменная типа int, может меняться в ходе выполнения программы
//unsigned char *ImageBuffer; // Указатель на блок данных BGR, управляемый HBITMAP (да, именно BGR - не RGB)
HBITMAP hBitmap = CreateDIBSection(ScreenDC,&BMI,DIB_RGB_COLORS,(void**)&SDt.ImageBuffer,0,0);
SelectObject(MemoryDC, hBitmap);
BitBlt(MemoryDC, 0, 0, SDt.ScreenWidth, SDt.ScreenHeigh, ScreenDC , 0, 0, SRCCOPY);
bool ret  = OBR(SDt,url); //Отправить даные в функцыю оброботки
// Контексты больше не нужны
DeleteDC(MemoryDC);
ReleaseDC(NULL, ScreenDC);
DeleteObject(hBitmap); 
//delete [] SDt.ImageBuffer;
return ret;
}
bool SaveTga(ScrenData &SDt,char* url)
{
     //
    // Теперь нужно записать полученные данные в файл Screen.tga.
    //
    FILE *sFile = 0;        // Дескриптор файла
    // Обьявляем переменные, которые понадобятся нам в дальнейшем:
    unsigned char tgaHeader[12] = {0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0};
    unsigned char header[6];
    unsigned char tempColors = 0;
    // Открываем файл скриншота
    sFile = fopen(url, "wb");
    // Проверяем, правильно ли произошло открытие
    if(!sFile){
         return 0;
    }

    // Записываем ширину и высоту:
    header[0] = SDt.ScreenWidth % 256;
    header[1] = SDt.ScreenWidth / 256;
    header[2] = SDt.ScreenHeigh % 256;
    header[3] = SDt.ScreenHeigh / 256;
    header[4] = 24;
    header[5] = 0;
    // Записываем хидеры в начало файла:
    fwrite(tgaHeader, sizeof(tgaHeader), 1, sFile);
    fwrite(header, sizeof(header), 1, sFile);
    // Записываем данные изображения:
    fwrite(SDt.ImageBuffer, SDt.ScreenWidth * SDt.ScreenHeigh * 3, 1, sFile);
    // Закрываем файл
    fclose(sFile);
    // Удаляем ненужные теперь данные
    return 1;
}

bool OBR1(ScrenData &SDt,char* url)
{
    //Тут можна отправить кудато даные или передать в функцыю сохранения
    return SaveTga(SDt,url);
}

int main()
{
/*  
    std::string url;
    int i = 0;
    while(1)
    {
        {
            url = "Scren";
            char D[10];
            itoa(i,D,10);
            url += D;
            url += ".tga";
            Scren(OBR1,(char*)url.c_str()); //Сделать скриншот и даные отправить в функцыю OBR можна сразу отправлять в SaveTga
            i++;
        }
    }
    */
        Scren(OBR1,"scren.tga"); //Сделать скриншот и даные отправить в функцыю OBR можна сразу отправлять в SaveTga
// Используем ImageBuffer как нам хочется... после удаляем его хендлер (HBITMAP) для избежания утечки памяти
//DeleteObject(hBitmap); 
}
READ ALSO
Сигналы и слоты на Qt и QML

Сигналы и слоты на Qt и QML

Как связать QML интерфейс и некий гибрид MVC паттерна ? Суть в том, что есть класс Model и в нем поля string и bool и методы get/setДля контроля моделями создается...

284
C++ работа с другой программой

C++ работа с другой программой

ЗдравствуйтеНедавно получил задание от друга, создать простую программу, которая поможет работать ему с другой программой

245
Генерирование матрицы (C++)

Генерирование матрицы (C++)

Доброго времени сутокЕсть задание: реализовать программу, которая меняет местами последнюю строку и первый столбик квадратной матрицы

248