lengshizai 2024-04-10 21:42 采纳率: 31.7%
浏览 9
已结题

C++后台循环截屏bug

目标功能:后台循环截图
遇见的问题: 控制台提示 无法获取设备上下文
实际: 代码可以 运行,没有报错 ,但是就是无法获取上下文
现目标:修改代码以实现后台截屏 并适当修改以实现后台循环截屏
请自行复制代码运行 并给出解决方案 ,我已经头秃了
然后指出哪里出现了错误

img

#include <windows.h>  
#include <iostream>  
#include <fstream>  
#include <vector>  
#include <string>  
 
// BMP文件头  
#pragma pack(2)  
typedef struct {
    unsigned short bfType;
    unsigned int bfSize;
    unsigned short bfReserved1;
    unsigned short bfReserved2;
    unsigned int bfOffBits;
} BITMAPFILEHEADER;
 
// BMP信息头  
typedef struct {
    unsigned int biSize;
    int biWidth;
    int biHeight;
    unsigned short biPlanes;
    unsigned short biBitCount;
    unsigned int biCompression;
    unsigned int biSizeImage;
    int biXPelsPerMeter;
    int biYPelsPerMeter;
    unsigned int biClrUsed;
    unsigned int biClrImportant;
} BITMAPINFOHEADER;
 
// 颜色定义  
typedef struct {
    unsigned char rgbBlue;
    unsigned char rgbGreen;
    unsigned char rgbRed;
    unsigned char rgbReserved;
} RGBQUAD;
#pragma pack()  
 
bool SaveBMP(const char* filename, int width, int height, std::vector<RGBQUAD>& bits) {  
    std::ofstream outfile(filename, std::ios::out | std::ios::binary);  
    if (!outfile.is_open()) {  
        std::cerr << "Error opening file for writing: " << filename << std::endl;  
        return false;  
    }  
  
    // BMP文件头  
    BITMAPFILEHEADER fileHeader;  
    fileHeader.bfType = 0x4D42; // BM  
    fileHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (width * height * sizeof(RGBQUAD));  
    fileHeader.bfReserved1 = 0;  
    fileHeader.bfReserved2 = 0;  
    fileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);  
  
    // BMP信息头  
    BITMAPINFOHEADER infoHeader;  
    infoHeader.biSize = sizeof(BITMAPINFOHEADER);  
    infoHeader.biWidth = width;  
    infoHeader.biHeight = height;  
    infoHeader.biPlanes = 1;  
    infoHeader.biBitCount = 32;  
    infoHeader.biCompression = BI_RGB;  
    infoHeader.biSizeImage = 0; // 可以为0,因为使用的是非压缩格式  
    infoHeader.biXPelsPerMeter = 0;  
    infoHeader.biYPelsPerMeter = 0;  
    infoHeader.biClrUsed = 0;  
    infoHeader.biClrImportant = 0;  
  
    // 写入文件头和信息头  
    outfile.write(reinterpret_cast<char*>(&fileHeader), sizeof(BITMAPFILEHEADER));  
    outfile.write(reinterpret_cast<char*>(&infoHeader), sizeof(BITMAPINFOHEADER));  
  
    // 写入图像数据  
    for (int i = 0; i < height; ++i) {  
        for (int j = 0; j < width; ++j) {  
            // BMP格式是从左下角开始存储的,所以我们需要翻转图像  
            int index = (height - i - 1) * width + j;  
            outfile.write(reinterpret_cast<char*>(&bits[index]), sizeof(RGBQUAD));  
        }  
    }  
  
    outfile.close();  
    return true;  
}
 
 
 
