Splash Screen Make Wait box move - c++

I have the following code that is supposed to paint a small box located on the bottom-left of a window, move 50 pixels to the right every 200 milliseconds, then reappear on the left once it reaches the right side.
Why doesn't my little rectangle move? It is painted in the same location all the time.
case WM_PAINT:
if (hBitmap != NULL)
{
// Paint the bitmap.
PAINTSTRUCT ps;
HDC hdc;
HDC hdcMem;
HGDIOBJ oldBitmap;
//
hdc = BeginPaint(hwnd, &ps);
// Create a dc in memory to paint on.
hdcMem = CreateCompatibleDC(hdc);
// Select the bitmap.
oldBitmap = SelectObject(hdcMem, hBitmap);
// Copy bitmap to splash screen window.
BitBlt(hdc, 0, 0, bmWidth, bmHeight, hdcMem, 0, 0, SRCCOPY);
// Fill rectangle.
HBRUSH hbr = CreateSolidBrush(RGB(42, 59, 87));
SelectObject(hdc, hbr);
FillRect(hdc, &rc, hbr);
// Cleanup.
SelectObject(hdcMem, oldBitmap);
DeleteObject(hbr);
DeleteObject(oldBitmap);
DeleteDC(hdcMem);
EndPaint(hwnd, &ps);
}
break;
case WM_TIMER:
timeCount++;
addLeft += 50;
if (addLeft == 300)
{
addLeft = 0;
}
// Move rectangle.
rc.left += addLeft;
rc.right += addLeft;
// Refresh the window.
UpdateWindow(hwnd);
// Timer and RECT from the top of the code page, and WinMain:
UINT_PTR ptrTimer;
const int TIMER_INTERVAL = 200;
const int MAX_TIME_COUNT = 100;
int timeCount;
// the timer works, but here's the code anyway.
ptrTimer = SetTimer(hwnd, 1, TIMER_INTERVAL, (TIMERPROC)NULL);
RECT rc;
rc.left = 141;
rc.top = 232;
rc.right = rc.left + 15;
rc.bottom = rc.top + 15;
Thanks for any replies,
Matt
Edit: Thanks hf.enigma, for the reply. This is what I ended up doing before I read your post. This works if anyone else wants to do this, but there are a couple more handles and GDI objects to clear. I am new to C++, so if anyone sees a memory leak here, please let me know. Thanks.
case WM_PAINT:
if (hBitmap != NULL)
{
// Paint the bitmap.
PAINTSTRUCT ps;
HDC hdc;
HDC hdcMem;
HGDIOBJ oldBitmap;
//
hdc = BeginPaint(hwnd, &ps);
hdcMem = CreateCompatibleDC(hdc); // a device context (dc) in memory to paint on.
oldBitmap = SelectObject(hdcMem, hBitmap);
// Copy bitmap to splash screen window.
BitBlt(hdc, 0, 0, bmWidth, bmHeight, hdcMem, 0, 0, SRCCOPY);
EndPaint(hwnd, &ps);
// Cleanup.
SelectObject(hdcMem, oldBitmap);
DeleteObject(oldBitmap);
DeleteDC(hdcMem);
HBRUSH hbr ;
// Fill rectangle.
RECT f;
GetClientRect(hwndBox, &f);
hdc = BeginPaint(hwndBox, &ps);
hdcMem = CreateCompatibleDC(hdc);
hbr = CreateSolidBrush(RGB(42, 59, 87));
SelectObject(hdc, hbr);
FillRect(hdc, &f, hbr);
EndPaint(hwnd, &ps);
// Cleanup.
SelectObject(hdcMem, oldBitmap);
DeleteObject(oldBitmap);
DeleteDC(hdcMem);
DeleteObject(hbr);
}
case WM_TIMER:
timeCount++;
if (addLeft == 60)
{
addLeft = 10000;
ival = 2;
}
// 'Hide' the box for 2/3 of timer interval when
// it reaches the right side.
if (addLeft == 10000)
{
addLeft = 0;
ival = 1;
}
//
switch (ival)
{
case 2:
addLeft += 12;
break;
case 3:
ival = 0;
break;
}
ival++;
if (ival == 2)
{
// Move rectangle.
MoveWindow(hwndBox, rcleft + addLeft, rctop, 12, 12, true);
}
if (timeCount == MAX_TIME_COUNT)
{
DestroyWindow(hwnd);
}
break;

You have to copy the bitmap after you have finished the painting, and you should paint with hdcMem, like this:
...
// Fill rectangle.
HBRUSH hbr = CreateSolidBrush(RGB(42, 59, 87));
SelectObject(hdcMem, hbr);
FillRect(hdcMem, &rc, hbr);
// Copy bitmap to splash screen window.
BitBlt(hdc, 0, 0, bmWidth, bmHeight, hdcMem, 0, 0, SRCCOPY);
// Cleanup.
....

