Winapi:屏幕截图未显示在窗口上

时间:2018-10-21 14:23:43

标签: c++ user-interface winapi window

我是Windows上的GUI编程的新手,遇到了一些问题(使用Visual Studio 2017)。

我有一个客户端和服务器应用程序,客户端基本上是为桌面拍照,然后将其发送到服务器,然后在屏幕上显示。当我决定在此处发布问题时,我创建了一个项目,该项目创建一个窗口(用于显示screensots)并截取屏幕截图并显示它(我尝试使用尽可能少的代码来重现问题)。

代码如下:

// Onefile.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#define SECURITY_WIN32
#include <Windowsx.h>
#include <WinSock.h>
#include <Windows.h>
#include <WinBase.h>
#include <Stdio.h>
#include <Security.h>
#include <Sddl.h>
#include <Shlwapi.h>
#include <Shlobj.h>
#include <TlHelp32.h>
#include <Psapi.h>
#include <Wininet.h>
#include <Urlmon.h>

#pragma comment(lib,"WS2_32")

static BITMAPINFO g_bmpInfo;
static BYTE      *g_pixels = NULL;
HWND hWndClient;
HDC hDcBmpServer;
static const TCHAR *className = TEXT("ControlWindow");
static const TCHAR *titlePattern = TEXT("Desktop");

static LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  switch (msg)
  {
  case WM_CREATE:
  {
    printf("WndProc: WM_CREATE\n");
    fflush(stdout);
    break;
  }
  case WM_SYSCOMMAND:
  {
    printf("WndProc: WM_SYSCOMMAND\n");
    fflush(stdout);
    return DefWindowProc(hWnd, msg, wParam, lParam);
  }
  case WM_PAINT:
  {
    printf("WndProc: WM_PAINT\n");
    fflush(stdout);

    PAINTSTRUCT ps;
    HDC         hDc = BeginPaint(hWnd, &ps);

    if (hDc == NULL)
    {
      printf("WM_PAINT: BeginPaint FAILED!\n");
      fflush(stdout);
    }

    RECT clientRect;
    if (GetClientRect(hWnd, &clientRect) == 0)
    {
      printf("WM_PAINT: GetClientRect FAILED %d\n", GetLastError());
      fflush(stdout);
    }

    RECT rect;
    HBRUSH hBrush = CreateSolidBrush(RGB(0, 0, 0));

    if (hBrush == NULL)
    {
      printf("%d WM_PAINT: CreateSolidBrush FAILED %d\n", GetLastError());
      fflush(stdout);
    }

    rect.left = 0;
    rect.top = 0;
    rect.right = clientRect.right;
    rect.bottom = clientRect.bottom;

    rect.left = g_bmpInfo.bmiHeader.biWidth;


    if (FillRect(hDc, &rect, hBrush) == 0)
    {
      printf("WM_PAINT: FillRect 1.0 failed!\n");
      fflush(stdout);
    }

    rect.left = 0;
    rect.top = g_bmpInfo.bmiHeader.biHeight;


    if (FillRect(hDc, &rect, hBrush) == 0)
    {
      printf("WM_PAINT: FillRect 2.0 failed!\n");
      fflush(stdout);
    }
    DeleteObject(hBrush);

    if (BitBlt(hDc, 0, 0, g_bmpInfo.bmiHeader.biWidth, g_bmpInfo.bmiHeader.biHeight, hDcBmpServer, 0, 0, SRCCOPY) == 0)
    {
      printf("WM_PAINT: BitBlt failed!%d\n", GetLastError());
      fflush(stdout);
    }
    else
    {
      printf("WM_PAINT: BitBl SUCCESS!\n");
      fflush(stdout);
    }
    EndPaint(hWnd, &ps);
    break;
  }
  case WM_DESTROY:
  {
    PostQuitMessage(0);
    break;
  }
  case WM_ERASEBKGND:
    return TRUE;
  case WM_LBUTTONDOWN:
  case WM_LBUTTONUP:
  case WM_RBUTTONDOWN:
  case WM_RBUTTONUP:
  case WM_MBUTTONDOWN:
  case WM_MBUTTONUP:
  case WM_LBUTTONDBLCLK:
  case WM_RBUTTONDBLCLK:
  case WM_MBUTTONDBLCLK:
  case WM_MOUSEMOVE:
  case WM_MOUSEWHEEL:
  {
    printf("WndProc: Buttons\n");
    fflush(stdout);
    break;
  }
  case WM_CHAR:
  {
    printf("WndProc: WM_char\n");
    fflush(stdout);
    break;
  }
  case WM_KEYDOWN:
  case WM_KEYUP:
  {
    printf("WndProc: KEYUPm\n");
    fflush(stdout);
    switch (wParam)
    {
    case VK_UP:
    case VK_DOWN:
    case VK_RIGHT:
    case VK_LEFT:
    case VK_HOME:
    case VK_END:
    case VK_PRIOR:
    case VK_NEXT:
    case VK_INSERT:
    case VK_RETURN:
    case VK_DELETE:
    case VK_BACK:
      break;
    }
  }
  case WM_GETMINMAXINFO:
  {
    printf("WndProc: WM_GETMINMAXINFO\n");
    fflush(stdout);
    break;
  }
  default:
    return DefWindowProc(hWnd, msg, wParam, lParam);
  }