bool CaptureWindowToBMP(HWND hwnd, const char* filename) {
    // 获取窗口的尺寸  
    RECT rect;
    if (!GetWindowRect(hwnd, &rect)) {
        std::cerr << "Error getting window rect for window handle " << hwnd << ". Error code: " << GetLastError() << std::endl;
        return false;
    }
    int width = rect.right - rect.left;
    int height = rect.bottom - rect.top;
 
    // 创建兼容DC和位图  
    HDC hdcScreen = ::GetWindowDC(hwnd);
    if (hdcScreen == NULL) {
        std::cerr << "Error: Unable to get the device context for window (" << hwnd << "). Error code: " << GetLastError() << std::endl;
        return false;
    }
 
    HDC hdcMemDC = CreateCompatibleDC(hdcScreen);
    if (hdcMemDC == NULL) {
        std::cerr << "Error: Unable to create a compatible device context. Error code: " << GetLastError() << std::endl;
        ReleaseDC(hwnd, hdcScreen); // 纠正:应该释放与窗口关联的设备上下文
        return false;
    }
 
    HBITMAP hBitmap = CreateCompatibleBitmap(hdcScreen, width, height);
    if (hBitmap == NULL) {
        std::cerr << "Error: Unable to create a compatible bitmap. Error code: " << GetLastError() << std::endl;
        DeleteDC(hdcMemDC);
        ReleaseDC(hwnd, hdcScreen);
        return false;
    }
 
    HGDIOBJ hOldBitmap = SelectObject(hdcMemDC, hBitmap);
    if (hOldBitmap == NULL) {
        std::cerr << "Error: Unable to select new bitmap into device context. Error code: " << GetLastError() << std::endl;
        DeleteObject(hBitmap);
        DeleteDC(hdcMemDC);
        ReleaseDC(hwnd, hdcScreen);
        return false;
    }
 
    // 将窗口内容拷贝到位图中  
    bool printSuccess = PrintWindow(hwnd, hdcMemDC, PW_CLIENTONLY);
    if (!printSuccess) {
        std::cerr << "Error: Failed to print window content to memory device context. Error code: " << GetLastError() << std::endl;
        // 即使打印失败,仍需继续进行资源清理
    }
 
    // 保存为BMP文件  
    BITMAP bmp;
    if (!GetObject(hBitmap, sizeof(bmp), &bmp)) {
        std::cerr << "Error: Unable to retrieve bitmap information. Error code: " << GetLastError() << std::endl;
        // 继续进行错误处理和资源清理
    }
 
    std::vector<RGBQUAD> bits(bmp.bmWidth * bmp.bmHeight);
 
    BITMAPINFO bmi = { 0 };
    bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bmi.bmiHeader.biWidth = bmp.bmWidth;
    bmi.bmiHeader.biHeight = bmp.bmHeight;
    bmi.bmiHeader.biPlanes = 1;
    bmi.bmiHeader.biBitCount = 32;
    bmi.bmiHeader.biCompression = BI_RGB;
    bmi.bmiHeader.biSizeImage = 0;
 
    if (!GetDIBits(hdcMemDC, hBitmap, 0, bmp.bmHeight, &bits[0], &bmi, DIB_RGB_COLORS)) {
        std::cerr << "Error getting bitmap bits. Error code: " << GetLastError() << std::endl;
        // 清理资源  
        SelectObject(hdcMemDC, hOldBitmap); // 还原之前选中的位图
        DeleteObject(hBitmap);
        DeleteDC(hdcMemDC);
        ReleaseDC(hwnd, hdcScreen);
        return false;
    }
 
    // 保存BMP文件  
    if (!SaveBMP(filename, bmp.bmWidth, bmp.bmHeight, bits)) {
        std::cerr << "Error saving BMP file '" << filename << "'. Error code: " << GetLastError() << std::endl;
        // 清理资源  
        SelectObject(hdcMemDC, hOldBitmap);
        DeleteObject(hBitmap);
        DeleteDC(hdcMemDC);
        ReleaseDC(hwnd, hdcScreen);
        return false;
    }
 
    // 恢复原始位图并清理资源  
    SelectObject(hdcMemDC, hOldBitmap);
    DeleteObject(hBitmap);
    DeleteDC(hdcMemDC);
    ReleaseDC(hwnd, hdcScreen);
 
    return true;
}
 
 
int main() {
    // 示例:捕获当前活动窗口并保存为BMP  
    HWND hwnd = GetForegroundWindow(); // 获取当前活动窗口句柄  
    if (hwnd == NULL) {
        std::cerr << "No active window found." << std::endl;
        return 1;
    }
 
    const char* filename = "capture.bmp";
    if (!CaptureWindowToBMP(hwnd, filename)) {
        std::cerr << "Failed to capture window to BMP." << std::endl;
        return 1;
    }
 
    std::cout << "Window captured successfully to " << filename << std::endl;
    return 0;
}
  • 写回答

