【问题标题】:C++ winapi Taking Screenshot and Making It Background of WindowC++ winapi 截图并将其设为窗口背景
【发布时间】:2021-05-11 10:58:57
【问题描述】:

我正在尝试用 C++ 制作截图工具。我设法通过这段代码创建了一个无边框的全屏窗口;

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);
  }
}

创建窗口;

    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 函数,但绘制表单花了大约半分钟。它非常缓慢。然后人们说使用设备上下文(据我了解,它在内存中的表单绘图数据)并在此基础上进行绘制,它会比更新窗口快得多。这就是我所做的;

  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 消息上写这个。我对此尝试了很多变体,实际上有一点我猜它起作用了,但是当我改变一些东西时它就停止了工作,我无法让它再次工作......

谢谢。

【问题讨论】:

  • BitBlt 部分应该在一个案例 WM_PAINT 内。如果您检测到屏幕截图的按键,您还需要调用 InvalidateRect。
  • 一般情况下,你应该画WM_PAINT,使用BeginPaint返回的DC而不是GetDC
  • 伙计们非常感谢你们的cmets。它为我指明了正确的方向。我找到了解决方案,我会在早上发布。(几个小时后)。谢谢

标签: c++ windows winapi gdi


【解决方案1】:

感谢 cmets,我对 WM_PAINT 消息做了更多研究。我发现了这份黄金文件:

http://www.winprog.org/tutorial/bitmaps.html

我在原帖中的代码保持不变,我只添加了 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,窗口的东西完全陌生。我只是把我在这里和那里找到的碎片拼凑在一起,但它有效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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多