问题
I'm trying to make the snippingtool in c++. I managed to create a borderless, fullscreen window via this code;
WindProc:
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);
}
}
Creating the 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);
Whats left to do is now taking the screenshot of the screen and drawing it on form. Which i fail at this part.
When i googled, i first saw SetPixel function but to draw the form it took like half a minute. it was veerry slow. Then people said use Device Context (its the forms' drawing data in the memory as i understood) and draw on that, it will be much quicker then just update the window. And here's what i did;
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);
As you can guess, it didn't work. My form is blank white. I don't understand if i should write this on WM_PAINT message on WindProc or not. I tried many variations to this and actually one point it worked i guess but then stopped working when i changed something and i couldn't get it to work again...
thank you.
回答1:
Thanks for the comments, i did some more research on WM_PAINT message. And i found this golden document:
http://www.winprog.org/tutorial/bitmaps.html
My code in the original post stays the same, i only added 2 things;
1-Taking a screenshot of the screen and saving it;
(got it from here:
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- Painting via 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;
Note that i'm totally new to GDI, window stuff. I just kinda mashed pieces together i found here and there, but it works xd
thanks all for the help.
EDIT: also, just a quick info. If your display settings have some kinda scaling, screenshots are also scaled. What this means is if you have per se 125% scaling, then the screenshot will not be the actual fullscreen. To prevent this you need to have a manifest file.
https://docs.microsoft.com/en-us/windows/win32/sbscs/application-manifests
the setting we are looking for is DPI awareness. here's my manifest file;
<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>
my .rc file:
#include "winuser.h"
1 RT_MANIFEST scanner.exe.manifest
来源:https://stackoverflow.com/questions/66091437/c-winapi-taking-screenshot-and-making-it-background-of-window