C++ winapi 截图并制作 Window 的背景
C++ winapi Taking Screenshot and Making It Background of Window
我正在尝试用 C++ 制作截图工具。我通过这段代码设法创建了一个无边框的全屏 window;
风波:
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
{
switch (message)
{
case WM_CHAR: //this is just for a program exit besides window's borders/taskbar
if (wparam==VK_ESCAPE)
{
DestroyWindow(hwnd);
}
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, message, wparam, lparam);
}
}
正在创建 window;
WNDCLASS windowClass={0};
windowClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
windowClass.hCursor=LoadCursor(NULL, IDC_ARROW);
windowClass.hInstance=NULL;
windowClass.lpfnWndProc=WndProc;
windowClass.lpszClassName=TEXT("Window in Console"); //needs to be the same name
//when creating the window as well
windowClass.style=CS_HREDRAW | CS_VREDRAW;
//also register the class
if (!RegisterClass(&windowClass))
MessageBoxA(NULL, "Could not register class", "Error", MB_OK);
HWND windowHandle=CreateWindowA("Window in Console",
NULL,
WS_POPUP, //borderless
0, //x coordinate of window start point
0, //y start point
GetSystemMetrics(SM_CXSCREEN), //width of window
GetSystemMetrics(SM_CYSCREEN), //height of the window
NULL, //handles and such, not needed
NULL,
NULL,
NULL);
ShowWindow(windowHandle, SW_RESTORE);
现在剩下要做的就是截取屏幕截图并将其绘制在表格上。我在这部分失败了。
当我用谷歌搜索时,我首先看到了 SetPixel 函数,但绘制表格花了半分钟。它非常慢。然后人们说使用设备上下文(据我所知,它是内存中的表单绘图数据)并以此为基础进行绘制,这样会比更新 window 快得多。这就是我所做的;
int nScreenWidth = GetSystemMetrics(SM_CXSCREEN);
int nScreenHeight = GetSystemMetrics(SM_CYSCREEN);
HDC hdc = GetDC(windowHandle);
BitBlt(hdc, 0, 0, nScreenWidth, nScreenHeight, GetDC(NULL), 0, 0, SRCCOPY | CAPTUREBLT);
UpdateWindow(windowHandle);
ShowWindow(windowHandle, SW_RESTORE);
UpdateWindow(windowHandle);
如您所料,它没有用。我的表格是空白的。我不明白我是否应该在 WindProc 上的 WM_PAINT 消息上写这个。我尝试了很多变体,实际上有一点我猜它起作用了,但是当我改变了一些东西并且我无法让它再次工作时停止工作......
谢谢。
感谢您的评论,我对 WM_PAINT 消息做了更多研究。我找到了这份黄金文件:
http://www.winprog.org/tutorial/bitmaps.html
我原来的代码 post 保持不变,我只添加了 2 个东西;
1-截屏并保存;
(从这里获取:
How can I take a screenshot in a windows application?)
// get the device context of the screen
HDC hScreenDC = GetDC(NULL);
// and a device context to put it in
HDC hMemoryDC = CreateCompatibleDC(hScreenDC);
int width = GetSystemMetrics(SM_CXSCREEN);
int height = GetSystemMetrics(SM_CYSCREEN);
// hBitmap is a HBITMAP that i declared globally to use within WM_PAINT
// maybe worth checking these are positive values
hBitmap = CreateCompatibleBitmap(hScreenDC, width, height);
// get a new bitmap
HBITMAP hOldBitmap = (HBITMAP) SelectObject(hMemoryDC, hBitmap);
BitBlt(hMemoryDC, 0, 0, width, height, hScreenDC, 0, 0, SRCCOPY);
hBitmap = (HBITMAP) SelectObject(hMemoryDC, hOldBitmap);
// clean up
DeleteDC(hMemoryDC);
ReleaseDC(NULL,hScreenDC);
// now your image is held in hBitmap. You can save it or do whatever with it
2- 通过 WM_PAINT 绘画:
switch (message)
{
case WM_PAINT:{
int nScreenWidth = GetSystemMetrics(SM_CXSCREEN);
int nScreenHeight = GetSystemMetrics(SM_CYSCREEN);
BITMAP bm;
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
HDC hdcMem = CreateCompatibleDC(hdc);
HBITMAP hbmOld = (HBITMAP) SelectObject(hdcMem, hBitmap);
GetObject(hBitmap, sizeof(bm), &bm);
BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, hbmOld);
DeleteDC(hdcMem);
EndPaint(hwnd, &ps);
}
return 0;
请注意,我对 GDI、window 完全陌生。我只是把我在这里和那里找到的碎片拼凑在一起,但它有效 xd
感谢大家的帮助。
编辑:另外,只是一个快速信息。如果您的显示设置有某种缩放比例,则屏幕截图也会缩放。这意味着如果您本身有 125% 的缩放比例,那么屏幕截图将不是实际的全屏。为防止这种情况,您需要有一个清单文件。
https://docs.microsoft.com/en-us/windows/win32/sbscs/application-manifests
我们正在寻找的设置是 DPI 感知。这是我的清单文件;
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" >
<asmv3:application>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>true</dpiAware>
</asmv3:windowsSettings>
</asmv3:application>
</assembly>
我的 .rc 文件:
#include "winuser.h"
1 RT_MANIFEST scanner.exe.manifest
我正在尝试用 C++ 制作截图工具。我通过这段代码设法创建了一个无边框的全屏 window;
风波:
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
{
switch (message)
{
case WM_CHAR: //this is just for a program exit besides window's borders/taskbar
if (wparam==VK_ESCAPE)
{
DestroyWindow(hwnd);
}
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, message, wparam, lparam);
}
}
正在创建 window;
WNDCLASS windowClass={0};
windowClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
windowClass.hCursor=LoadCursor(NULL, IDC_ARROW);
windowClass.hInstance=NULL;
windowClass.lpfnWndProc=WndProc;
windowClass.lpszClassName=TEXT("Window in Console"); //needs to be the same name
//when creating the window as well
windowClass.style=CS_HREDRAW | CS_VREDRAW;
//also register the class
if (!RegisterClass(&windowClass))
MessageBoxA(NULL, "Could not register class", "Error", MB_OK);
HWND windowHandle=CreateWindowA("Window in Console",
NULL,
WS_POPUP, //borderless
0, //x coordinate of window start point
0, //y start point
GetSystemMetrics(SM_CXSCREEN), //width of window
GetSystemMetrics(SM_CYSCREEN), //height of the window
NULL, //handles and such, not needed
NULL,
NULL,
NULL);
ShowWindow(windowHandle, SW_RESTORE);
现在剩下要做的就是截取屏幕截图并将其绘制在表格上。我在这部分失败了。
当我用谷歌搜索时,我首先看到了 SetPixel 函数,但绘制表格花了半分钟。它非常慢。然后人们说使用设备上下文(据我所知,它是内存中的表单绘图数据)并以此为基础进行绘制,这样会比更新 window 快得多。这就是我所做的;
int nScreenWidth = GetSystemMetrics(SM_CXSCREEN);
int nScreenHeight = GetSystemMetrics(SM_CYSCREEN);
HDC hdc = GetDC(windowHandle);
BitBlt(hdc, 0, 0, nScreenWidth, nScreenHeight, GetDC(NULL), 0, 0, SRCCOPY | CAPTUREBLT);
UpdateWindow(windowHandle);
ShowWindow(windowHandle, SW_RESTORE);
UpdateWindow(windowHandle);
如您所料,它没有用。我的表格是空白的。我不明白我是否应该在 WindProc 上的 WM_PAINT 消息上写这个。我尝试了很多变体,实际上有一点我猜它起作用了,但是当我改变了一些东西并且我无法让它再次工作时停止工作......
谢谢。
感谢您的评论,我对 WM_PAINT 消息做了更多研究。我找到了这份黄金文件:
http://www.winprog.org/tutorial/bitmaps.html
我原来的代码 post 保持不变,我只添加了 2 个东西;
1-截屏并保存;
(从这里获取:
How can I take a screenshot in a windows application?)
// get the device context of the screen
HDC hScreenDC = GetDC(NULL);
// and a device context to put it in
HDC hMemoryDC = CreateCompatibleDC(hScreenDC);
int width = GetSystemMetrics(SM_CXSCREEN);
int height = GetSystemMetrics(SM_CYSCREEN);
// hBitmap is a HBITMAP that i declared globally to use within WM_PAINT
// maybe worth checking these are positive values
hBitmap = CreateCompatibleBitmap(hScreenDC, width, height);
// get a new bitmap
HBITMAP hOldBitmap = (HBITMAP) SelectObject(hMemoryDC, hBitmap);
BitBlt(hMemoryDC, 0, 0, width, height, hScreenDC, 0, 0, SRCCOPY);
hBitmap = (HBITMAP) SelectObject(hMemoryDC, hOldBitmap);
// clean up
DeleteDC(hMemoryDC);
ReleaseDC(NULL,hScreenDC);
// now your image is held in hBitmap. You can save it or do whatever with it
2- 通过 WM_PAINT 绘画:
switch (message)
{
case WM_PAINT:{
int nScreenWidth = GetSystemMetrics(SM_CXSCREEN);
int nScreenHeight = GetSystemMetrics(SM_CYSCREEN);
BITMAP bm;
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
HDC hdcMem = CreateCompatibleDC(hdc);
HBITMAP hbmOld = (HBITMAP) SelectObject(hdcMem, hBitmap);
GetObject(hBitmap, sizeof(bm), &bm);
BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, hbmOld);
DeleteDC(hdcMem);
EndPaint(hwnd, &ps);
}
return 0;
请注意,我对 GDI、window 完全陌生。我只是把我在这里和那里找到的碎片拼凑在一起,但它有效 xd
感谢大家的帮助。
编辑:另外,只是一个快速信息。如果您的显示设置有某种缩放比例,则屏幕截图也会缩放。这意味着如果您本身有 125% 的缩放比例,那么屏幕截图将不是实际的全屏。为防止这种情况,您需要有一个清单文件。
https://docs.microsoft.com/en-us/windows/win32/sbscs/application-manifests
我们正在寻找的设置是 DPI 感知。这是我的清单文件;
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" >
<asmv3:application>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>true</dpiAware>
</asmv3:windowsSettings>
</asmv3:application>
</assembly>
我的 .rc 文件:
#include "winuser.h"
1 RT_MANIFEST scanner.exe.manifest