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