16条回答 默认 最新

  • 普通 网友 2024-04-10 21:44
    关注

    以下参考免费微信小程序:皆我百晓生 作答,并由妄北y整理回复。

    从你提供的代码来看,你正在尝试捕获一个窗口的内容并将其保存为BMP文件。根据你的描述,控制台提示“无法获取设备上下文”,这通常是因为没有正确地初始化图形环境或者因为某些资源没有被正确地释放。

    在尝试解决这个问题之前,我们需要确保程序在运行结束后能够正确地释放所有分配的资源。在你的代码中,我注意到在GetDIBits函数调用失败的情况下,你没有进行资源清理。这可能会导致资源泄露,进而影响后续的截图操作。我们需要确保即使在发生错误的情况下,也能够正确地清理所有资源。

    下面是修改后的代码,我添加了错误处理和资源清理的逻辑,并且做了一些必要的修改以确保程序能够正常运行:

    #include <windows.h>
    #include <iostream>
    #include <fstream>
    #include <vector>
    #include <string>
    
    // ...(省略了之前定义的结构体和#pragma pack(2)部分)
    
    bool SaveBMP(const char* filename, int width, int height, std::vector<RGBQUAD>& bits) {
        // ...(省略了之前的SaveBMP函数代码)
    }
    
    bool CaptureWindowToBMP(HWND hwnd, const char* filename) {
        // ...(省略了之前的代码)
    
        HBITMAP hBitmap = CreateCompatibleBitmap(hdcScreen, width, height);
        if (hBitmap == NULL) {
            // ...(省略了之前的错误处理代码)
        }
    
        HDC hdcMemDC = CreateCompatibleDC(hdcScreen);
        if (hdcMemDC == NULL) {
            // ...(省略了之前的错误处理代码)
        }
    
        HGDIOBJ hOldBitmap = SelectObject(hdcMemDC, hBitmap);
        if (hOldBitmap == NULL) {
            // ...(省略了之前的错误处理代码)
        }
    
        // ...(省略了之前的代码)
    
        bool success = false;
        if (!PrintWindow(hwnd, hdcMemDC, PW_CLIENTONLY)) {
            std::cerr << "Error: Failed to print window content to memory device context. Error code: " << GetLastError() << std::endl;
            goto cleanup; // 跳转到清理代码块
        }
    
        if (!GetObject(hBitmap, sizeof(BITMAP), &bmp)) {
            std::cerr << "Error: Unable to retrieve bitmap information. Error code: " << GetLastError() << std::endl;
            goto cleanup; // 跳转到清理代码块
        }
    
        std::vector<RGBQUAD> bits(bmp.bmWidth * bmp.bmHeight);
    
        BITMAPINFO bmi = { 0 };
        bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
        bmi.bmiHeader.biWidth = bmp.bmWidth;
        bmi.bmiHeader.biHeight = bmp.bmHeight;
        bmi.bmiHeader.biPlanes = 1;
        bmi.bmiHeader.biBitCount = 32;
        bmi.bmiHeader.biCompression = BI_RGB;
        bmi.bmiHeader.biSizeImage = 0;
    
        if (!GetDIBits(hdcMemDC, hBitmap, 0, bmp.bmHeight, &bits[0], &bmi, DIB_RGB_COLORS)) {
            std::cerr << "Error getting bitmap bits. Error code: " << GetLastError() << std::endl;
            goto cleanup; // 跳转到清理代码块
        }
    
        success = SaveBMP(filename, bmp.bmWidth, bmp.bmHeight, bits);
    
    cleanup:
        if (hOldBitmap != NULL) {
            SelectObject(hdcMemDC, hOldBitmap);
        }
        if (hBitmap != NULL) {
            DeleteObject(hBitmap);
        }
        if (hdcMemDC != NULL) {
            DeleteDC(hdcMemDC);
        }
        if (hdcScreen != NULL) {
            ReleaseDC(hwnd, hdcScreen);
        }
    
        return success;
    }
    
    int main() {
        // ...(省略了之前的main函数代码)
    
        return 0;
    }
    

    在这个修改后的代码中,我添加了一个名为cleanup的标签,用于在发生错误时跳转到资源清理代码块。这样可以确保即使在错误发生时,所有的资源也都能被正确地释放。这应该会解决你遇到的“无法获取设备上下文”的问题。

    另外,你的原始代码中GetWindowDCReleaseDC的使用是正确的,但是请确保在程序的其他地方没有重复获取和释放相同的设备上下文,这也可能导致上下文无法获取的问题。

    请注意,后台截图通常需要操作系统的特殊权限,如果你的程序没有以管理员身份运行,那么即使代码本身没有错误,也可能无法成功截取屏幕。确保你的程序有足够的权限来执行这些操作。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(15条)

报告相同问题?

问题事件

  • 系统已结题 4月20日
  • 已采纳回答 4月12日
  • 创建了问题 4月10日

悬赏问题

  • ¥15 软件工程用例图的建立(相关搜索:软件工程用例图|画图)
  • ¥15 如何在arcgis中导出拓扑关系表
  • ¥15 处理数据集文本挖掘代码
  • ¥15 matlab2017
  • ¥15 在vxWorks下TCP/IP编程,总是connect()报错,连接服务器失败: errno = 0x41
  • ¥15 AnolisOs7.9如何安装 Qt_5.14.2的运行库
  • ¥20 求:怎么实现qt与pcie通信
  • ¥50 前后端数据顺序不一致问题,如何解决?(相关搜索:数据结构)
  • ¥15 基于蒙特卡罗法的中介效应点估计代码
  • ¥15 罗技G293和UE5.3