这是我的程序截取的屏幕截图:我尝试制作一个 C++ 程序来截取屏幕截图并将其保存为 png。一切正常,只是它只截取了屏幕左上角的屏幕截图。
这是我的程序截取的屏幕截图:
我尝试编写一个 C++ 程序,用于截取屏幕截图并将其保存为 png。除了只截取屏幕左上角的屏幕截图外,其他一切都正常。
问题是我的应用程序拍摄了我的桌面左上角的照片。
我如何截取整个屏幕的屏幕截图?我需要对代码进行哪些更改才能实现我的目标?
这是我的代码:
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <objidl.h>
#include <stdio.h>
#include <gdiplus.h>
using namespace Gdiplus;
#pragma comment (lib,"Gdiplus.lib")
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid) {
UINT num = 0;
UINT size = 0;
Gdiplus::ImageCodecInfo* pImageCodecInfo = NULL;
Gdiplus::GetImageEncodersSize(&num, &size);
if (size == 0) {
return -1;
}
pImageCodecInfo = (Gdiplus::ImageCodecInfo*)(malloc(size));
if (pImageCodecInfo == NULL) {
return -1;
}
GetImageEncoders(num, size, pImageCodecInfo);
for (UINT j = 0; j < num; ++j) {
if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0) {
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j;
}
}
free(pImageCodecInfo);
return -1;
}
void TakeScreenshot(const wchar_t* file_name) {
// Get the dimensions of the whole desktop
int width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
int height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
// Create a bitmap to hold the screenshot
HDC screen_dc = GetDC(NULL);
HDC mem_dc = CreateCompatibleDC(screen_dc);
HBITMAP bitmap = CreateCompatibleBitmap(screen_dc, width, height);
HGDIOBJ old_bitmap = SelectObject(mem_dc, bitmap);
// Copy the screen contents to the bitmap
BitBlt(mem_dc, 0, 0, width, height, screen_dc, 0, 0, SRCCOPY);
// Save the bitmap to a file
Gdiplus::Bitmap image(bitmap, NULL);
CLSID png_clsid;
GetEncoderClsid(L"image/png", &png_clsid);
image.Save((WCHAR*)file_name, &png_clsid, NULL);
// Clean up
SelectObject(mem_dc, old_bitmap);
DeleteObject(bitmap);
DeleteDC(mem_dc);
ReleaseDC(NULL, screen_dc);
}
int main()
{
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
TakeScreenshot(L"test.png");
GdiplusShutdown(gdiplusToken);
}
更新的代码:我尝试使用, GetDpiForSystem()
因为我的应用程序应该支持 DPI。但结果仍然相同。
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <objidl.h>
#include <stdio.h>
#include <gdiplus.h>
using namespace Gdiplus;
#pragma comment (lib,"Gdiplus.lib")
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
UINT num = 0;
UINT size = 0;
Gdiplus::ImageCodecInfo* pImageCodecInfo = NULL;
Gdiplus::GetImageEncodersSize(&num, &size);
if (size == 0) {
printf("Error: GetImageEncodersSize returned size 0\n");
return -1;
}
pImageCodecInfo = (Gdiplus::ImageCodecInfo*)(malloc(size));
if (pImageCodecInfo == NULL) {
printf("Error: malloc failed to allocate memory for ImageCodecInfo\n");
return -1;
}
if (GetImageEncoders(num, size, pImageCodecInfo) != Ok) {
printf("Error: GetImageEncoders failed\n");
free(pImageCodecInfo);
return -1;
}
for (UINT j = 0; j < num; ++j) {
if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0) {
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j;
}
}
free(pImageCodecInfo);
return -1;
}
void TakeScreenshot(const wchar_t* file_name)
{
// Get the dimensions of the whole desktop
int dpi_x = GetDpiForSystem();
int dpi_y = GetDpiForSystem();
int width = GetSystemMetricsForDpi(SM_CXSCREEN, dpi_x);
int height = GetSystemMetricsForDpi(SM_CYSCREEN, dpi_y);
if (width == 0 || height == 0) {
printf("Error: GetSystemMetrics returned invalid screen dimensions\n");
return;
}
printf("Width: %d\n", width);
printf("Height: %d\n", height);
// Create a bitmap to hold the screenshot
HDC screen_dc = GetDC(NULL);
if (screen_dc == NULL) {
printf("Error: GetDC failed to get a handle to the screen device context\n");
return;
}
HDC mem_dc = CreateCompatibleDC(screen_dc);
if (mem_dc == NULL) {
printf("Error: CreateCompatibleDC failed to create a compatible device context\n");
ReleaseDC(NULL, screen_dc);
return;
}
// Create a bitmap that is scaled to the appropriate DPI
HBITMAP bitmap = CreateBitmap(width, height, 1, GetDeviceCaps(screen_dc, BITSPIXEL), NULL);
if (bitmap == NULL) {
printf("Error: CreateCompatibleBitmap failed to create a compatible bitmap\n");
DeleteDC(mem_dc);
ReleaseDC(NULL, screen_dc);
return;
}
HGDIOBJ old_bitmap = SelectObject(mem_dc, bitmap);
// Set the DPI of the memory DC to match the system DPI
SetGraphicsMode(mem_dc, GM_ADVANCED);
XFORM xform;
xform.eM11 = (FLOAT)dpi_x / 96;
xform.eM12 = xform.eM21 = xform.eM22 = 0;
xform.eDx = xform.eDy = 0;
SetWorldTransform(mem_dc, &xform);
// Copy the screen contents to the bitmap
if (BitBlt(mem_dc, 0, 0, width, height, screen_dc, 0, 0, SRCCOPY) == 0) {
printf("Error:BitBlt failed to copy screen contents to bitmap\n");
return;
}
// Save the bitmap to a file
Gdiplus::Bitmap image(bitmap, NULL);
if (image.GetLastStatus() != Ok) {
printf("Error: Bitmap constructor failed to create a Bitmap object\n");
return;
}
CLSID png_clsid;
int r = GetEncoderClsid(L"image/png", &png_clsid);
if (r == -1)
{
printf("Error: unable to find image encoder for MIME type 'image/png'\n");
return;
}
if (image.Save(file_name, &png_clsid, NULL) != Ok) {
printf("Error: Bitmap::Save failed to save image\n");
return;
}
// Clean up
SelectObject(mem_dc, old_bitmap);
DeleteObject(bitmap);
DeleteDC(mem_dc);
ReleaseDC(NULL, screen_dc);
}
int main()
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
TakeScreenshot(L"test.png");
GdiplusShutdown(gdiplusToken);
return 0;
}
代码必须具备 DPI 感知能力,因为显示器的 DPI 可能因系统而异。例如,一个用户可能拥有 DPI 为 192 的高分辨率显示器,而另一个用户可能拥有 DPI 为 96 的低分辨率显示器。
我在调用之前添加了此代码 TakeScreenshot()
: DPI_AWARENESS_CONTEXT dpi_awareness_context = SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_SYSTEM_AWARE);
.
在 TakeScreenshot()
我添加之后: SetThreadDpiAwarenessContext(dpi_awareness_context);
.
正如@AndreasWenzel 在评论中所说,这确保我的应用程序具有 DPI 感知能力。
这段代码运行完美:
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <objidl.h>
#include <stdio.h>
#include <gdiplus.h>
using namespace Gdiplus;
#pragma comment (lib,"Gdiplus.lib")
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
UINT num = 0;
UINT size = 0;
Gdiplus::ImageCodecInfo* pImageCodecInfo = NULL;
Gdiplus::GetImageEncodersSize(&num, &size);
if (size == 0) {
printf("Error: GetImageEncodersSize returned size 0\n");
return -1;
}
pImageCodecInfo = (Gdiplus::ImageCodecInfo*)(malloc(size));
if (pImageCodecInfo == NULL) {
printf("Error: malloc failed to allocate memory for ImageCodecInfo\n");
return -1;
}
if (GetImageEncoders(num, size, pImageCodecInfo) != Ok) {
printf("Error: GetImageEncoders failed\n");
free(pImageCodecInfo);
return -1;
}
for (UINT j = 0; j < num; ++j) {
if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0) {
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j;
}
}
free(pImageCodecInfo);
return -1;
}
void TakeScreenshot(const wchar_t* file_name)
{
// Get the dimensions of the whole desktop
int dpi_x = GetDpiForSystem();
int dpi_y = GetDpiForSystem();
int width = GetSystemMetricsForDpi(SM_CXSCREEN, dpi_x);
int height = GetSystemMetricsForDpi(SM_CYSCREEN, dpi_y);
if (width == 0 || height == 0) {
printf("Error: GetSystemMetrics returned invalid screen dimensions\n");
return;
}
printf("Width: %d\n", width);
printf("Height: %d\n", height);
// Create a bitmap to hold the screenshot
HDC screen_dc = GetDC(NULL);
if (screen_dc == NULL) {
printf("Error: GetDC failed to get a handle to the screen device context\n");
return;
}
HDC mem_dc = CreateCompatibleDC(screen_dc);
if (mem_dc == NULL) {
printf("Error: CreateCompatibleDC failed to create a compatible device context\n");
ReleaseDC(NULL, screen_dc);
return;
}
// Create a bitmap that is scaled to the appropriate DPI
HBITMAP bitmap = CreateBitmap(width, height, 1, GetDeviceCaps(screen_dc, BITSPIXEL), NULL);
if (bitmap == NULL) {
printf("Error: CreateCompatibleBitmap failed to create a compatible bitmap\n");
DeleteDC(mem_dc);
ReleaseDC(NULL, screen_dc);
return;
}
HGDIOBJ old_bitmap = SelectObject(mem_dc, bitmap);
// Set the DPI of the memory DC to match the system DPI
SetGraphicsMode(mem_dc, GM_ADVANCED);
XFORM xform;
xform.eM11 = (FLOAT)dpi_x / 96;
xform.eM12 = xform.eM21 = xform.eM22 = 0;
xform.eDx = xform.eDy = 0;
SetWorldTransform(mem_dc, &xform);
// Copy the screen contents to the bitmap
if (BitBlt(mem_dc, 0, 0, width, height, screen_dc, 0, 0, SRCCOPY) == 0) {
printf("Error:BitBlt failed to copy screen contents to bitmap\n");
return;
}
// Save the bitmap to a file
Gdiplus::Bitmap image(bitmap, NULL);
if (image.GetLastStatus() != Ok) {
printf("Error: Bitmap constructor failed to create a Bitmap object\n");
return;
}
CLSID png_clsid;
int r = GetEncoderClsid(L"image/png", &png_clsid);
if (r == -1)
{
printf("Error: unable to find image encoder for MIME type 'image/png'\n");
return;
}
if (image.Save(file_name, &png_clsid, NULL) != Ok) {
printf("Error: Bitmap::Save failed to save image\n");
return;
}
// Clean up
SelectObject(mem_dc, old_bitmap);
DeleteObject(bitmap);
DeleteDC(mem_dc);
ReleaseDC(NULL, screen_dc);
}
int main()
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
DPI_AWARENESS_CONTEXT dpi_awareness_context = SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_SYSTEM_AWARE);
TakeScreenshot(L"test.png");
SetThreadDpiAwarenessContext(dpi_awareness_context);
GdiplusShutdown(gdiplusToken);
return 0;
}