我是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
并以这种方式发送,所以我想了解为什么此方法无效。谢谢。
答案 0 :(得分:2)
创建存储设备上下文和位图(HBITMAP
句柄)。选择内存DC中的位图。然后使用BitBlt
从屏幕复制到内存dc。然后,位图将包含屏幕数据。
然后可以直接在WM_PAINT
中打印位图句柄。这将是DDB,无法在程序之间传输。您需要DIB,或使用GetDIBits
将内容从位图复制到字节数组(g_bmpInfo
和g_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;
}