This can be painted like hf.enigma has stated, but FillRect() must be called twice for each paint event. Without 'erasing' the previous block, the window ended up having a long rectangle along the bottom, similar to a progress bar.
I ended up making a sub-window the size of the block I wanted, and calling the MoveWindow() function. For me, this is the simplest and most maintainable way to accomplish this without having to use MFC or CLI.
If this can help anyone else, Here is all of the code that I am using for this.
//This displays a splash screen, and starts
//another application that takes a long time
//to load.
#pragma once
#include "resource.h"
#include "PathStatus.h"
#include "StartApp.h"
#include "CenterWindow.h"
#include <string>
#include <stdlib.h>
#include <iostream>
using namespace std;
const LPWSTR CLASS_NAME(L"TMS_Logs");
const LPWSTR PATH_SUFFIX (L"\\bin\\tl.exe");
const int LEN_APP_NAME = 12; // length of "TMS_Logs.exe".
bool b;
int ival;
char *appPath;
HWND hwnd; // Main window.
HBITMAP hBitmap = NULL; // Bitmap painted in window.
HWND hwndBox; // Moving box shows app. is loading.
HWND hwndButton; // Close box.
int bmWidth;
int bmHeight;
UINT_PTR ptrTimer;
const int TIMER_INTERVAL = 50;
const int MAX_TIME_COUNT = 650;
int timeCount;
int addLeft;
int rcleft, rctop, rcright, rcbottom;
RECT rcMover;
RECT rcMover2;
bool exitApp = false;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
rcleft = 157;
rctop = 220;
rcright = rcleft + 15;
rcbottom = rctop + 15;
//
rcMover.left = rcleft;
rcMover.bottom = rcbottom;
rcMover.top = rctop;
rcMover.right = rcright;
//
rcMover2.bottom = rcbottom;
rcMover2.top = rctop;
rcMover.left + rcleft + 50;
rcMover.right = rcleft + 50 + 15;
// Get the commandline.
const wchar_t *args = GetCommandLineW();
wstring s(args);
// With no external arguments, GetCommandLineW() returns the
// full path with quotes around it, plus one space after the
// end qoute, e.g., '"c:\fullpath.exe" '.
// Remove the starting and ending quote chars, the space at
// the end, and and the exe path.
//
int len = s.length();
s = s.substr(1, len - 3 - LEN_APP_NAME) + PATH_SUFFIX;
// Convert the wide string to a regular char array.
// Convert the wstring to c-string.
const wchar_t *path = s.c_str();
// Convert the WCHAR to char* and store it in the
// 'appPath' variable.
const size_t BUFFER_SIZE = 200;
size_t i;
appPath = (char*)malloc(BUFFER_SIZE);
wcstombs_s(&i, appPath, BUFFER_SIZE, path, BUFFER_SIZE);
// Create window class objects.
WNDCLASSEX wc;
MSG msg;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc; // Sets callback function.
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, MAKEINTRESOURCE(IDICN_WSDOT));
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = CLASS_NAME;
wc.hIconSm = wc.hIcon;
if (!RegisterClassEx(&wc))
{
MessageBox(NULL, L"Window Registration Failed!", L"Error!",
MB_ICONEXCLAMATION | MB_OK);
return -1;
}
// Load the resource bitmap.
hBitmap = LoadBitmap(hInstance, MAKEINTRESOURCE(IDBMP_TL7));
// Make a bitmap to hold the returned width and height.
// This bitmap will be deleted when DeleteObject is called
// for the hBitmap handle.
BITMAP bm;
if (hBitmap != NULL && GetObject(hBitmap, sizeof(bm), &bm))
{
bmWidth = bm.bmWidth;
bmHeight = bm.bmHeight;
}
else
{
MessageBox(hwnd, L"Error loading splash screen bitmap.",
L"Error", MB_ICONEXCLAMATION | MB_OK);
return -1;
}
// Center the window on the monitor
// where the mouse is.
RECT rc;
CenterRectToMonitor(&rc, bmWidth, bmHeight);
//
hwnd = CreateWindowEx(WS_EX_PALETTEWINDOW, wc.lpszClassName, // Uses the properties set in Window Class wc.
L"TMS Logs", WS_POPUP, rc.left, rc.top, bmWidth, bmHeight, NULL, NULL, hInstance, NULL);
if (hwnd == NULL)
{
MessageBox(NULL, L"Error creating Window.", L"Error",
MB_ICONEXCLAMATION | MB_OK);
return -1;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
//bool started = true;
bool started = false;
bool ret;
int x, w, y, h;
x = 369;
y = 10;
w = 19;
h = 19;
// Make close box in upper-right corner.
hwndButton = CreateWindow(
L"BUTTON", // Predefined class.
L"X", // Button text
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, // Styles
369, // x position
10, // y position
19, // Button width
19, // Button height
hwnd, // Parent window
NULL, // No menu.
(HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE),
NULL); // Pointer not needed.
// Make a box that moves along the bottom of
// the window while the other app. loads.
x = rcleft;
y = rctop;
w = 10;
h = 10;
hwndBox = CreateWindow(L"static", 0, WS_CHILD | WS_VISIBLE, x, y, w, h, hwnd, NULL,
(HINSTANCE) GetWindowLong (hwnd, GWL_HINSTANCE), NULL);
// Create a timer to close this window after
// several seconds, in case the main app. does
// not send the close window message to this app.
// the WM_TIMER message is handled in the WndProc
// callback function.
ptrTimer = SetTimer(hwnd, 1, TIMER_INTERVAL, (TIMERPROC)NULL);
// Start message Loop.
while ((ret = GetMessage(&msg, NULL, 0, 0)) != 0)
{
if (ret == -1)
{
MessageBox(NULL,
L"Error in Splash screen window message loop, at:\n\n'GetMessage(&msg, NULL, 0, 0))'.\n\nMessage return value was -1.",
L"TMS Logs", MB_ICONEXCLAMATION | MB_OK);
// Exit this app.
return -1;
}
// TranslateMessage(&Msg); // Not needed with what I have here.
DispatchMessage(&msg);
// Check app. path and start the other app.
if (!started)
{
started = true;
if (Path_Accessible(appPath))
{
// Start the main app.
if (!StartApplication(appPath))
{
// The folder or file is missing, or
// the user does not have security
// permissions to the sub-folder.
MessageBox(hwnd, L"Unexpected error at: StartApplication().",
L"Error", MB_ICONEXCLAMATION | MB_OK);
if (appPath)
{
free(appPath);
}
return -1;
}
}
else
{
// Display an error message.
char msg[350];
char* prefix = "Error: Can't find 'tl.exe'.\n\n It's missing from: '";
//char* suffix = " '\ncannot be accessed, or does not exist.";
int count = sizeof(msg);
strncpy(msg, prefix, count);
strncat(msg, appPath, count);
//strncat(msg, suffix, count);
// Convert c-string to wide char. string.
count = strlen(msg) + 1;
wchar_t* wMsg = new wchar_t[count];
size_t returnCount;
int ret = mbstowcs_s(&returnCount, wMsg, count, msg, _TRUNCATE);
if (ret == 0 && returnCount == count)
{
MessageBox(hwnd, wMsg, L"Error", MB_ICONEXCLAMATION | MB_OK);
}
else
{
// Error
MessageBox(hwnd, L"Error: The application path cannot be accessd, or does not exist.", L"Error", MB_ICONEXCLAMATION | MB_OK);
}
delete[] wMsg;
if (appPath)
{
free(appPath);
}
return -1;
}
}
}
if (appPath)
{
free(appPath);
}
return msg.wParam;
}
// Process messages.
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_COMMAND:
// The close button is the only child window,
// so no need to check wparam.
DestroyWindow(hwnd);
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
DestroyWindow(hwndButton);
DestroyWindow(hwndBox);
if (hBitmap != NULL)
{
if (!DeleteObject(hBitmap))
{
MessageBox(hwnd, L"Error at: WndProc(). Failed to delete the application bitmap.",
L"Error", MB_OK);
}
}
if (ptrTimer)
{
if (!KillTimer(hwnd, ptrTimer))
{
MessageBox(hwnd, L"Error at :WndProc(). Failed to free the application timer.",
L"Error", MB_OK);
}
}
PostQuitMessage(0);
break;
case WM_PAINT:
if (hBitmap != NULL)
{
// Paint the bitmap.
PAINTSTRUCT ps;
HDC hdc;
HDC hdcMem;
HGDIOBJ oldBitmap;
//
hdc = BeginPaint(hwnd, &ps);
hdcMem = CreateCompatibleDC(hdc); // a device context (dc) in memory to paint on.
oldBitmap = SelectObject(hdcMem, hBitmap);
// Copy bitmap to splash screen window.
BitBlt(hdc, 0, 0, bmWidth, bmHeight, hdcMem, 0, 0, SRCCOPY);
EndPaint(hwnd, &ps);
// Cleanup.
SelectObject(hdcMem, oldBitmap);
DeleteObject(oldBitmap);
DeleteDC(hdcMem);
HBRUSH hbr ;
// Fill rectangle.
RECT f;
GetClientRect(hwndBox, &f);
hdc = BeginPaint(hwndBox, &ps);
hdcMem = CreateCompatibleDC(hdc);
hbr = CreateSolidBrush(RGB(42, 59, 87));
SelectObject(hdc, hbr);
FillRect(hdc, &f, hbr);
EndPaint(hwnd, &ps);
// Cleanup.
SelectObject(hdcMem, oldBitmap);
DeleteObject(oldBitmap);
DeleteDC(hdcMem);
DeleteObject(hbr);
}
break;
case WM_TIMER:
timeCount++;
if (addLeft == 60)
{
addLeft = 10000;
ival = 2;
}
// 'Hide' the box for 2/3 of timer interval when
// it reaches the right side.
if (addLeft == 10000)
{
addLeft = 0;
ival = 1;
}
//
switch (ival)
{
case 2:
addLeft += 12;
break;
case 3:
ival = 0;
break;
}
ival++;
if (ival == 2)
{
// Move rectangle.
MoveWindow(hwndBox, rcleft + addLeft, rctop, 12, 12, true);
}
if (timeCount == MAX_TIME_COUNT)
{
DestroyWindow(hwnd);
}
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
Here is the code for referenced H files.
StartApp.H
#pragma once
#define WIN32_LEAN_AND_MEAN // Exclude DDE, RPC, Shell, Sockets, etc.
#define NOCOMM // Exclude serial communication APIs.
#include <Windows.h>
#include <cstdlib>
// Calls the CreateProcess function to start another app.
// ref: https://msdn.microsoft.com/en-us/library/windows/desktop/ms682512(v=vs.85).aspx
bool StartApplication(const char* appPath)
{
// The CreateProcess function requires
// wide char. array, so convert appPath.
size_t count = strlen(appPath) + 1;
wchar_t* path = new wchar_t[count];
size_t returnCount;
int ret = mbstowcs_s(&returnCount, path, count, appPath, _TRUNCATE);
if (ret != 0 || returnCount != count)
{
// Error converting C-string.
delete[] path;
return false;
}
// Required objects.
STARTUPINFO si;
PROCESS_INFORMATION pi;
// Set the size of the objects.
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
// Start the program at 'path'.
bool ok = CreateProcess(
path, // module name/app. path.
NULL, // Command line/path, if null app. path is used.
NULL, // Process handle, use null.
NULL, // Thread handle, use null.
FALSE, // Set handle inheritance to FALSE.
0, // No creation flags.
NULL, // Use parent's environment block.
NULL, // Use parent's starting directory.
&si, // Pointer to STARTUPINFO structure.
&pi); // Pointer to PROCESS_INFORMATION structure.
// Close process and thread handles.
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
delete[] path;
return ok;
}
CenterWindow.H
void CenterRectToMonitor(LPRECT prc, int rcWidth, int rcHeight)
{
POINT pt;
GetCursorPos(&pt);
HMONITOR mon;
mon = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
MONITORINFO mi;
mi.cbSize = sizeof(mi);
GetMonitorInfo(mon, &mi);
*prc = mi.rcMonitor;
//
// Center the window rectangle to the monitor rectangle.
prc->left = prc->left + (prc->right - prc->left - rcWidth) / 2;
prc->top = prc->top + (prc->bottom - prc->top - rcHeight) / 2;
prc->right = prc->left + rcWidth;
prc->bottom = prc->top + rcHeight;
}
PathStatus.H
#include <sys/stat.h>
const int SUCCESS = 0; // Indicates stat buffer was successfully set.
// Method to check if a file or folder
// exists and is accessible, using the stat
// structure and stat function, ref:
// http://pubs.opengroup.org/onlinepubs/009695399/functions/stat.html
bool Path_Accessible(const char* path)
{
if (path == 0)
{
return false;
}
struct stat path_status;
int result = stat(path, &path_status);
//
// The _S_IFREG flag indicates the path is a
// regular file and not a directory.
//
// To check for a directory, use '_S_IFDIR'.
return result == SUCCESS &&
(path_status.st_mode & _S_IFREG);
}
This is what my splash screen looks like. Note that the small box starts a little to the left of where it is now, moves a little to the right, and this repeats until the other app. this one started sends the WM_CLOSE message to this window.

