我做了一个程序,采取屏幕截图,并保存在本地文件夹(这工作)。我删除了这部分,并希望在新创建的窗口中显示新拍摄的屏幕截图。一切都很好,截图被采取,窗口被创建,但唯一的问题是,窗口不显示采取的截图。
如果您想在Visual Studio中运行代码,则必须将Properties -> Linker -> System -> SubSystem (in Visual Studio)
从Windows (/SUBSYSTEM:WINDOWS)
更改为Console (/SUBSYSTEM:CONSOLE)
如何在新创建的窗口中显示新拍摄的屏幕截图?
这就是代码:
#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()
{
// 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;
}
// 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;
}
HWND hwnd;
WNDCLASSEX wc;
HINSTANCE hInstance = GetModuleHandle(NULL);
// Define window class
ZeroMemory(&wc, sizeof(WNDCLASSEX));
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = DefWindowProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpszClassName = L"ScreenshotWindowClass";
RegisterClassEx(&wc);
// Create window
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
L"ScreenshotWindowClass",
L"Screenshot",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, width, height,
NULL, NULL, hInstance, NULL);
if (hwnd == NULL) {
printf("Error: CreateWindowEx failed to create window\n");
return;
}
// Show screenshot in window
HDC hdc = GetDC(hwnd);
Graphics graphics(hdc);
graphics.DrawImage(&image, 0, 0, width, height);
ReleaseDC(hwnd, hdc);
// Display window
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
// Wait for user to close window
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// Clean up
UnregisterClass(L"ScreenshotWindowClass", hInstance);
// 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();
SetThreadDpiAwarenessContext(dpi_awareness_context);
GdiplusShutdown(gdiplusToken);
return 0;
}
2条答案
按热度按时间dauxcl2d1#
这里的核心问题是试图“从外部”呈现到窗口上。如果没有目标窗口的配合,显示窗口内容是很困难的。你应该做的是让窗口自己处理显示。
注册窗口类时,分配给
lpfnWndProc
的函数指针实现了窗口的行为。在这种简单的情况下,只有两条消息需要显式处理:WM_PAINT
(负责渲染客户端区域)和WM_DESTROY
。后者是必需的,这样我们就可以通过发布WM_QUIT
线程消息来打破以下消息循环。下面的实现也处理
WM_CREATE
,以便将位图句柄传递到CreateWindowEx
中,并将其存储在每个窗口的内存中,以方便访问。ScreenshotWndProc
:*由于该设计依赖于保持对屏幕截图位图的引用的中间窗口,因此重要的是该位图不再被选择到任何设备上下文中,并且也比窗口更长。这需要重新安排部分资源管理代码,所以这里是完整的示例代码供参考:
顺便说一句:传入
CreateWindowEx
的nWidth
和nHeight
参数指定 window 大小。代码表明,您实际需要的是一个具有所需 client 大小的窗口。您可以调用AdjustWindowRectEx
来计算与给定客户端矩形对应的窗口矩形。n6lpvg4x2#
根据When to Draw in a Window,在WM_PAINT之外进行绘制是可行的。
如果应用程序在任何其他时间进行绘制,例如从WinMain内或在处理键盘或鼠标消息期间,它将调用GetDC或GetDCEx函数来检索显示DC。
正如@IgorTandetnik昨天解释的那样,可能是由于绘画发生在
ShowWindow
之前,它被删除是因为ShowWindow
绘制了默认的白色背景。下面的代码适合我。