C++ winapi Taking Screenshot and Making It Background of Window

你说的曾经没有我的故事 提交于 2021-02-11 12:42:34

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!