Related

C++ Win32 API GDI : Rectangle AntiAliasing not working properly with transparent background

I use extended frame to render a custom caption and window border.
hr = DwmExtendFrameIntoClientArea(hWnd, &margins);
I also use layered window to render background transparent. My COLORKEY is RGB(0,0,0)
SetLayeredWindowAttributes(hWnd, RGB(0, 0, 0), 255, LWA_COLORKEY);
The reason I use layered window is, I want to make window's bottom border's corners rounded.
The issue is I want to do something beatiful and I tried to render a Anti Aliased window border in client area with GDI. However, It work as Anti Aliased with painted background (Solid) but not with TRANSPARENT background.
IMAGE: https://ibb.co/fD9GsFg
What should I do to fix this ?
If you try it please use VS debugger as I did not put functional window buttons.
#include <windows.h>
#include <tchar.h>
#include <windowsx.h>
#include <objidl.h>
#include <gdiplus.h>
#include <dwmapi.h>
#pragma comment(lib,"Dwmapi")
using namespace Gdiplus;
#pragma comment (lib,"Gdiplus.lib")
HINSTANCE hInst;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int nWidth = 600, nHeight = 400;
#define RECTWIDTH(rc) (rc.right - rc.left)
#define RECTHEIGHT(rc) (rc.bottom - rc.top)
//CHANGE THEM TO 0 when thats maximized!
const int TOPEXTENDWIDTH = 48;
const int LEFTEXTENDWIDTH = 8;
const int RIGHTEXTENDWIDTH = 8;
const int BOTTOMEXTENDWIDTH = 8;
HBRUSH RED_BRUSH = CreateSolidBrush(RGB(237, 28, 36));
HBRUSH DARKBLUE_BRUSH = CreateSolidBrush(RGB(26, 31, 96));
HBRUSH PURPLE_BRUSH = CreateSolidBrush(RGB(163, 73, 164));
HBRUSH LIGHTPURPLE_BRUSH_1 = CreateSolidBrush(RGB(189, 106, 189));
HBRUSH LIGHTPURPLE_BRUSH_2 = CreateSolidBrush(RGB(255, 174, 201));
HBRUSH DARKEST_BRUSH = CreateSolidBrush(RGB(0, 0, 0));
LRESULT HitTestNCA(HWND hWnd, WPARAM wParam, LPARAM lParam);
int WINAPI wWinMain( _In_opt_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_opt_ LPTSTR lpCmdLine, _In_opt_ int nCmdShow)
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
// Initialize GDI+.
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
hInst = hInstance;
WNDCLASSEX wcex =
{
sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW, WndProc, 0, 0, hInst, LoadIcon(NULL, IDI_APPLICATION),
LoadCursor(NULL, IDC_ARROW), (HBRUSH)GetStockObject(BLACK_BRUSH), NULL, TEXT("WindowClass"), NULL,
};
if (!RegisterClassEx(&wcex))
return MessageBox(NULL, L"Cannot register class !", L"Error", MB_ICONERROR | MB_OK);
int nX = (GetSystemMetrics(SM_CXSCREEN) - nWidth) / 2, nY = (GetSystemMetrics(SM_CYSCREEN) - nHeight) / 2;
HWND hWnd = CreateWindowEx(0, wcex.lpszClassName, TEXT("Test"), WS_OVERLAPPEDWINDOW, nX, nY, nWidth, nHeight, NULL, NULL, hInst, NULL);
if (!hWnd) return MessageBox(NULL, L"Cannot create window !", L"Error", MB_ICONERROR | MB_OK);
//NO SHADOW
SystemParametersInfoA(SPI_SETDROPSHADOW,0,(const PVOID) false,SPIF_SENDWININICHANGE);
ShowWindow(hWnd, SW_SHOWNORMAL);
UpdateWindow(hWnd);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
void FillRoundRectangle(Gdiplus::Graphics* g, Brush* p, Gdiplus::Rect& rect, UINT8 radius[4])
{
if (g == NULL) return;
GraphicsPath path;
//TOP RIGHT
path.AddLine(rect.X + radius[0], rect.Y, rect.X + rect.Width - (radius[0] * 2), rect.Y);
path.AddArc(rect.X + rect.Width - (radius[0] * 2), rect.Y, radius[0] * 2, radius[0] * 2, 270, 90);
//BOTTOM RIGHT
path.AddLine(rect.X + rect.Width, rect.Y + radius[1], rect.X + rect.Width, rect.Y + rect.Height - (radius[1] * 2));
path.AddArc(rect.X + rect.Width - (radius[1] * 2), rect.Y + rect.Height - (radius[1] * 2), radius[1] * 2, radius[1] * 2, 0, 90);
//BOTTOM LEFT
path.AddLine(rect.X + rect.Width - (radius[2] * 2), rect.Y + rect.Height, rect.X + radius[2], rect.Y + rect.Height);
path.AddArc(rect.X, rect.Y + rect.Height - (radius[2] * 2), radius[2] * 2, radius[2] * 2, 90, 90);
//TOP LEFT
path.AddLine(rect.X, rect.Y + rect.Height - (radius[3] * 2), rect.X, rect.Y + radius[3]);
path.AddArc(rect.X, rect.Y, radius[3] * 2, radius[3] * 2, 180, 90);
path.CloseFigure();
g->FillPath(p, &path);
}
VOID OnPaint(HDC hdc,int width, int height)
{
Graphics graphics(hdc);
graphics.SetSmoothingMode(SmoothingMode::SmoothingModeHighQuality);
graphics.SetCompositingQuality(CompositingQuality::CompositingQualityInvalid);
graphics.SetPixelOffsetMode(PixelOffsetMode::PixelOffsetModeHighQuality);
SolidBrush mySolidBrush(Color(255, 255, 0, 0)); ;
Gdiplus::Rect rect1;
rect1.X = 0;
rect1.Y = TOPEXTENDWIDTH;
rect1.Width = width;
rect1.Height = height- TOPEXTENDWIDTH-111;
UINT8 rad[4]{ 0,12,12,0 };
FillRoundRectangle(&graphics, &mySolidBrush, rect1, rad);
SolidBrush DarkSolidBrush(Color(255, 0, 1, 0)); ;
Gdiplus::Rect rectX = {0,455,55,55};
graphics.FillEllipse(&DarkSolidBrush,rectX);
}
LRESULT CustomCaptionProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool* pfCallDWP)
{
LRESULT lRet = 0;
HRESULT hr = S_OK;
bool fCallDWP = true; // Pass on to DefWindowProc?
static HICON hIcon = NULL;
fCallDWP = !DwmDefWindowProc(hWnd, message, wParam, lParam, &lRet);
if (message == WM_CREATE)
{
RECT rcClient;
GetWindowRect(hWnd, &rcClient);
// Inform the application of the frame change.
SetWindowPos(hWnd, NULL, rcClient.left, rcClient.top, RECTWIDTH(rcClient), RECTHEIGHT(rcClient), SWP_FRAMECHANGED);
HMODULE hDLL = LoadLibrary(L"Setupapi.dll");
if (hDLL)
{
hIcon = (HICON)LoadImage(hDLL, MAKEINTRESOURCE(2), IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR | LR_SHARED);
}
SetWindowLong(hWnd, GWL_EXSTYLE,
GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
/*Use pointer to function*/
SetLayeredWindowAttributes(hWnd, 0,
(255 * 70) / 100, LWA_ALPHA);
fCallDWP = true;
lRet = 0;
}
// Handle window activation.
if (message == WM_ACTIVATE)
{
// Extend the frame into the client area.
MARGINS margins;
margins.cxLeftWidth = 0;
margins.cxRightWidth = 0;
margins.cyBottomHeight = 0;
margins.cyTopHeight = 0;
hr = DwmExtendFrameIntoClientArea(hWnd, &margins);
if (!SUCCEEDED(hr))
{
// Handle error.
}
fCallDWP = true;
lRet = 0;
}
if (message == WM_PAINT)
{
PAINTSTRUCT ps;
BITMAP bm;
RECT rect, rectCaptionButtonBounds, rectText,myRect,ContentRect,ClientRect,CaptionBorderBottom, rect_EXIT_BTN, rect_RESTORE_BTN, rect_MINIMIZE_BTN;
HFONT windowTitleText;
GetClientRect(hWnd,&ClientRect);
BeginPaint(hWnd, &ps);
SetGraphicsMode(ps.hdc, GM_ADVANCED);
SetLayeredWindowAttributes(hWnd, RGB(0, 0, 0), 255, LWA_COLORKEY);
SetBkMode(ps.hdc, TRANSPARENT);
if (SUCCEEDED(DwmGetWindowAttribute(hWnd, DWMWA_CAPTION_BUTTON_BOUNDS, &rectCaptionButtonBounds, sizeof(rectCaptionButtonBounds))))
{
GetClientRect(hWnd, &rect);
//HRGN hrgn_cptBtmBrdrRND = CreateRoundRectRgn(0, 0, RECTWIDTH(ClientRect), RECTHEIGHT(ClientRect), 16, 16);
//FillRgn(ps.hdc, hrgn_cptBtmBrdrRND, DARKBLUE_BRUSH);
HRGN hrgn = CreateRectRgn(0, 0, RECTWIDTH(ClientRect), TOPEXTENDWIDTH);
FillRgn(ps.hdc, hrgn, PURPLE_BRUSH);
DrawIconEx(ps.hdc, rect.right - (rectCaptionButtonBounds.right - rectCaptionButtonBounds.left) - 32, 0, hIcon, 32, 32, 0, NULL, DI_NORMAL);
SetRect(&myRect, LEFTEXTENDWIDTH, 10, RECTWIDTH(rect)-200, TOPEXTENDWIDTH);
SetTextColor(ps.hdc, RGB(1, 0, 0));
DrawText(ps.hdc,L"test",-1,&myRect, DT_SINGLELINE | DT_RIGHT);
SetTextColor(ps.hdc, RGB(255, 255, 255));
WCHAR wsText[255] = L"ARMNET";
SetRect(&rectText, LEFTEXTENDWIDTH, 0, RECTWIDTH(rect), TOPEXTENDWIDTH);
windowTitleText =
CreateFontA
(
32,
0,
GM_ADVANCED,
0,
FW_DONTCARE,
false,
false,
false,
DEFAULT_CHARSET,
OUT_OUTLINE_PRECIS,
CLIP_DEFAULT_PRECIS,
CLEARTYPE_QUALITY, //BETTER BLENDING THAN ANTIALIASED
VARIABLE_PITCH,
"RETRO COMPUTER");
SelectObject(ps.hdc, windowTitleText);
DrawText(ps.hdc, wsText, -1, &rectText, DT_SINGLELINE | DT_VCENTER);
DeleteObject(windowTitleText);
DeleteObject(hrgn);
}
//CONTENT AREA
//SetRect(&ContentRect, 0, TOPEXTENDWIDTH, RECTWIDTH(ClientRect) - 0, RECTHEIGHT(ClientRect) - 0);
//FillRect(ps.hdc, &ContentRect, DARKBLUE_BRUSH);
HRGN hrgn_cptBtmBrdr = CreateRectRgn(0, TOPEXTENDWIDTH-1, RECTWIDTH(ClientRect), TOPEXTENDWIDTH);
FillRgn(ps.hdc, hrgn_cptBtmBrdr, CreateSolidBrush(RGB(132, 68, 133)));
hrgn_cptBtmBrdr = CreateRectRgn(0, TOPEXTENDWIDTH - 2, RECTWIDTH(ClientRect), TOPEXTENDWIDTH-1);
FillRgn(ps.hdc, hrgn_cptBtmBrdr,CreateSolidBrush(RGB(185, 91, 186)));
//BUTTONS
hrgn_cptBtmBrdr = CreateRectRgn(RECTWIDTH(ClientRect)-32, 0, RECTWIDTH(ClientRect), 32);
FillRgn(ps.hdc, hrgn_cptBtmBrdr, CreateSolidBrush(RGB(11, 11, 111)));
hrgn_cptBtmBrdr = CreateRectRgn(RECTWIDTH(ClientRect) - 64, 0, RECTWIDTH(ClientRect)-32, 32);
FillRgn(ps.hdc, hrgn_cptBtmBrdr, CreateSolidBrush(RGB(111, 11, 111)));
hrgn_cptBtmBrdr = CreateRectRgn(RECTWIDTH(ClientRect) - 96, 0, RECTWIDTH(ClientRect)-64, 32);
FillRgn(ps.hdc, hrgn_cptBtmBrdr, CreateSolidBrush(RGB(11, 111, 11)));
OnPaint(ps.hdc, RECTWIDTH(ClientRect), RECTHEIGHT(ClientRect));
DeleteObject(hrgn_cptBtmBrdr);
EndPaint(hWnd, &ps);
fCallDWP = true;
lRet = 0;
}
// Handle the non-client size message.
if ((message == WM_NCCALCSIZE) && (wParam == TRUE))
{
// Calculate new NCCALCSIZE_PARAMS based on custom NCA inset.
NCCALCSIZE_PARAMS *pncsp = reinterpret_cast<NCCALCSIZE_PARAMS*>(lParam);
pncsp->rgrc[0].left = pncsp->rgrc[0].left + 1;
pncsp->rgrc[0].top = pncsp->rgrc[0].top + 0;
pncsp->rgrc[0].right = pncsp->rgrc[0].right - 1;
pncsp->rgrc[0].bottom = pncsp->rgrc[0].bottom - 1;
lRet = 0;
// No need to pass the message on to the DefWindowProc.
fCallDWP = false;
}
// Handle hit testing in the NCA if not handled by DwmDefWindowProc.
if ((message == WM_NCHITTEST) && (lRet == 0))
{
lRet = HitTestNCA(hWnd, wParam, lParam);
if (lRet != HTNOWHERE)
{
fCallDWP = false;
}
}
if (message == WM_SIZE)
{
if (unsigned int(wParam) == SIZE_MAXIMIZED) {
}
else
{
}
}
if (message == WM_GETMINMAXINFO)
{
LPMINMAXINFO lpMMI = (LPMINMAXINFO)lParam;
lpMMI->ptMinTrackSize.x = 800;
lpMMI->ptMinTrackSize.y = 600;
}
if (message == WM_DESTROY)
PostQuitMessage(0);
*pfCallDWP = fCallDWP;
return lRet;
}
// Hit test the frame for resizing and moving.
LRESULT HitTestNCA(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
// Get the point coordinates for the hit test.
POINT ptMouse = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
// Get the window rectangle.
RECT rcWindow;
GetWindowRect(hWnd, &rcWindow);
// Get the frame rectangle, adjusted for the style without a caption.
RECT rcFrame = { 0 };
AdjustWindowRectEx(&rcFrame, WS_OVERLAPPEDWINDOW & ~WS_CAPTION, FALSE, NULL);
// Determine if the hit test is for resizing. Default middle (1,1).
USHORT uRow = 1;
USHORT uCol = 1;
bool fOnResizeBorder = false;
// Determine if the point is at the top or bottom of the window.
if ((ptMouse.y >= rcWindow.top && ptMouse.y < rcWindow.top + TOPEXTENDWIDTH) )
{
if((ptMouse.x < rcWindow.right - 100) || (ptMouse.y > rcWindow.top + 32)){
fOnResizeBorder = (ptMouse.y < (rcWindow.top - rcFrame.top));
uRow = 0;
}
}
else if (ptMouse.y < rcWindow.bottom && ptMouse.y >= rcWindow.bottom - BOTTOMEXTENDWIDTH)
{
uRow = 2;
}
// Determine if the point is at the left or right of the window.
if (ptMouse.x >= rcWindow.left && ptMouse.x < rcWindow.left + LEFTEXTENDWIDTH)
{
uCol = 0; // left side
}
else if (ptMouse.x < rcWindow.right && ptMouse.x >= rcWindow.right - RIGHTEXTENDWIDTH)
{
uCol = 2; // right side
}
// Hit test (HTTOPLEFT, ... HTBOTTOMRIGHT)
LRESULT hitTests[3][3] =
{
{ fOnResizeBorder ? HTTOPLEFT : HTLEFT, fOnResizeBorder ? HTTOP : HTCAPTION, fOnResizeBorder ? HTTOPRIGHT : HTRIGHT },
{ HTLEFT, HTNOWHERE, HTRIGHT },
{ HTBOTTOMLEFT, HTBOTTOM, HTBOTTOMRIGHT },
};
return hitTests[uRow][uCol];
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
bool fCallDWP = true;
BOOL fDwmEnabled = FALSE;
LRESULT lRet = 0;
HRESULT hr = S_OK;
// Winproc worker for custom frame issues.
hr = DwmIsCompositionEnabled(&fDwmEnabled);
if (SUCCEEDED(hr))
{
lRet = CustomCaptionProc(hWnd, message, wParam, lParam, &fCallDWP);
}
// Winproc worker for the rest of the application.
if (fCallDWP)
{
// lRet = AppWinProc(hWnd, message, wParam, lParam);
lRet = DefWindowProc(hWnd, message, wParam, lParam);
}
return lRet;
}
Well, after searching for solutions, in the end I think I found an answer.
SOLUTION:
A solution for Anti aliasing issue could be capturing the background of entire window bound by exluding it from capture. At this time you will not need layered window since you are painting the background with a bitmap. HDC will be able to blend it with background. Hopefully Windows 10 2004 version you have an option called:
WDA_EXCLUDEFROMCAPTURE
USAGE:
SetWindowDisplayAffinity(hWnd, WDA_EXCLUDEFROMCAPTURE); //At creation time
SOURCE: https://blogs.windows.com/windowsdeveloper/2019/09/16/new-ways-to-do-screen-capture/
After that you can paint background with bitmap and then paint everything over it. However, this produced low performance and I did not benefit. Still, that works and produces ANTI-ALIASED looking when drawn. For the performance issue Direct2D can be used for drawing from returned bitmap.
EXAMPLE:
int DsktpBkSS(HWND hWnd) {
HDC hdcScreen;
HDC hdcWindow;
HDC hdcMemDC = NULL;
HBITMAP hbmScreen = NULL;
BITMAP bmpScreen;
RECT windowPos;
SetWindowDisplayAffinity(hWnd, WDA_EXCLUDEFROMCAPTURE);
GetWindowRect(hWnd, &windowPos);
// Retrieve the handle to a display device context for the client
// area of the window.
hdcScreen = GetDC(NULL);
hdcWindow = GetDC(hWnd);
// Create a compatible DC which is used in a BitBlt from the window DC
hdcMemDC = CreateCompatibleDC(hdcWindow);
if (!hdcMemDC)
{
MessageBox(hWnd, L"CreateCompatibleDC has failed", L"Failed", MB_OK);
}
// Get the client area for size calculation
RECT rcClient;
GetClientRect(hWnd, &rcClient);
//This is the best stretch mode
SetStretchBltMode(hdcWindow, HALFTONE);
//The source DC is the entire screen and the destination DC is the current window (HWND)
if (!StretchBlt(hdcWindow,
0, 0,
GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN),
hdcScreen,
windowPos.left+1,windowPos.top,
GetSystemMetrics(SM_CXSCREEN),
GetSystemMetrics(SM_CYSCREEN),
SRCCOPY))
{
MessageBox(hWnd, L"StretchBlt has failed", L"Failed", MB_OK);
}
// Create a compatible bitmap from the Window DC
hbmScreen = CreateCompatibleBitmap(hdcWindow, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top);
if (!hbmScreen)
{
MessageBox(hWnd, L"CreateCompatibleBitmap Failed", L"Failed", MB_OK);
}
// Select the compatible bitmap into the compatible memory DC.
if(hdcMemDC && hbmScreen){
SelectObject(hdcMemDC, hbmScreen);
}
// Bit block transfer into our compatible memory DC.
if(hdcMemDC)
if (!BitBlt(hdcMemDC,
0, 0,
rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,
hdcWindow,
0, 0,
SRCCOPY))
{
MessageBox(hWnd, L"BitBlt has failed", L"Failed", MB_OK);
}
// Get the BITMAP from the HBITMAP
if(hbmScreen)
GetObjectW(hbmScreen, sizeof(BITMAP), &bmpScreen);
if (hbmScreen)DeleteObject(hbmScreen);
if (hdcMemDC)DeleteObject(hdcMemDC);
ReleaseDC(NULL, hdcScreen);
ReleaseDC(hWnd, hdcWindow);
return 0;
}
Later,
(...){
if(message==WM_PAINT)
{
PAINTSTRUCT ps;
BeginPaint(hWnd, &ps);
Graphics graphics(ps.hdc);
/*SET SMOOTHING (AA)*/
graphics.SetSmoothingMode(SmoothingMode::SmoothingModeHighQuality);
if(DsktpBkSS(hWnd))
{
/*DRAW ROUNDED RECTANGLE*/
}
}
//...
};
One more important thing for window drawn with "Desktop Window Manager" when resizing from left too fast and continuously that will produce "flickering" after a time. On Microsoft docs that is recommended that to use StretchBlt(...) for drawing since GDI+ cause this.
flicker mentioned for DWM: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-stretchblt
"Use BitBlt or StretchBlt function instead of Windows GDI+ to present
your drawing for rendering. GDI+ renders one scan line at a time with
software rendering. This can cause flickering in your applications."

