使用GDI+时如何修复Access violation内存访问冲突错误?
GDI+截图发送内存访问错误修复
问题场景
开发服务器-客户端系统时,用GDI+捕获屏幕截图并发送给客户端,终端提示截图已保存并发送成功,但触发内存访问错误:
Exception thrown at 0x00007FF8627CA58B (GdiPlus.dll) in Server.exe: 0xC0000005: Access violation reading location 0x00000290B64B1F28
错误根源
核心问题是GDI+对象与GDI资源的释放顺序错误:
- 创建
Gdiplus::Bitmap对象时,它会内部引用传入的hBitmap句柄 - 代码中先调用
DeleteObject(hBitmap)释放了GDI位图资源,之后才销毁Bitmap对象并关闭GDI+,导致Bitmap对象在后续清理时访问已释放的内存,触发访问违规
修复步骤
调整资源释放顺序,严格遵循:
- 先销毁
Gdiplus::Bitmap对象,释放其对hBitmap的引用 - 再删除
hBitmap、memoryDC等GDI资源 - 最后调用
GdiplusShutdown关闭GDI+
修复后的完整代码
std::string getCurrentTimestamp() { std::time_t t = std::time(nullptr); std::tm tm; localtime_s(&tm, &t); std::ostringstream oss; oss << (tm.tm_year + 1900) << "-" << (tm.tm_mon + 1) << "-" << tm.tm_mday << "_" << tm.tm_hour << "-" << tm.tm_min << "-" << tm.tm_sec; return oss.str(); } void sendFile(SOCKET clientSocket, const std::string& filename) { std::ifstream inFile(filename, std::ios::binary | std::ios::ate); if (!inFile) { std::cerr << "Failed to open file: " << filename << std::endl; std::string errorMessage = "TYPE:text|SIZE:0\nFailed to open file."; send(clientSocket, errorMessage.c_str(), errorMessage.size(), 0); return; } // 获取文件大小 std::streamsize fileSize = inFile.tellg(); inFile.seekg(0, std::ios::beg); // 提取文件名并判断MIME类型 size_t lastSlash = filename.find_last_of("/\\"); std::string fileName = (lastSlash != std::string::npos) ? filename.substr(lastSlash + 1) : filename; std::string mimeType; if (fileName.size() >= 4 && fileName.compare(fileName.size() - 4, 4, ".png") == 0) { mimeType = "image/png"; } else if (fileName.size() >= 4 && fileName.compare(fileName.size() - 4, 4, ".mp4") == 0) { mimeType = "video/mp4"; } else { std::cerr << "Unsupported file format: " << filename << std::endl; std::string errorMessage = "TYPE:text|SIZE:0\nUnsupported file format."; send(clientSocket, errorMessage.c_str(), errorMessage.size(), 0); inFile.close(); return; } // 发送文件头部信息 std::string header = "TYPE:file|SIZE:" + std::to_string(fileSize) + "|FILENAME:" + fileName + "|MIMETYPE:" + mimeType + "\n"; if (send(clientSocket, header.c_str(), header.size(), 0) != static_cast<int>(header.size())) { std::cerr << "Failed to send header" << std::endl; inFile.close(); return; } // 分块发送文件数据 char buffer[1024]; while (inFile) { inFile.read(buffer, sizeof(buffer)); int bytesRead = inFile.gcount(); if (bytesRead > 0) { if (send(clientSocket, buffer, bytesRead, 0) != bytesRead) { std::cerr << "Failed to send file data" << std::endl; inFile.close(); return; } } } std::cout << "File sent successfully!" << std::endl; inFile.close(); } void takeScreenshotWithTimestamp(SOCKET clientSocket) { // 初始化GDI+ Gdiplus::GdiplusStartupInput gdiplusStartupInput; ULONG_PTR gdiplusToken; Gdiplus::Status status = Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); if (status != Gdiplus::Ok) { std::cerr << "Failed to initialize GDI+" << std::endl; return; } // 获取屏幕分辨率 int screenWidth = GetSystemMetrics(SM_CXSCREEN); int screenHeight = GetSystemMetrics(SM_CYSCREEN); // 创建屏幕兼容DC HDC screenDC = GetDC(NULL); if (screenDC == NULL) { std::cerr << "Failed to get screen DC" << std::endl; Gdiplus::GdiplusShutdown(gdiplusToken); return; } HDC memoryDC = CreateCompatibleDC(screenDC); if (memoryDC == NULL) { std::cerr << "Failed to create memory DC" << std::endl; ReleaseDC(NULL, screenDC); Gdiplus::GdiplusShutdown(gdiplusToken); return; } HBITMAP hBitmap = CreateCompatibleBitmap(screenDC, screenWidth, screenHeight); if (hBitmap == NULL) { std::cerr << "Failed to create compatible bitmap" << std::endl; DeleteDC(memoryDC); ReleaseDC(NULL, screenDC); Gdiplus::GdiplusShutdown(gdiplusToken); return; } SelectObject(memoryDC, hBitmap); // 捕获屏幕内容到内存DC if (!BitBlt(memoryDC, 0, 0, screenWidth, screenHeight, screenDC, 0, 0, SRCCOPY | CAPTUREBLT)) { std::cerr << "Failed to capture screen content" << std::endl; DeleteObject(hBitmap); DeleteDC(memoryDC); ReleaseDC(NULL, screenDC); Gdiplus::GdiplusShutdown(gdiplusToken); return; } // 生成带时间戳的文件名 std::string filename = "Screenshot_" + getCurrentTimestamp() + ".png"; // 新增局部作用域,确保Bitmap对象先销毁 { Gdiplus::Bitmap bitmap(hBitmap, NULL); CLSID pngClsid; HRESULT hr = CLSIDFromString(L"{557CF406-1A04-11D3-9A73-0000F81EF32E}", &pngClsid); if (FAILED(hr)) { std::cerr << "Failed to get PNG CLSID" << std::endl; DeleteObject(hBitmap); DeleteDC(memoryDC); ReleaseDC(NULL, screenDC); Gdiplus::GdiplusShutdown(gdiplusToken); return; } status = bitmap.Save(std::wstring(filename.begin(), filename.end()).c_str(), &pngClsid, NULL); if (status != Gdiplus::Ok) { std::cerr << "Failed to save screenshot as PNG" << std::endl; } else { std::cout << "Screenshot saved as " << filename << std::endl; } } // Bitmap对象在此处自动销毁,释放对hBitmap的引用 // 按顺序释放GDI资源和关闭GDI+ DeleteObject(hBitmap); DeleteDC(memoryDC); ReleaseDC(NULL, screenDC); Gdiplus::GdiplusShutdown(gdiplusToken); sendFile(clientSocket, filename); }
可选优化
- 跳过本地文件保存步骤,直接将GDI+ Bitmap转换为内存流发送,减少磁盘IO开销
- 完善
send函数的错误处理,处理部分数据发送成功的情况(循环发送剩余数据)
内容的提问来源于stack exchange,提问作者zeepaul
相关产品推荐
相关产品推荐

