C: 尝试将 BITMAP 转换为 .bmp 文件时出现问题

C: Trouble when trying to comvert HBITMAP into .bmp file

我正在创建一个将屏幕截图保存到 .bmp 文件的功能,我已经设法将屏幕保存到 HBITMAP,但我在保存时遇到了问题。我将不胜感激。

这里是 header 函数:

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <stdbool.h>
#include <wingdi.h>
#include <winuser.h>

typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned long u32;
typedef unsigned long long u64;

void getScreen()
{
    u16 screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
    u16 screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);

    HDC hdc = GetDC(NULL); //get a desktop dc (NULL for entire screen)
    HDC hDest = CreateCompatibleDC(hdc); //create a dc for capture

    ReleaseDC(NULL, hdc);

    HBITMAP hbCapture = CreateCompatibleBitmap(hdc, screenWidth, screenHeight);
    SelectObject(hDest, hbCapture);

    //Copy screen to bitmap
    BitBlt(hDest, 0, 0, screenWidth, screenHeight, hdc, 0, 0, SRCCOPY);

//test
    ReleaseDC(NULL, hdc);

    char memBuffer[10000];
    BITMAPINFO bitmapInfo;
    bitmapInfo.bmiHeader.biHeight = screenHeight;
    bitmapInfo.bmiHeader.biWidth = screenWidth;
    bitmapInfo.bmiHeader.biSize = screenWidth * screenHeight * 3;
    bitmapInfo.bmiHeader.biCompression = BI_RGB;//NOT SURE
    bitmapInfo.bmiHeader.biPlanes = 1;
    bitmapInfo.bmiHeader.biBitCount = 8; //NOT SURE
    bitmapInfo.bmiHeader.biClrImportant = 0;
    GetDIBits(hDest, hbCapture, 0, screenHeight, &memBuffer, &bitmapInfo, DIB_RGB_COLORS);

    FILE * fPointer = fopen("screen.png", "wb");//TO CHANGE
    WriteFile(fPointer, &memBuffer, (WORD) sizeof(memBuffer), 0, NULL);

    fclose(fPointer);
//test

    //Clean up
    ReleaseDC(NULL, hdc);
    DeleteDC(hDest);
    DeleteObject(hbCapture);
}

这里还有 main.c:

#include "main.h"

int main()
{
    getScreen();

    return 0;
}

我已经在 Whosebug 中阅读了关于这个主题的所有问题,其中 none 已经为我澄清了问题。

bitmapInfo.bmiHeader.biSize = screenWidth * screenHeight * 3;

biSize不是总像素数,是位图信息结构的大小,总是sizeof(BITMAPINFOHEADER).

您想改为设置 bitmapInfo.bmiHeader.biSizeImage。这个值大概是 (width * height * bits_per_pixel/8) 但必须调整,因为每一行都被填充了。也不要将 fopen 句柄与 CreateFile 句柄混合使用,如注释中所述。

fopen("screen.png", "wb");

您无法使用此方法保存为png 格式。对于 png 格式,您需要一个不同的库,例如 Gdiplus。

要保存为位图,您必须设置BITMAFILEHEADERBITMAPINFOHEADER并将它们写入文件,然后写入从GetDIBits

获得的位

您可能希望避免使用 typedef unsigned char u8; 等。您可以包含 <stdint.h> 并使用标准类型 uint8_tuint16_tuint32_t 等。或者您可以使用标准 Windows 类型,例如 BYTE

void getScreen()
{
    int screenWidth  = GetSystemMetrics(SM_CXVIRTUALSCREEN);
    int screenHeight  = GetSystemMetrics(SM_CYVIRTUALSCREEN);

    HDC hdc = GetDC(NULL);
    HDC hDest = CreateCompatibleDC(hdc);
    HBITMAP hbCapture = CreateCompatibleBitmap(hdc, screenWidth , screenHeight );
    HBITMAP oldbmp = (HBITMAP)SelectObject(hDest, hbCapture);
    BitBlt(hDest, 0, 0, screenWidth , screenHeight , hdc, 0, 0, SRCCOPY);

    WORD bpp = 24; //<- bits per pixel
    int size = ((screenWidth  * bpp + 31) / 32) * 4 * screenHeight ;

    BITMAPINFOHEADER bmp_info_hdr;
    bmp_info_hdr.biSize = sizeof(bmp_info_hdr);
    bmp_info_hdr.biHeight  = screenHeight ;
    bmp_info_hdr.biWidth  = screenWidth ;
    bmp_info_hdr.biPlanes = 1;
    bmp_info_hdr.biBitCount = bpp;
    bmp_info_hdr.biCompression = BI_RGB;
    bmp_info_hdr.biSizeImage = size;

    BITMAPFILEHEADER bmp_file_hdr;
    bmp_file_hdr.bfType = (WORD)'MB';
    bmp_file_hdr.bfOffBits = sizeof(bmp_file_hdr) + sizeof(bmp_info_hdr);
    bmp_file_hdr.bfSize = bmp_file_hdr.bfOffBits + size;

    char *buffer = malloc(size);
    GetDIBits(hDest, hbCapture, 0, screenHeight , buffer, 
        (BITMAPINFO*)&bmp_info_hdr, DIB_RGB_COLORS);

    FILE * fPointer = fopen("screen.bmp", "wb");
    fwrite((char*)&bmp_file_hdr, sizeof(bmp_file_hdr), 1, fPointer);
    fwrite((char*)&bmp_info_hdr, sizeof(bmp_info_hdr), 1, fPointer);
    fwrite(buffer, size, 1, fPointer);
    fclose(fPointer);

    free(buffer);

    SelectObject(hDest, oldbmp);
    DeleteObject(hbCapture);
    DeleteDC(hDest);
    ReleaseDC(NULL, hdc);
}