Winapi: Screenshot is not displayed on window

I'm new with GUI programming on windows and came across some problems (using 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).
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. I've deleted basically all code in WndProc in sake of this question and only left WM_PAINT part.
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.
P.S
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. thanks.
Create a memory device context and bitmap (HBITMAP handle). Select the bitmap in to memory dc. Then use BitBlt to copy from screen to memory dc. The bitmap will then contain the screen data.
You can then print the bitmap handle directly in WM_PAINT. This would be a DDB, it can't be transferred between programs. You need DIB, or so use GetDIBits to copy the content from bitmap in to a byte array (g_bmpInfo and g_pixels)
Note that the size of 24-bit bitmap is not always with * height * 3, you need a special formula to account for padding.
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;
}

WinAPI WC_LISTVIEW paint issues

I created a ListView using CreateWindowEx and using the WC_LISTVIEW as class name.
I'm trying to create a smooth scrolling. Everything works perfectly except that list is not drawn correctly. See below the screenshot of the list:
The list view has the following style in CreateWindowEx:
WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_NOCOLUMNHEADER |
WS_TABSTOP | WS_BORDER | LVS_SHOWSELALWAYS | LVS_SINGLESEL | LVS_OWNERDRAWFIXED
I'm using a timer to scroll window. It does the following:
ScrollWindowEx(
listHandle,
0,
step * linesDelta,
NULL,
NULL,
0, 0, 0
);
UpdateWindow(listHandle);
Scroll works perfectly except painting.
I tried:
UpdateWindow() - screenshoot attached
RedrawWindow with all possible options - window is painted only once
InvalidateRect + UpdateWindow = same as 2
InvalidateRect + SendMessage(hwnd, WM_PAINT, 0, 0) - same as 2
The code that paints the item for the list is below:
LRESULT drawItem(HWND hwnd, DRAWITEMSTRUCT* drawStruct) {
Item *itemData = (Item *)drawStruct->itemData;
HDC hdc = drawStruct->hDC;
COLORREF backgroundColor;
COLORREF oldColor;
if (drawStruct->itemState & ODS_SELECTED || ListView_GetHotItem(hwnd) == drawStruct->itemID) {
backgroundColor = GetSysColor(COLOR_HIGHLIGHT);
oldColor = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
} else {
backgroundColor = RGB(255, 255, 255);
oldColor = SetTextColor(hdc, GetSysColor(COLOR_CAPTIONTEXT));
}
HBRUSH backgroundBrush = CreateSolidBrush(backgroundColor);
HBRUSH hOldBrush = (HBRUSH)SelectObject(hdc, backgroundBrush);
FillRect(hdc, &drawStruct->rcItem, backgroundBrush);
drawStruct->rcItem.left += 5;
drawStruct->rcItem.right -= 5;
drawStruct->rcItem.left += 30;
DrawText(hdc, itemData->path, -1, &drawStruct->rcItem,
DT_NOPREFIX | DT_SINGLELINE | DT_END_ELLIPSIS);
drawStruct->rcItem.left -= 30;
if (itemData->searchData && itemData->searchData->bitmap) {
HBITMAP bitmap = itemData->searchData->bitmap;
HDC hdcMem = CreateCompatibleDC(hdc);
HGDIOBJ oldBitmap = SelectObject(hdcMem, bitmap);
BITMAPINFO bi = { 0 };
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
// Get the bitmap info header.
if (0 != GetDIBits(
hdcMem, // hdc
bitmap, // hbmp
0, // uStartScan
0, // cScanLines
NULL, // lpvBits
&bi,
DIB_RGB_COLORS
)) {
BLENDFUNCTION blendFunc;
blendFunc.BlendOp = AC_SRC_OVER;
blendFunc.BlendFlags = 0;
blendFunc.SourceConstantAlpha = 255;
blendFunc.AlphaFormat = AC_SRC_ALPHA;
AlphaBlend(hdc,
drawStruct->rcItem.left + 2, //dest X
drawStruct->rcItem.top + 3, //dest Y
bi.bmiHeader.biWidth,
bi.bmiHeader.biHeight,
hdcMem, 0, 0,
bi.bmiHeader.biWidth,
bi.bmiHeader.biHeight, blendFunc);
}
SelectObject(hdcMem, oldBitmap);
DeleteDC(hdcMem);
}
SelectObject(hdc, hOldBrush);
DeleteObject(backgroundBrush);
SetTextColor(hdc, oldColor);
return 0;
}
Does anyone know a solution for this?
Please see below a complete example created from scratch that has exactly the same behavior:
#include "stdafx.h"
#include "TestList.h"
#include <strsafe.h>
#include <commctrl.h>
#define MAX_LOADSTRING 100
#define ID_LIST_BOX 200
#define TIMER_ID_SMOOTH_SCROLL 100
class ListData {
int scrollToDelta;
int currentScrollPos;
int numPixelsToChangeScrollPos;
int numPixelsChanged;
public:
HWND listWindow;
WNDPROC defaultListProcedure;
void startSmoothScrolling(HWND hwnd, int delta) {
if (delta < 0) {
scrollToDelta = 100;
} else {
scrollToDelta = -100;
}
SCROLLINFO si;
si.cbSize = sizeof(si);
si.fMask = SIF_RANGE | SIF_POS;
if (GetScrollInfo(listWindow, SB_VERT, &si)) {
double count = SendMessage(listWindow, LVM_GETITEMCOUNT, 0, 0);
double totalHeight = count * 30;
currentScrollPos = (int)((totalHeight * (double)si.nPos) / (double)si.nMax);
numPixelsToChangeScrollPos = totalHeight / si.nMax;
numPixelsChanged = 0;
} else {
currentScrollPos = 0;
numPixelsChanged = 0;
numPixelsToChangeScrollPos = 30;
}
}
void smoothScroll(HWND listHandle) {
SCROLLINFO si;
si.cbSize = sizeof(si);
si.fMask = SIF_RANGE | SIF_POS;
DWORD linesDelta;
SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &linesDelta, 0);
if (scrollToDelta < 0) {
if (GetScrollInfo(listHandle, SB_VERT, &si)) {
if (si.nPos == 0) {
KillTimer(listHandle, TIMER_ID_SMOOTH_SCROLL);
return;
}
}
scrollToDelta += 5;
int step = -5;
if (scrollToDelta > -80) {
step = -4;
} else if (scrollToDelta > -60) {
step = -3;
} else if (scrollToDelta > -40) {
step = -3;
} else if (scrollToDelta > -20) {
step = -2;
}
numPixelsChanged += abs(step);
if (numPixelsChanged >= numPixelsToChangeScrollPos) {
int posDelta = numPixelsChanged / numPixelsToChangeScrollPos;
numPixelsChanged -= posDelta * numPixelsToChangeScrollPos;
si.nPos = si.nPos + posDelta;
si.fMask = SIF_POS;
SetScrollInfo(listHandle, SB_VERT, &si, TRUE);
}
ScrollWindowEx(
listHandle,
0,
step * linesDelta,
NULL,
NULL,
0, 0,
SW_INVALIDATE);
if (scrollToDelta >= 0) {
KillTimer(listHandle, TIMER_ID_SMOOTH_SCROLL);
}
} else {
if (GetScrollInfo(listHandle, SB_VERT, &si)) {
int pos = GetScrollPos(listHandle, SB_VERT);
if (pos == si.nMax) {
KillTimer(listHandle, TIMER_ID_SMOOTH_SCROLL);
return;
}
}
scrollToDelta -= 5;
int step = 5;
if (scrollToDelta > -80) {
step = 4;
} else if (scrollToDelta > -60) {
step = 3;
} else if (scrollToDelta > -40) {
step = 3;
} else if (scrollToDelta > -20) {
step = 2;
}
numPixelsChanged += abs(step);
if (numPixelsChanged >= numPixelsToChangeScrollPos) {
int posDelta = numPixelsChanged / numPixelsToChangeScrollPos;
numPixelsChanged -= posDelta * numPixelsToChangeScrollPos;
si.nPos = si.nPos - posDelta;
si.fMask = SIF_POS;
SetScrollInfo(listHandle, SB_VERT, &si, TRUE);
}
ScrollWindowEx(
listHandle,
0,
step * linesDelta,
NULL,
NULL,
0, 0, 0
);
if (scrollToDelta <= 0) {
KillTimer(listHandle, TIMER_ID_SMOOTH_SCROLL);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//RedrawWindow(listHandle, NULL, NULL,
// RDW_UPDATENOW | RDW_INTERNALPAINT | RDW_INVALIDATE | RDW_NOERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
//InvalidateRect(listHandle, NULL, FALSE);
//SendMessage(listHandle, WM_PAINT, 0, 0);
UpdateWindow(listHandle);
//ListView_RedrawItems(listHandle, 0, 300);
////////////////////////////////////////////////////////////////////////////////////////////////////
}
};
struct Item {
WCHAR *name;
};
// Global Variables:
HINSTANCE hInst; // current instance
WCHAR szTitle[MAX_LOADSTRING]; // The title bar text
WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
// Forward declarations of functions included in this code module:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: Place code here.
// Initialize global strings
StringCchCopy(szTitle, MAX_LOADSTRING, L"Test");
StringCchCopy(szWindowClass, MAX_LOADSTRING, L"TestClassList");
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TESTLIST));
MSG msg;
// Main message loop:
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
//
// FUNCTION: MyRegisterClass()
//
// PURPOSE: Registers the window class.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TESTLIST));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_TESTLIST);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
LRESULT drawItem(HWND hwnd, DRAWITEMSTRUCT* drawStruct) {
Item *itemData = (Item *)drawStruct->itemData;
HDC hdc = drawStruct->hDC;
COLORREF backgroundColor;
//pcd->clrTextBk;
COLORREF oldColor;
if (drawStruct->itemState & ODS_SELECTED || ListView_GetHotItem(hwnd) == drawStruct->itemID) {
backgroundColor = GetSysColor(COLOR_HIGHLIGHT);
oldColor = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
} else {
backgroundColor = RGB(255, 255, 255);
oldColor = SetTextColor(hdc, GetSysColor(COLOR_CAPTIONTEXT));
}
HBRUSH backgroundBrush = CreateSolidBrush(backgroundColor);
HBRUSH hOldBrush = (HBRUSH)SelectObject(hdc, backgroundBrush);
FillRect(hdc, &drawStruct->rcItem, backgroundBrush);
drawStruct->rcItem.left += 5;
drawStruct->rcItem.right -= 5;
drawStruct->rcItem.left += 30;
DrawText(hdc, itemData->name, -1, &drawStruct->rcItem,
DT_NOPREFIX | DT_SINGLELINE | DT_END_ELLIPSIS);
drawStruct->rcItem.left -= 30;
SelectObject(hdc, hOldBrush);
DeleteObject(backgroundBrush);
SetTextColor(hdc, oldColor);
return 0;
}
LRESULT CALLBACK ListViewWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
////////////////////////////////////////////////////////////////////////////////////////////////////
case WM_TIMER: {
if (wParam == TIMER_ID_SMOOTH_SCROLL) {
ListData *listData = (ListData*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
listData->smoothScroll(hwnd);
}
break;
}
case WM_MOUSEWHEEL: {
int delta = HIWORD(wParam);
ListData *listData = (ListData*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
listData->startSmoothScrolling(hwnd, delta);
SetTimer(hwnd, TIMER_ID_SMOOTH_SCROLL, 200, NULL);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
default:
ListData *listData = (ListData*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
return CallWindowProc(listData->defaultListProcedure, hwnd, uMsg, wParam, lParam);
}
return 0;
}
//
// FUNCTION: InitInstance(HINSTANCE, int)
//
// PURPOSE: Saves instance handle and creates main window
//
// COMMENTS:
//
// In this function, we save the instance handle in a global variable and
// create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // Store instance handle in our global variable
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
100, 100, 400, 400, nullptr, nullptr, hInstance, nullptr);
if (!hWnd) {
return FALSE;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
HWND listWindow = CreateWindowEx(
0,
WC_LISTVIEW,
L"",
WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_NOCOLUMNHEADER |
WS_TABSTOP | WS_BORDER | LVS_SHOWSELALWAYS | LVS_SINGLESEL | LVS_OWNERDRAWFIXED,
1, //x
1, //y
400 - 20, //width
400 - 20, //height
hWnd,
(HMENU)ID_LIST_BOX,
hInstance,
NULL);
ListData *listData = new ListData();
listData->listWindow = listWindow;
SetWindowLongPtr(listWindow, GWLP_USERDATA, (LPARAM)listData);
listData->defaultListProcedure = (WNDPROC)SetWindowLongPtr(listWindow, GWLP_WNDPROC, (LONG_PTR)ListViewWindowProc);
ListView_SetExtendedListViewStyle(listWindow, LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER | LVS_EX_AUTOSIZECOLUMNS);
SendMessage(listWindow, LVM_SETTEXTBKCOLOR, 0, 0xFFFFFF);
LVCOLUMN col;
col.mask = LVCF_TEXT | LVCF_WIDTH;
col.pszText = L"";
col.cx = 390;
SendMessage(listWindow, LVM_INSERTCOLUMN, 0, (LPARAM)&col);
LVITEM item;
item.mask = LVIF_PARAM | LVIF_TEXT;
item.iSubItem = 0;
for (int i = 0; i < 300; i++) {
item.iItem = i;
Item *itemData = (Item*)malloc(sizeof(Item));
WCHAR *name = (WCHAR*)malloc(sizeof(WCHAR) * 30);;
wsprintf(name, L"Item Name %d", i);
itemData->name = name;
item.pszText = name;
item.lParam = (LPARAM)itemData;
SendMessage(listWindow, LVM_INSERTITEM, 0, (LPARAM)&item);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
//
// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// PURPOSE: Processes messages for the main window.
//
// WM_COMMAND - process the application menu
// WM_PAINT - Paint the main window
// WM_DESTROY - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) {
case WM_DRAWITEM: {
////////////////////////////////////////////////////////////////////////////////////////////////////
if (wParam == ID_LIST_BOX) {
DRAWITEMSTRUCT *drawStruct = (DRAWITEMSTRUCT*)lParam;
drawItem(drawStruct->hwndItem, drawStruct);
return TRUE;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
break;
}
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code that uses hdc here...
EndPaint(hWnd, &ps);
}
break;
case WM_MEASUREITEM: {
if (wParam == ID_LIST_BOX) {
MEASUREITEMSTRUCT *measureStruct = (MEASUREITEMSTRUCT*)lParam;
measureStruct->itemHeight = 30;
measureStruct->itemWidth = 390;
return TRUE;
}
break;
}
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
Here's an old trick: before invoking ScrollWindowEx() invalidate the entire listHandle area without erasing the background.
InvalidateRect(listHandle, NULL, FALSE);
ScrollWindowEx(...
Have a nice day!
From MSDN for ScrollWindowEx:
If the SW_INVALIDATE and SW_ERASE flags are not specified, ScrollWindowEx does not invalidate the area that is scrolled from. If either of these flags is set, ScrollWindowEx invalidates this area. The area is not updated until the application calls the UpdateWindow function, calls the RedrawWindow function (specifying the RDW_UPDATENOW or RDW_ERASENOW flag), or retrieves the WM_PAINT message from the application queue.
So let's look at what you tried:
UpdateWindow() - screenshoot attached
Nothing got invalidated, so the update region is empty, so UpdateWindow does nothing.
RedrawWindow with all possible options - window is painted only once
If called correctly, this should invalidate the client. You can cause the WM_ERASEBKGND to happen immediately, but the WM_PAINT message will come only when there's nothing else in the queue. I suspect this didn't work because the WM_TIMER takes priority over the WM_PAINT. (Both are special messages in that they are not actually posted but synthesized when you call GetMessage and nothing else is pending.)
InvalidateRect + UpdateWindow = same as 2
I would expect this to work, but it seems to make more sense to pass the flags to ScrollWindowEx to get the invalidation. I think what's happening is that the control is not designed to draw the items an non-integral positions. So you're getting invalidation, but the window is attempting to draw the items at different offsets than you expect. I don't see an straightforward way to solve this.
InvalidateRect + SendMessage(hwnd, WM_PAINT, 0, 0) - same as 2
That's not a valid WM_PAINT message. Don't do that.
The problem is that ListView is created using the LVS_REPORT flag. This means that list cannot scroll smoothly but only in lines. I.e. if line height is, for instance, 25, scrolling 20 pixels will make the list scroll 25 pixels.
One more issue is that ScrollWindowEx doesn't really scroll the list (or at least it wasn't used correctly). In order to scroll the list, the ListView_Scroll macro should be used (which again scrolls by lines not by pixels).

C++ CreateDIBitmap return null if move windows

I'm writing a demo program to load an image from file to OpenCV cv::Mat format, then convert to bitmap and display in Win32 windows. This is full source code:
// Win32
#include <Windows.h>
// OpenCV
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#pragma comment(lib, "opencv_core310d.lib")
#pragma comment(lib, "opencv_imgcodecs310d.lib")
// Marco
#define WIN_CLASS_NAME TEXT("DisplayTest")
#define WIN_NAME TEXT("Display Test")
#define IMAGE_SRC "./image.jpg"
// Global object
cv::Mat imgMat;
HWND hwndWindow;
// Convert cv::Mat data to bitmap
HBITMAP ConvertCVMatToBMP(cv::Mat frame)
{
auto convertOpenCVBitDepthToBits = [](const INT32 value) {
auto regular = 0u;
switch (value) {
case CV_8U:
case CV_8S:
regular = 8u;
break;
case CV_16U:
case CV_16S:
regular = 16u;
break;
case CV_32S:
case CV_32F:
regular = 32u;
break;
case CV_64F:
regular = 64u;
break;
default:
regular = 0u;
break;
}
return regular;
};
auto imageSize = frame.size();
if (imageSize.width && imageSize.height) {
auto headerInfo = BITMAPINFOHEADER{};
ZeroMemory(&headerInfo, sizeof(headerInfo));
headerInfo.biSize = sizeof(headerInfo);
headerInfo.biWidth = imageSize.width;
headerInfo.biHeight = -(imageSize.height); // negative otherwise it will be upsidedown
headerInfo.biPlanes = 1;// must be set to 1 as per documentation frame.channels();
const auto bits = convertOpenCVBitDepthToBits(frame.depth());
headerInfo.biBitCount = frame.channels() * bits;
auto bitmapInfo = BITMAPINFO{};
ZeroMemory(&bitmapInfo, sizeof(bitmapInfo));
bitmapInfo.bmiHeader = headerInfo;
bitmapInfo.bmiColors->rgbBlue = 0;
bitmapInfo.bmiColors->rgbGreen = 0;
bitmapInfo.bmiColors->rgbRed = 0;
bitmapInfo.bmiColors->rgbReserved = 0;
auto dc = GetDC(nullptr);
assert(dc != nullptr && "Failure to get DC");
auto bmp = CreateDIBitmap(dc,
&headerInfo,
CBM_INIT,
frame.data,
&bitmapInfo,
DIB_RGB_COLORS);
assert(bmp != nullptr && "Failure creating bitmap from captured frame");
DeleteDC(dc);
return bmp;
}
return nullptr;
}
// Attach image to windows
void attachImage()
{
HBITMAP bitImage = (HBITMAP)ConvertCVMatToBMP(imgMat);
// Display image
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwndWindow, &ps);
HDC imageDC = CreateCompatibleDC(hdc);
BITMAP bm;
HBITMAP imageBmpOld = (HBITMAP)SelectObject(imageDC, (HGDIOBJ)bitImage);
GetObject(bitImage, sizeof(bm), &bm);
BitBlt(
hdc, // tell it we want to draw to the screen
0, 0, // as position 0,0 (upper-left corner)
(int)bm.bmWidth, // width of the rect to draw
(int)bm.bmHeight, // height of the rect
imageDC, // the DC to get the rect from (our image DC)
0, 0, // take it from position 0,0 in the image DC
SRCCOPY // tell it to do a pixel-by-pixel copy
);
SelectObject(imageDC, (HGDIOBJ)imageBmpOld);
DeleteDC(imageDC);
DeleteObject((HGDIOBJ)imageBmpOld);
EndPaint(hwndWindow, &ps);
}
// WndProc callback
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
{
switch (message) {
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_PAINT:
attachImage();
break;
default:
return (DefWindowProc(hwnd, message, wparam, lparam));
}
}
// Register class
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASS wc = { 0 };
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.hCursor = LoadCursor(hInstance, IDC_ARROW);
wc.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
wc.hInstance = hInstance;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = WIN_CLASS_NAME;
wc.style = CS_HREDRAW | CS_VREDRAW;
return RegisterClass(&wc);
}
int main(int argc, char* argv[])
{
// Register class
char t[500];
GetConsoleTitleA(t, 500);
HWND hwndConsole = FindWindowA(NULL, t);
HINSTANCE hInstance = (HINSTANCE)GetWindowLong(hwndConsole, GWL_HINSTANCE);
MyRegisterClass(hInstance);
// Init data
imgMat = cv::imread(IMAGE_SRC);
// Create Win32 windows
hwndWindow = CreateWindow(WIN_CLASS_NAME, WIN_NAME, WS_OVERLAPPEDWINDOW ^ WS_THICKFRAME,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
NULL, NULL, hInstance, NULL);
ShowWindow(hwndWindow, SW_SHOWNORMAL);
UpdateWindow(hwndWindow);
MSG msg;
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(109));
// Main message loop:
while (GetMessage(&msg, NULL, 0, 0)) {
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return 0;
}
This code worked fine for me (Windows 10):
But if it running on Windows 7, after quickly drag and move the windows so that it "go out of" the screen:
And this is the result after move the window back into screen:
In this case, assert(bmp != nullptr) will fall and the program exit suddenly.
This does not happen on Windows 10!
Why CreateDIBitmap return null in this case???
SelectObject doesn't create a handle. In the function below you don't create imageBmpOld and you are not responsible for destroying it. But you did create bitImage (it is created in ConvertCVMatToBMP) and you should destroy bitImage at the end.
// Attach image to windows
void attachImage()
{
HBITMAP bitImage = (HBITMAP)ConvertCVMatToBMP(imgMat);
...
HBITMAP imageBmpOld = (HBITMAP)SelectObject(imageDC, (HGDIOBJ)bitImage);
...
SelectObject(imageDC, imageBmpOld);
DeleteDC(imageDC);
//DeleteObject((HGDIOBJ)imageBmpOld); <<== remove this line
DeleteObject(bitImage); //add this
EndPaint(hwndWindow, &ps);
}
Cleanup for GetDC is done by ReleaseDC, not DeleteDC
HBITMAP ConvertCVMatToBMP(cv::Mat frame)
{
...
auto dc = GetDC(nullptr);
assert(dc != nullptr && "Failure to get DC");
auto bmp = CreateDIBitmap(dc,
&headerInfo, CBM_INIT, frame.data, &bitmapInfo, DIB_RGB_COLORS);
assert(bmp != nullptr && "Failure creating bitmap from captured frame");
//DeleteDC(dc); <<== remove
ReleaseDC(nullptr, dc); //<<== add
...
}
Your window procedure doesn't always return a value. Add return 0; at the end.
The function attachImage() does not attach the image to window. It only paints the image on window. The way you have set it up it only works in response to WM_PAINT so you should really rename it OnPaint()
Also FindWindow(NULL, title) is not reliable because more than one window can have the same title. You should use GetConsoleWindow to get that window handle:
int main()
{
HWND hwndConsole = GetConsoleWindow();
...
}
Better yet, you could use WinMain entry point to skip the console window. The easiest way is to create a new project in your IDE, it should let you select "win32 project" (not "win32 console project")
//int main()
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, LPTSTR, int)
{
...
}