return 0;
}

//Register class
BOOL CW_Register(WNDPROC lpfnWndProc)
{
  WNDCLASSEX wndClass;
  wndClass.cbSize = sizeof(WNDCLASSEX);
  wndClass.style = CS_DBLCLKS;
  wndClass.lpfnWndProc = lpfnWndProc;
  wndClass.cbClsExtra = 0;
  wndClass.cbWndExtra = 0;
  wndClass.hInstance = NULL;
  wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
  wndClass.hbrBackground = (HBRUSH)COLOR_WINDOW;
  wndClass.lpszMenuName = NULL;
  wndClass.lpszClassName = className;
  wndClass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
  return RegisterClassEx(&wndClass);
}

//Create window which should display pictures
HWND CW_Create()
{
  printf("CW_Create: Creating Windows...\n");
  fflush(stdout);

  hWndClient = CreateWindow(className,
    titlePattern,
    WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SIZEBOX | WS_SYSMENU,
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    800,
    600,
    NULL,
    NULL,
    GetModuleHandle(NULL),
    NULL);

  if (hWndClient == NULL)
  {
    printf("CW_Create:  ERROR! CreateWindow failed %d\n", GetLastError());
    fflush(stdout);
    return NULL;
  }

  if (ShowWindow(hWndClient, SW_SHOW) == 0)
  {
    printf("CW_Create: The window was previously hidden\n");
    fflush(stdout);
    return NULL;
  }
  else
  {
    printf("CW_Create: The window was previously shown\n");
    fflush(stdout);
  }
  printf("CW_Create: Exiting...\n");
  return hWndClient;
}


void CreateWindows()
{
  CW_Register(WndProc);
  CW_Create();
}

