简体   繁体   English

Winapi:屏幕快照未显示在窗口上

[英]Winapi: Screenshot is not displayed on window

I'm new with GUI programming on windows and came across some problems (using visual studio 2017). 我是Windows上GUI编程的新手,遇到一些问题(使用Visual Studio 2017)。

I have a client and server application, client basically takes pictures of desktop and sends it to server which then displays it on the screen. 我有一个客户端和服务器应用程序,客户端基本上是为桌面拍照,然后将其发送到服务器,然后在屏幕上显示它。 As I decided to post a question here I created one project which creates a window(which is used to display screensots) takes the screenshot and displays it (I tried to use as minimal code as possible to reproduce the problem). 当我决定在此处发布问题时,我创建了一个项目,该项目创建一个窗口(用于显示screensots)并截取屏幕截图并将其显示(我尝试使用尽可能少的代码来重现该问题)。

Here is the code: 这是代码:

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

As you can see I'm creating a window using CreateWindows function, then try to take a screenshot, then I resize it to the proper window size which in my case is 800x600 and then try to display it using InvalidateRgn function. 如您所见,我正在使用CreateWindows函数创建一个窗口,然后尝试截屏,然后将其调整为合适的窗口大小(在我的情况下为800x600 ,然后尝试使用InvalidateRgn函数显示它。 I've deleted basically all code in WndProc in sake of this question and only left WM_PAINT part. WndProc这个问题,我基本上删除了WndProc中的所有代码,只剩下WM_PAINT部分。

The problem I'm having is that the window which should be filled with the screenshot is black and nothing is displayed on it. 我遇到的问题是应该用屏幕截图填充的窗口是黑色的,并且没有任何显示。 I have no compiler or runtime errors but the screenshot is not displayed. 我没有编译器或运行时错误,但没有显示截图。 I think I'm missing some information on how to do this properly. 我想我缺少有关如何正确执行此操作的信息。 Hope you can help. 希望能对您有所帮助。

PS PS

This is the source code of some project, I don't want to convert screenshot into .bmp and send it that way, I want to understand why this method doesn't work. 这是某些项目的源代码,我不想将屏幕截图转换为.bmp并以这种方式发送,我想了解为什么此方法不起作用。 thanks. 谢谢。

Create a memory device context and bitmap ( HBITMAP handle). 创建一个内存设备上下文和位图( HBITMAP句柄)。 Select the bitmap in to memory dc. 选择内存DC中的位图。 Then use BitBlt to copy from screen to memory dc. 然后使用BitBlt从屏幕复制到内存dc。 The bitmap will then contain the screen data. 然后,位图将包含屏幕数据。

You can then print the bitmap handle directly in WM_PAINT . 然后,您可以直接在WM_PAINT打印位图句柄。 This would be a DDB, it can't be transferred between programs. 这将是DDB,无法在程序之间传输。 You need DIB, or so use GetDIBits to copy the content from bitmap in to a byte array ( g_bmpInfo and g_pixels ) 您需要DIB,或者使用GetDIBits将内容从位图复制到字节数组( g_bmpInfog_pixels

Note that the size of 24-bit bitmap is not always with * height * 3 , you need a special formula to account for padding. 请注意,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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM