You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

截图与复制位图的结构差异及BMP保存异常排查

Hey there! Let's break down why your code works for Paint's copied bitmaps but fails with Print Screen screenshots, and fix it up properly.

Key Differences Between the Two Clipboard Bitmap Types

  • Paint's copied bitmap: Typically uses the CF_DIB format, which wraps a standard BITMAPINFOHEADER (40 bytes) followed by a color table (if needed) and raw pixel data. This matches the fixed-size DIB struct you defined, so your code handles it without issues.
  • Print Screen screenshot: Usually comes in the CF_DIBV5 format, which uses a BITMAPV5HEADER (124 bytes). This extended header includes extra color space information (like ICC profiles) that your custom DIB struct can't account for. When you force-cast CF_DIBV5 data to your DIB type, you end up reading invalid values for critical fields like biSizeImage, which breaks file size and offset calculations.

Critical Issues in Your Current Code

  1. Hardcoded offset (54 bytes): This only works for BITMAPINFOHEADER (14-byte file header + 40-byte info header = 54). For BITMAPV5HEADER, the correct offset is 14 + biSize (14 is the size of your HEADER struct, plus the actual header size from the clipboard).
  2. Incorrect bfSize calculation: Your code adds biSizeImage to 54, but ignores the size of the info header itself and any extra data (like color tables or ICC profiles) in the DIB. This leads to a file that's too small or misaligned.
  3. Struct mismatch: Your custom DIB struct doesn't support BITMAPV5HEADER fields, so processing CF_DIBV5 data results in corrupted header values.

Fixed Code That Handles Both Formats

Here's a revised version that properly supports both CF_DIB and CF_DIBV5:

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

// Use Windows-native header structures to avoid mismatches
typedef BITMAPINFOHEADER DIBStandardHeader;
typedef BITMAPV5HEADER DIBV5Header;

typedef struct {
    std::uint16_t type;
    std::uint32_t bfSize;
    std::uint32_t reserved;
    std::uint32_t offset;
} BMPFileHeader;

int main() {
    std::cout << "Format Bitmap: " << IsClipboardFormatAvailable(CF_BITMAP) << "\n";
    std::cout << "Format DIB: " << IsClipboardFormatAvailable(CF_DIB) << "\n";
    std::cout << "Format DIBv5: " << IsClipboardFormatAvailable(CF_DIBV5) << "\n";

    if (IsClipboardFormatAvailable(CF_DIB) || IsClipboardFormatAvailable(CF_DIBV5)) {
        if (OpenClipboard(NULL)) {
            HANDLE hClipboard = nullptr;
            bool isDIBV5 = false;

            // Prioritize CF_DIB first, fall back to CF_DIBV5
            if (IsClipboardFormatAvailable(CF_DIB)) {
                hClipboard = GetClipboardData(CF_DIB);
            } else if (IsClipboardFormatAvailable(CF_DIBV5)) {
                hClipboard = GetClipboardData(CF_DIBV5);
                isDIBV5 = true;
            }

            if (hClipboard && hClipboard != INVALID_HANDLE_VALUE) {
                void* dibData = GlobalLock(hClipboard);
                if (dibData) {
                    DWORD totalDIBSize = GlobalSize(hClipboard);
                    BMPFileHeader fileHeader = {0};
                    fileHeader.type = 0x4D42; // Magic "BM" identifier
                    fileHeader.offset = sizeof(BMPFileHeader);

                    // Calculate correct offset and file size based on header type
                    if (isDIBV5) {
                        DIBV5Header* v5Header = reinterpret_cast<DIBV5Header*>(dibData);
                        fileHeader.offset += v5Header->bV5Size;
                        fileHeader.bfSize = sizeof(BMPFileHeader) + totalDIBSize;

                        // Print V5 header details for debugging
                        std::cout << "\nBITMAPV5HEADER detected:\n";
                        std::cout << "Header Size: " << v5Header->bV5Size << "\n";
                        std::cout << "Width: " << v5Header->bV5Width << "\n";
                        std::cout << "Height: " << v5Header->bV5Height << "\n";
                        std::cout << "Bit Count: " << v5Header->bV5BitCount << "\n";
                    } else {
                        DIBStandardHeader* stdHeader = reinterpret_cast<DIBStandardHeader*>(dibData);
                        fileHeader.offset += stdHeader->biSize;
                        fileHeader.bfSize = sizeof(BMPFileHeader) + totalDIBSize;

                        // Print standard header details for debugging
                        std::cout << "\nBITMAPINFOHEADER detected:\n";
                        std::cout << "Header Size: " << stdHeader->biSize << "\n";
                        std::cout << "Width: " << stdHeader->biWidth << "\n";
                        std::cout << "Height: " << stdHeader->biHeight << "\n";
                        std::cout << "Bit Count: " << stdHeader->biBitCount << "\n";
                    }

                    // Write the complete BMP file
                    std::ofstream file("Test2.bmp", std::ios::out | std::ios::binary);
                    if (file) {
                        // Write the file header
                        file.write(reinterpret_cast<char*>(&fileHeader), sizeof(fileHeader));
                        // Write the entire DIB data (header + color data + pixels)
                        file.write(reinterpret_cast<char*>(dibData), totalDIBSize);
                        std::cout << "\nFile saved successfully! You should be able to open it now.\n";
                    } else {
                        std::cerr << "\nFailed to open file for writing.\n";
                    }

                    GlobalUnlock(hClipboard);
                } else {
                    std::cerr << "\nFailed to lock clipboard data.\n";
                }
            } else {
                std::cerr << "\nFailed to retrieve clipboard data.\n";
            }
            CloseClipboard();
        } else {
            std::cerr << "\nFailed to open clipboard.\n";
        }
    } else {
        std::cerr << "\nNo compatible bitmap format found in clipboard.\n";
    }

    return 0;
}

What Changed?

  • Uses Windows-native headers: BITMAPINFOHEADER and BITMAPV5HEADER ensure we handle both header types without struct mismatches.
  • Dynamic offset calculation: Instead of hardcoding 54, we calculate the offset based on the actual header size from the clipboard data.
  • Accurate file size: The total file size is sizeof(BMPFileHeader) + GlobalSize(hClipboard), which includes all DIB data (header, color tables, ICC profiles, pixels).
  • Writes full DIB data: We write the entire clipboard DIB block directly, avoiding errors from manually copying individual header fields.

This code will work for both Paint's copied bitmaps and Print Screen screenshots, as it properly handles both CF_DIB and CF_DIBV5 formats.

内容的提问来源于stack exchange,提问作者Avi El

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 09:06:03