int main()
{
  CreateWindows();

  memset(&g_bmpInfo, 0, sizeof(g_bmpInfo));

  g_bmpInfo.bmiHeader.biSize = sizeof(g_bmpInfo.bmiHeader);
  g_bmpInfo.bmiHeader.biPlanes = 1;
  g_bmpInfo.bmiHeader.biBitCount = 24;
  g_bmpInfo.bmiHeader.biCompression = BI_RGB;
  g_bmpInfo.bmiHeader.biClrUsed = 0;
  g_bmpInfo.bmiHeader.biSizeImage = 800 * 3 * 600;

  //Client side which takes a picture of screen
  RECT rect;
  HWND hWndDesktop = GetDesktopWindow();
  GetWindowRect(hWndDesktop, &rect);

  HDC     hDc = GetDC(NULL);

  if(hDc == NULL)
  {
    printf("Client: hDc is NULL %d\n", GetLastError());
  }
  HDC     hDcScreen = CreateCompatibleDC(hDc);

  if (hDcScreen == NULL)
  {
    printf("Client: hDcScreen is NULL %d\n", GetLastError());
  }

  HBITMAP hBmpScreen = CreateCompatibleBitmap(hDc, rect.right - rect.left, rect.bottom - rect.top);

  if (hBmpScreen == NULL)
  {
    printf("Client: hBmpScreen is NULL %d\n", GetLastError());
  }

  //Resize the picture to 800x600 dimension
  HBITMAP hBmpScreenResized = CreateCompatibleBitmap(hDc, 800, 600);

  if (hBmpScreenResized == NULL)
  {
    printf("Client: hBmpScreenResized is NULL %d\n", GetLastError());
  }
  HDC     hDcScreenResized = CreateCompatibleDC(hDc);

  if (hDcScreenResized == NULL)
  {
    printf("Client: hDcScreenResized is NULL %d\n", GetLastError());
  }

  SelectObject(hDcScreenResized, hBmpScreenResized);

  SetStretchBltMode(hDcScreenResized, HALFTONE);
  if (StretchBlt(hDcScreenResized, 0, 0, 800, 600,
    hDcScreen, 0, 0, rect.right, rect.bottom, SRCCOPY) == 0)
  {
    printf("Client: StretchBlt is NULL %d\n", GetLastError());
  }

  DeleteObject(hBmpScreen);
  DeleteDC(hDcScreen);

  //Assign new values
  hBmpScreen = hBmpScreenResized;
  hDcScreen = hDcScreenResized;

  SelectObject(hDcScreen, hBmpScreen);

  free((HLOCAL)g_pixels);

  g_pixels = (BYTE *)malloc(g_bmpInfo.bmiHeader.biSizeImage);

  g_bmpInfo.bmiHeader.biWidth = 800;
  g_bmpInfo.bmiHeader.biHeight = 600;

  if (GetDIBits(hDcScreen, hBmpScreen, 0, 600, g_pixels, &g_bmpInfo, DIB_RGB_COLORS) == 0)
  {
    printf("Client: GetDIBits is NULL %d\n", GetLastError());
  }

  DeleteObject(hBmpScreen);
  ReleaseDC(NULL, hDc);
  DeleteDC(hDcScreen);


  //Server side which should take the pixels and paint them on the window
  HDC hDcServer = GetDC(NULL);
  if (hDcServer == NULL)
  {
    printf("Server: hDcServer is NULL %d\n", GetLastError());
  }

  hDcBmpServer = CreateCompatibleDC(hDcServer);
  HBITMAP hBmp;

  hBmp = CreateCompatibleBitmap(hDcServer, g_bmpInfo.bmiHeader.biWidth, g_bmpInfo.bmiHeader.biHeight);
  if (hBmp == NULL)
  {
    printf("Server: hBmp is NULL %d\n", GetLastError());
  }

  SelectObject(hDcBmpServer, hBmp);

  int ScanLines = SetDIBits(hDcBmpServer,
    hBmp,
    0,
    g_bmpInfo.bmiHeader.biHeight,
    g_pixels,
    &g_bmpInfo,
    DIB_RGB_COLORS);

  if (ScanLines == 0)
  {
    printf("Server: hBmp is NULL %d\n", GetLastError());
  }
  else
  {
    printf("Server: Scanned lines %d\n", ScanLines);
  }

  fflush(stdout);

  InvalidateRgn(hWndClient, NULL, TRUE);

  MSG msg;
  while (GetMessage(&msg, NULL, 0, 0) > 0)
  {
    PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
}

如您所见,我正在使用CreateWindows函数创建一个窗口,然后尝试截屏,然后将其调整为合适的窗口大小 在我的情况下是800x600,然后尝试使用InvalidateRgn函数显示它。出于这个问题,我基本上已经删除了WndProc中的所有代码,只剩下了WM_PAINT部分。

我遇到的问题是应该用屏幕截图填充的窗口是黑色的,并且没有任何显示。我没有编译器或运行时错误,但没有显示截图。我想我缺少有关如何正确执行此操作的信息。希望您能提供帮助。

PS

这是某些项目的源代码,我不想将屏幕截图转换为.bmp并以这种方式发送,所以我想了解为什么此方法无效。谢谢。

1 个答案:

答案 0 :(得分:2)

创建存储设备上下文和位图(HBITMAP句柄)。选择内存DC中的位图。然后使用BitBlt从屏幕复制到内存dc。然后,位图将包含屏幕数据。

然后可以直接在WM_PAINT中打印位图句柄。这将是DDB,无法在程序之间传输。您需要DIB,或使用GetDIBits将内容从位图复制到字节数组(g_bmpInfog_pixels

请注意,24位位图的大小并不总是with * height * 3,因此需要特殊的公式来说明填充。

BITMAPINFO g_bmpInfo;
BYTE      *g_pixels = NULL;

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch(msg)
    {
    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        auto hdc = BeginPaint(hWnd, &ps);

        RECT rc;
        GetClientRect(hWnd, &rc);

        int w = g_bmpInfo.bmiHeader.biWidth;
        int h = g_bmpInfo.bmiHeader.biHeight;
        if(g_pixels && w && h)
        {
            //print the bitmap on screen
            SetDIBitsToDevice(hdc, 0, 0, w, h, 0, 0, 0, h, g_pixels,
                 &g_bmpInfo, DIB_RGB_COLORS);
        }

        EndPaint(hWnd, &ps);
        break;
    }
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    }
    return DefWindowProc(hWnd, msg, wParam, lParam);
}

int main()
{
    CreateWindows();

    RECT rect;
    HWND hWndDesktop = GetDesktopWindow();
    GetWindowRect(hWndDesktop, &rect);
    int width = rect.right - rect.left;
    int height = rect.bottom - rect.top;

    int bpp = 24; //24-bit
    int size = ((width * bpp + 31) / 32) * 4 * height;

    memset(&g_bmpInfo, 0, sizeof(g_bmpInfo));
    g_bmpInfo.bmiHeader.biSize = sizeof(g_bmpInfo.bmiHeader);
    g_bmpInfo.bmiHeader.biWidth = width;
    g_bmpInfo.bmiHeader.biHeight = height;
    g_bmpInfo.bmiHeader.biPlanes = 1;
    g_bmpInfo.bmiHeader.biBitCount = 24;
    g_bmpInfo.bmiHeader.biCompression = BI_RGB;
    g_bmpInfo.bmiHeader.biSizeImage = size;

    g_pixels = new BYTE[size];

    auto hdc = GetDC(HWND_DESKTOP);
    auto memdc = CreateCompatibleDC(hdc);
    auto hbitmap = CreateCompatibleBitmap(hdc, width, height);
    auto oldbmp = SelectObject(memdc, hbitmap);

    //copy from screen to memory    
    BitBlt(memdc, 0, 0, width, height, hdc, 0, 0, SRCCOPY);

    //fill g_pixels array
    GetDIBits(hdc, hbitmap, 0, height, g_pixels, &g_bmpInfo, DIB_RGB_COLORS);

    MSG msg;
    while(GetMessage(&msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    //cleanup    
    SelectObject(memdc, oldbmp);
    DeleteObject(hbitmap);
    DeleteDC(memdc);
    ReleaseDC(HWND_DESKTOP, hdc);

    delete[] g_pixels;

    return 0;
}