Getting Rid of Black Console Window When Running C++ Application

I am using Netbeans 7.1 to toy around with the AI tutorial I found here.
edit: I am using the GCC compiler.
I've gotten everything working, but I can't seem to get the application to compile and run with the Windows Subsystem... The application appears to be written properly for Windows API, and the executable that came with the source files from that website launches without producing the black console window that my own executable creates.
I've tried adding -mwindows as an option to the linker, and I've tried -Wl,-subsystem,windows. Neither of these have worked for me. I've provided the main.cpp below.
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdlib.h>
#include <time.h>
#include "utils.h"
#include "CController.h"
#include "CTimer.h"
#include "resource.h"
#include "CParams.h"
// edited this out, still not working
// #pragma comment(linker, "/SUBSYSTEM:windows /ENTRY:mainCRTStartup")
///////////////////////GLOBALS ////////////////////////////////////
char* szApplicationName = "Smart Sweepers v1.0";
char* szWindowClassName = "sweeper";
//The controller class for this simulation
CController* g_pController = NULL;
//create an instance of the parameter class.
CParams g_Params;
//---------------------------- Cleanup ----------------------------------
//
// simply cleans up any memory issues when the application exits
//-----------------------------------------------------------------------
void Cleanup()
{
if (g_pController)
delete g_pController;
}
//-----------------------------------WinProc-----------------------------
//
//-----------------------------------------------------------------------
LRESULT CALLBACK WindowProc(HWND hwnd,
UINT msg,
WPARAM wparam,
LPARAM lparam)
{
//these hold the dimensions of the client window area
static int cxClient, cyClient;
//used to create the back buffer
static HDC hdcBackBuffer;
static HBITMAP hBitmap;
static HBITMAP hOldBitmap;
switch(msg)
{
case WM_CREATE:
{
//seed the random number generator
srand((unsigned) time(NULL));
//get the size of the client window
RECT rect;
GetClientRect(hwnd, &rect);
cxClient = rect.right;
cyClient = rect.bottom;
//setup the controller
g_pController = new CController(hwnd);
//create a surface for us to render to(backbuffer)
hdcBackBuffer = CreateCompatibleDC(NULL);
HDC hdc = GetDC(hwnd);
hBitmap = CreateCompatibleBitmap(hdc,
cxClient,
cyClient);
ReleaseDC(hwnd, hdc);
hOldBitmap = (HBITMAP)SelectObject(hdcBackBuffer, hBitmap);
}
break;
//check key press messages
case WM_KEYUP:
{
switch(wparam)
{
case VK_ESCAPE:
{
PostQuitMessage(0);
}
break;
case 'F':
{
g_pController->FastRenderToggle();
}
break;
//reset the demo
case 'R':
{
if (g_pController)
{
delete g_pController;
}
//setup the new controller
g_pController = new CController(hwnd);
}
break;
}//end WM_KEYUP switch
}
break;
//has the user resized the client area?
case WM_SIZE:
{
cxClient = LOWORD(lparam);
cyClient = HIWORD(lparam);
//resize the backbuffer accordingly
SelectObject(hdcBackBuffer, hOldBitmap);
HDC hdc = GetDC(hwnd);
hBitmap = CreateCompatibleBitmap(hdc,
cxClient,
cyClient);
ReleaseDC(hwnd, hdc);
hOldBitmap = (HBITMAP)SelectObject(hdcBackBuffer, hBitmap);
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
//fill our backbuffer with white
BitBlt(hdcBackBuffer,
0,
0,
cxClient,
cyClient,
NULL,
NULL,
NULL,
WHITENESS);
//render the mines and sweepers
g_pController->Render(hdcBackBuffer);
//now blit backbuffer to front
BitBlt(ps.hdc, 0, 0, cxClient, cyClient, hdcBackBuffer, 0, 0, SRCCOPY);
EndPaint(hwnd, &ps);
}
break;
case WM_DESTROY:
{
SelectObject(hdcBackBuffer, hOldBitmap);
//clean up our backbuffer objects
DeleteDC(hdcBackBuffer);
DeleteObject(hBitmap);
// kill the application, this sends a WM_QUIT message
PostQuitMessage(0);
}
break;
default:break;
}//end switch
// default msg handler
return (DefWindowProc(hwnd, msg, wparam, lparam));
}//end WinProc
//-----------------------------------WinMain-----------------------------------------
// Entry point for our windows application
//-----------------------------------------------------------------------------------
int WINAPI WinMain( HINSTANCE hinstance,
HINSTANCE hprevinstance,
LPSTR lpcmdline,
int ncmdshow)
{
WNDCLASSEX winclass;
HWND hwnd;
MSG msg;
// first fill in the window class stucture
winclass.cbSize = sizeof(WNDCLASSEX);
winclass.style = CS_HREDRAW | CS_VREDRAW;
winclass.lpfnWndProc = WindowProc;
winclass.cbClsExtra = 0;
winclass.cbWndExtra = 0;
winclass.hInstance = hinstance;
winclass.hIcon = LoadIcon(hinstance, MAKEINTRESOURCE(IDI_ICON1));
winclass.hCursor = LoadCursor(NULL, IDC_ARROW);
winclass.hbrBackground= NULL;
winclass.lpszMenuName = NULL;
winclass.lpszClassName= szWindowClassName;
winclass.hIconSm = LoadIcon(hinstance, MAKEINTRESOURCE(IDI_ICON1));
// register the window class
if (!RegisterClassEx(&winclass))
{
MessageBox(NULL, "Error Registering Class!", "Error", 0);
return 0;
}
// create the window (one that cannot be resized)
if (!(hwnd = CreateWindowEx(NULL,
szWindowClassName,
szApplicationName,
WS_OVERLAPPED | WS_VISIBLE | WS_CAPTION | WS_SYSMENU,
GetSystemMetrics(SM_CXSCREEN)/2 - CParams::WindowWidth/2,
GetSystemMetrics(SM_CYSCREEN)/2 - CParams::WindowHeight/2,
CParams::WindowWidth,
CParams::WindowHeight,
NULL,
NULL,
hinstance,
NULL)))
{
MessageBox(NULL, "Error Creating Window!", "Error", 0);
return 0;
}
//Show the window
ShowWindow(hwnd, SW_SHOWDEFAULT );
UpdateWindow(hwnd);
//create a timer
CTimer timer(CParams::iFramesPerSecond);
//start the timer
timer.Start();
// Enter the message loop
bool bDone = FALSE;
while(!bDone)
{
while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
{
if( msg.message == WM_QUIT )
{
//Stop loop if it's a quit message
bDone = TRUE;
}
else
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
}
if (timer.ReadyForNextFrame() || g_pController->FastRender())
{
if(!g_pController->Update())
{
//we have a problem, end app
bDone = TRUE;
}
//this will call WM_PAINT which will render our scene
InvalidateRect(hwnd, NULL, TRUE);
UpdateWindow(hwnd);
}
}//end while
// Clean up everything and exit the app
Cleanup();
UnregisterClass( szWindowClassName, winclass.hInstance );
return 0;
} // end WinMain
This seems a bit strange, but windows subsystem application uses WinMainCRTStartup as entry point. So the following line looks inconsistent:
#pragma comment(linker, "/SUBSYSTEM:windows /ENTRY:mainCRTStartup")
It probably should be "/SUBSYSTEM:windows /ENTRY:WinMainCRTStartup" or "/SUBSYSTEM:console /ENTRY:mainCRTStartup"
On the other hand, I never trried to make a windows app with gcc. It may completely ignore this #pragma... Anyway, try to comment it out and see what happens. Generally compiler should be able to select the proper entry point without the compile time parameter anyway.