Winapi: Screenshot is not displayed on window - c++

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

Related

How to display animated GIF?

I have an animated GIF, I'm trying to display it but I couldn't make it work. It won't run and throwing exception 0xC0000005: Access violation reading location 0x80000B00. at the line SetTimer(hWnd, ID_TIMER, ((UINT*)m_pItem[0].value)[m_iCurrentFrame] * 10, (TIMERPROC)NULL);.
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
Image* m_pImage = new Image(L"spinner.gif");
UINT m_iCurrentFrame = 0;
UINT m_FrameCount = 0;
PropertyItem* m_pItem = 0;
switch(message) {
...
case WM_TIMER: {
//Because there will be a new delay value
KillTimer(hWnd, ID_TIMER);
//Change Active frame
GUID Guid = FrameDimensionTime;
m_pImage->SelectActiveFrame(&Guid, m_iCurrentFrame);
//New timer
SetTimer(hWnd, ID_TIMER, ((UINT*)m_pItem[0].value)[m_iCurrentFrame] * 10, NULL);
//Again move to the next
m_iCurrentFrame = (++m_iCurrentFrame) % m_FrameCount;
InvalidateRect(hWnd, NULL, FALSE);
break;
}
case WM_PAINT:{
HDC hdc;
PAINTSTRUCT ps;
hdc = BeginPaint(hWnd, &ps);
Graphics graphics(hdc);
//First of all we should get the number of frame dimensions
//Images considered by GDI+ as:
//frames[animation_frame_index][how_many_animation];
UINT count = m_pImage->GetFrameDimensionsCount();
//Now we should get the identifiers for the frame dimensions
GUID* m_pDimensionIDs = new GUID[count];
m_pImage->GetFrameDimensionsList(m_pDimensionIDs, count);
//For gif image , we only care about animation set#0
WCHAR strGuid[40];
StringFromGUID2(m_pDimensionIDs[0], strGuid, 40);
m_FrameCount = m_pImage->GetFrameCount(&m_pDimensionIDs[0]);
//PropertyTagFrameDelay is a pre-defined identifier
//to present frame-delays by GDI+
UINT TotalBuffer = m_pImage->GetPropertyItemSize(PropertyTagFrameDelay);
m_pItem = (PropertyItem*)malloc(TotalBuffer);
m_pImage->GetPropertyItem(PropertyTagFrameDelay, TotalBuffer, m_pItem);
//Set Current Frame at #0
m_iCurrentFrame = 0;
GUID Guid = FrameDimensionTime;
m_pImage->SelectActiveFrame(&Guid, m_iCurrentFrame);
//Use Timer
//NOTE HERE: frame-delay values should be multiply by 10
SetTimer(hWnd, ID_TIMER, ((UINT*)m_pItem[0].value)[m_iCurrentFrame] * 10, (TIMERPROC)NULL);
//Move to the next frame
++m_iCurrentFrame;
InvalidateRect(hWnd, NULL, FALSE);
//Finally simply draw
graphics.DrawImage(m_pImage, 120, 120, 100, 100);
EndPaint(hWnd, &ps);
break;
}
...
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
Most of the example I found is WCF or the functions are depreciated.
Solution:
You can set m_pItem to global. In addition to this error, you have some other errors. For example, m_iCurrentFrame is always initialized to 0 in WM_PAINT.
And we only need to load the GIF image file once, and don't need to put it in WM_PAINT.
GIF loop play,
#include <memory>
#include <vector>
#include <algorithm>
#include <windows.h>
#include <objidl.h>
#include <GdiPlus.h>
#include <gdiplusimaging.h>
#include <tchar.h>
using namespace Gdiplus;
#pragma comment (lib,"Gdiplus.lib")
#define TIMER_ID 101
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
ULONG_PTR m_gdiplusToken;
GdiplusStartupInput gdiplusStartupInput;
GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);
Image gif(_T("spinner.gif"));
MSG msg = { 0 };
WNDCLASS wc = { 0 };
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hbrBackground = NULL; // <= Do not provide a background brush.
wc.lpszClassName = L"anim_gif_player";
if (!RegisterClass(&wc))
return -1;
if (!CreateWindow(wc.lpszClassName,
L"Animated GIF player",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
0, 0, 1080, 800, 0, 0, hInstance, &gif))
return -2;
while (GetMessage(&msg, NULL, 0, 0) > 0)
DispatchMessage(&msg);
return 0;
}
std::vector<unsigned int> LoadGifFrameInfo(Image* image)
{
// I think animated gifs will always only have 1 frame dimension...
// the "dimension" being the frame count, but I could be wrong about this
int count = image->GetFrameDimensionsCount();
if (count != 1)
return std::vector<unsigned int>();
GUID guid;
if (image->GetFrameDimensionsList(&guid, 1) != 0)
return std::vector<unsigned int>();
int frame_count = image->GetFrameCount(&guid);
auto sz = image->GetPropertyItemSize(PropertyTagFrameDelay);
if (sz == 0)
return std::vector<unsigned int>();
// copy the frame delay property into the buffer backing an std::vector
// of bytes and then get a pointer to its value, which will be an array of
// unsigned ints
std::vector<unsigned char> buffer(sz);
PropertyItem* property_item = reinterpret_cast<PropertyItem*>(&buffer[0]);
image->GetPropertyItem(PropertyTagFrameDelay, sz, property_item);
unsigned int* frame_delay_array = (unsigned int*)property_item[0].value;
// copy the delay values into an std::vector while converting to milliseconds.
std::vector<unsigned int> frame_delays(frame_count);
std::transform(frame_delay_array, frame_delay_array + frame_count, frame_delays.begin(),
[](unsigned int n) {return n * 10; }
);
return frame_delays;
}
void GenerateFrame(Bitmap* bmp, Image* gif)
{
Graphics dest(bmp);
SolidBrush white(Color::White);
dest.FillRectangle(&white, 0, 0, bmp->GetWidth(), bmp->GetHeight());
if (gif)
dest.DrawImage(gif, 0, 0);
}
std::unique_ptr<Bitmap> CreateBackBuffer(HWND hWnd)
{
RECT r;
GetClientRect(hWnd, &r);
return std::make_unique<Bitmap>(r.right - r.left, r.bottom - r.top);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static Image* animated_gif;
static std::unique_ptr<Bitmap> back_buffer;
static std::vector<unsigned int> frame_delays;
static int current_frame;
switch (message) {
case WM_CREATE: {
animated_gif = reinterpret_cast<Image*>(
reinterpret_cast<CREATESTRUCT*>(lParam)->lpCreateParams
);
if (!animated_gif || animated_gif->GetLastStatus() != 0) {
MessageBox(hWnd, _T("Unable to load animated gif"), _T("error"), MB_ICONERROR);
return 0;
}
// Create a bitmap the size of the window's clent area
back_buffer = CreateBackBuffer(hWnd);
// get the frame delays and thereby test that this is really an animated gif
frame_delays = LoadGifFrameInfo(animated_gif);
if (frame_delays.empty()) {
MessageBox(hWnd, _T("Invalid gif or not an animated gif"), _T("error"), MB_ICONERROR);
return 0;
}
current_frame = 0;
animated_gif->SelectActiveFrame(&FrameDimensionTime, current_frame);
GenerateFrame(back_buffer.get(), animated_gif);
SetTimer(hWnd, TIMER_ID, frame_delays[0], nullptr);
InvalidateRect(hWnd, nullptr, FALSE);
}
break;
case WM_TIMER: {
KillTimer(hWnd, TIMER_ID);
current_frame = (current_frame + 1) % frame_delays.size();
animated_gif->SelectActiveFrame(&FrameDimensionTime, current_frame);
GenerateFrame(back_buffer.get(), animated_gif);
SetTimer(hWnd, TIMER_ID, frame_delays[current_frame], nullptr);
InvalidateRect(hWnd, nullptr, FALSE);
} break;
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
Graphics g(hdc);
g.DrawImage(back_buffer.get(), 0, 0);
EndPaint(hWnd, &ps);
} break;
case WM_SIZE: {
back_buffer = CreateBackBuffer(hWnd);
GenerateFrame(back_buffer.get(), animated_gif);
} break;
case WM_CLOSE:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
As per as reading into the comment section, it looks like the error 0xC0000005: Access violation reading location 0x80000B00. at the line SetTimer(hWnd, ID_TIMER, ((UINT*)m_pItem[0].value)[m_iCurrentFrame] * 10, (TIMERPROC)NULL); happened because the GIF contains only one frame.
You can avoid it by calling the timer only if there are more than one frame, for example;
if(m_FrameCount > 1){
//Continue calling the timer.
}
Here's another example;
/** These are global **/
Image* m_pImage;
GUID* m_pDimensionIDs;
UINT m_FrameCount;
PropertyItem* m_pItem;
UINT m_iCurrentFrame;
UINT_PTR timer_id = 123456; //set your own timer id
BOOL m_bIsPlaying;
BOOL isPlayable;
/** Functions **/
void LoadImage() {
m_pImage = Image::FromFile(L"C:\\Users\\username\\location_of_file\\spinner.gif");
UINT count = m_pImage->GetFrameDimensionsCount();
m_pDimensionIDs = new GUID[count];
m_pImage->GetFrameDimensionsList(m_pDimensionIDs, count);
WCHAR strGuid[39];
(void)StringFromGUID2(m_pDimensionIDs[0], strGuid, 39);
m_FrameCount = m_pImage->GetFrameCount(&m_pDimensionIDs[0]);
if(m_FrameCount > 1) { //frame of GIF is more than one, all good, we don't want the error of `Access violation reading location`
isPlayable = TRUE;
OutputDebugString(_T("Image file contains more than 1 frame, its playable.\n"));
}
UINT TotalBuffer = m_pImage->GetPropertyItemSize(PropertyTagFrameDelay);
m_pItem = (PropertyItem*)malloc(TotalBuffer);
m_pImage->GetPropertyItem(PropertyTagFrameDelay, TotalBuffer, m_pItem);
}
/** call this function on timer **/
void OnTimer() {
if(isPlayable) {
KillTimer(hwnd, timer_id);
GUID Guid = FrameDimensionTime;
m_pImage->SelectActiveFrame(&Guid, m_iCurrentFrame);
SetTimer(hwnd, 120, ((UINT*)m_pItem[0].value)[m_iCurrentFrame] * 10, NULL);
m_iCurrentFrame = (++m_iCurrentFrame) % m_FrameCount;
//Invalidate(FALSE);
InvalidateRect(hwnd, NULL, FALSE);
}
}
/** call this to animate the GIF image, should be call before drawing the image usually on WM_PAINT. **/
void AnimateGIF() {
if(m_bIsPlaying == TRUE) {
return;
}
if(isPlayable) {
m_iCurrentFrame = 0;
GUID Guid = FrameDimensionTime;
m_pImage->SelectActiveFrame(&Guid, m_iCurrentFrame);
SetTimer(hwnd, 120, ((UINT*)m_pItem[0].value)[m_iCurrentFrame] * 10, NULL);
++m_iCurrentFrame;
InvalidateRect(hwnd, NULL, FALSE);
m_bIsPlaying = TRUE;
}
}
/** Function to draw the image in Window, must be call on WM_PAINT. **/
void DrawItem(HDC hdc, int xPosition, int yPosition, int width, int height) {
AnimateGIF(); //This will only works if the image has more than one frame. Otherwise, the single frame of image will be shown.
Graphics g(hdc);
g.DrawImage(m_pImage, xPosition, yPosition, width, height);
}
/** Function to destroy objects and arrays, call this function on WM_DESTROY of WinProc. **/
void Desrtroy() {
if(m_pDimensionIDs) {
delete[] m_pDimensionIDs;
}
if(m_pItem) {
free(m_pItem);
}
if(m_pImage) {
delete m_pImage;
}
}
Then on your WndProc, you can call the functions above;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch(message) {
case WM_CREATE: {
LoadImage(); //1. Load the image
break;
}
case WM_TIMER: {
OnTimer(); //2. Call on timer.
break;
}
case WM_PAINT:
{
HDC hdc;
PAINTSTRUCT ps;
hdc = BeginPaint(hWnd, &ps);
DrawItem(hdc, 160, 150, 95, 95); //3. Draw your image, specify your own x and y Position, width and height.
EndPaint(hWnd, &ps);
break;
}
case WM_DESTROY:
{
Desrtroy(); //4. Don't forget to delete the images and free up the arrays.
PostQuitMessage(0);
break;
}
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
With this approach, you won't get the same error. The GIF will play forever if there are more than one frame. Otherwise, it will only display the single frame.
The project example can be found from this link, credit to SeynalKim.

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 redrawing bitmaps in client area

I am attempting to make image viewer in winapi,
so basically it loads and draws my first image, but then i click button to navigate through .bmp images in given directory and they are not being drawn.
I would appreciate if you could help me and tell me where I am making mistake.
Here are my WndProc variables:
WIN32_FIND_DATA file;
HBITMAP img;
std::vector<WIN32_FIND_DATA> vFiles;
std::vector<WIN32_FIND_DATA>::iterator it;
HANDLE hFile = FindFirstFile(L"C:\\Users\\eclip7e\\Pictures\\*.bmp", &file);
if (hFile != INVALID_HANDLE_VALUE)
{
it = vFiles.begin();
it = vFiles.insert(it, file);
while (FindNextFile(hFile, &file))
{
it = vFiles.insert(it, file);
}
it = vFiles.begin();
}
Here is my button click:
case WM_COMMAND:
{
switch (LOWORD(wParam))
{
case IDC_RIGHT_BUTTON:
{
if (it<vFiles.end())
{
it++;
InvalidateRect(hWnd, NULL, TRUE);
}
else if (it == vFiles.end())
{
it = vFiles.begin();
InvalidateRect(hWnd, NULL, TRUE);
}
}
break;
}
}
break;
Here is my WM_PAINT:
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
HDC hdcMem;
LPCWSTR d = L"C:\\Users\\eclip7e\\Pictures\\";
WIN32_FIND_DATA filex;
filex = *(it);
std::wstring df = std::wstring(d) + filex.cFileName;
LPCWSTR dfc = df.c_str();
img = (HBITMAP)LoadImage(NULL, dfc, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
hdcMem = CreateCompatibleDC(hdc);
// Get the bitmap's parameters and verify the get
BITMAP qBitmap;
int iReturn = GetObject(reinterpret_cast<HGDIOBJ>(img), sizeof(BITMAP),
reinterpret_cast<LPVOID>(&qBitmap));
SelectObject(hdcMem, img);
BOOL bb =BitBlt(hdc, 0, 0, qBitmap.bmWidth, qBitmap.bmHeight, hdcMem, 0, 0, SRCCOPY);
DeleteDC(hdcMem);
DeleteObject(img);
EndPaint(hWnd, &ps);
}
break;

Splash Screen Make Wait box move

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.

C++ & WinApi - Text output from the keyboard to the screen in the created window

I do not see where the error is. The window display only the first character you type, and the other is not. Although the cursor moves when typing. And when minimize and maximize the window, the text becomes visible.
#include <windows.h>
#define IDI_MYICON 201
#define IDR_MYMENU 201
#define ID_FIO_FirstName 4002
#define ID_FIO_SecondName 4003
#define ID_FIO_Name 4001
#define ID_INFO_DeveloperStatus 9002
#define ID_INFO_LearningForm 9003
#define ID_INFO_Group 9004
#define BUFSIZE 6665535
#define SHIFTED 0x8000
const char g_szClassName[] = "MFW";
LONG APIENTRY WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
TEXTMETRIC tm;
static DWORD dwCharX;
static DWORD dwCharY;
static DWORD dwClientX;
static DWORD dwClientY;
static DWORD dwLineLen;
static int nCaretPosX = 0;
static int nCaretPosY = 0;
static int nCharWidth = 0;
static int cch = 0;
static int nCurChar = 0;
static PTCHAR pchInputBuf;
int i, j;
int cCR = 0;
int nCRIndex = 0;
int nVirtKey;
TCHAR szBuf[128];
TCHAR ch;
PAINTSTRUCT ps;
RECT rc;
SIZE sz;
COLORREF crPrevText;
COLORREF crPrevBk;
switch(Message)
{
case WM_CREATE:
{
hdc = GetDC(hwnd);
GetTextMetrics(hdc, &tm);
ReleaseDC(hwnd, hdc);
dwCharX = tm.tmAveCharWidth;
dwCharY = tm.tmHeight;
pchInputBuf=(LPTSTR)GlobalAlloc(GPTR,BUFSIZE*sizeof(TCHAR));
HMENU hMenu, hSubMenu;
HICON hIcon, hIconSm;
hMenu = CreateMenu();
hSubMenu = CreatePopupMenu();
AppendMenu(hSubMenu, MF_STRING, ID_FIO_FirstName, "FirstName");
AppendMenu(hSubMenu, MF_STRING, ID_FIO_SecondName, "SecondName");
AppendMenu(hSubMenu, MF_STRING, ID_FIO_Name, "Name");
AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, "&FIO");
hSubMenu = CreatePopupMenu();
AppendMenu(hSubMenu, MF_STRING, ID_INFO_DeveloperStatus, "DeveloperStatus");
AppendMenu(hSubMenu, MF_STRING, ID_INFO_LearningForm, "LearningForm");
AppendMenu(hSubMenu, MF_STRING, ID_INFO_Group, "Group");
AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, "&INFO");
SetMenu(hwnd, hMenu);
hIcon =(HICON)LoadImage(NULL, "my_icon.ico", IMAGE_ICON, 32, 32, LR_LOADFROMFILE);
if(hIcon)
SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
else
MessageBox(hwnd, "Could not load large icon!","Error", MB_OK | MB_ICONERROR);
hIconSm =(HICON)LoadImage(NULL, "my_icon.ico", IMAGE_ICON, 16, 16, LR_LOADFROMFILE);
if(hIconSm)
SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIconSm);
else
MessageBox(hwnd, "Could not load small icon!","Error", MB_OK | MB_ICONERROR);
break;
}
break;
case WM_PAINT:
if (cch==0)break;
hdc=BeginPaint(hwnd,&ps);
HideCaret(hwnd);
SetRect(&rc, 0, 0, dwLineLen, dwClientY);
DrawText(hdc, pchInputBuf,-1,&rc,DT_NOCLIP);
ShowCaret(hwnd);
EndPaint(hwnd, &ps);
break;
case WM_SIZE:
dwClientX=LOWORD(lParam);
dwClientY = HIWORD(lParam);
dwLineLen=dwClientX-dwCharX;
break;
case WM_SETFOCUS:
CreateCaret(hwnd, (HBITMAP) 0,3, dwCharY);
SetCaretPos(nCaretPosX,nCaretPosY*dwCharY);
ShowCaret(hwnd);
break;
case WM_KILLFOCUS:
HideCaret(hwnd);
DestroyCaret();
break;
case WM_CHAR:
if (cch > BUFSIZE-5)
{
pchInputBuf[cch] = 0x00;
SendMessage(hwnd,WM_PAINT, 0, 0);
}
switch (wParam)
{
case 0x08:
case 0x0A:
case 0x1B:
MessageBeep(0xFFFFFFFF);
return 0;
case 0x09:
for (i = 0; i < 4; i++)
SendMessage(hwnd, WM_CHAR, 0x20, 0);
return 0;
case 0x0D:
pchInputBuf[cch++] = 0x0D;
nCaretPosX = 0;
nCaretPosY += 1;
break;
default:
ch =(TCHAR)wParam;
HideCaret(hwnd);
hdc = GetDC(hwnd);
GetCharWidth32(hdc,wParam,wParam,&nCharWidth);
DrawText(hdc, pchInputBuf,-1,&rc, DT_NOCLIP);
ReleaseDC(hwnd, hdc);
pchInputBuf[cch++] = ch;
nCaretPosX += nCharWidth;
if ((DWORD) nCaretPosX > dwLineLen)
{
nCaretPosX = 0;
pchInputBuf[cch++] = 0x0D;
++nCaretPosY;
}
nCurChar = cch;
ShowCaret(hwnd);
break;
}
SetCaretPos(nCaretPosX, nCaretPosY * dwCharY);
break;
case WM_KEYDOWN:
switch (wParam)
{
case VK_LEFT:
if (nCaretPosX > 0)
{
HideCaret(hwnd);
ch = pchInputBuf[--nCurChar];
hdc = GetDC(hwnd);
GetCharWidth32(hdc, ch, ch, &nCharWidth);
ReleaseDC(hwnd, hdc);
nCaretPosX = max(nCaretPosX - nCharWidth, 0);
ShowCaret(hwnd);
} break;
case VK_RIGHT:
if (nCurChar > cch) {
HideCaret(hwnd);
ch = pchInputBuf[nCurChar];
if (ch == 0x0D) {
nCaretPosX = 0;
nCaretPosY++;
}
else {
hdc = GetDC(hwnd);
nVirtKey = GetKeyState(VK_SHIFT);
if (nVirtKey & SHIFTED) {
crPrevText = SetTextColor(hdc,RGB(255, 255, 255));
crPrevBk = SetBkColor(hdc,RGB(0,0,0));
TextOut(hdc, nCaretPosX,nCaretPosY * dwCharY,&ch,1);
SetTextColor(hdc, crPrevText);
SetBkColor(hdc, crPrevBk);
}
GetCharWidth32(hdc, ch, ch, &nCharWidth);
ReleaseDC(hwnd, hdc);
nCaretPosX = nCaretPosX + nCharWidth;
}
nCurChar++;
ShowCaret(hwnd);
break;
}
break;
case VK_HOME:
nCaretPosX = nCaretPosY = 0;
nCurChar = 0;
break;
case VK_END:
for (i=0; i < cch; i++)
{
if (pchInputBuf[i] == 0x0D) {
cCR++;
nCRIndex = i + 1;
}
}
nCaretPosY = cCR;
for (i = nCRIndex, j = 0; i < cch; i++, j++)
szBuf[j] = pchInputBuf[i];
szBuf[j] = TEXT('\0');
hdc = GetDC(hwnd);
GetTextExtentPoint32(hdc,szBuf,lstrlen(szBuf),&sz);
nCaretPosX = sz.cx;
ReleaseDC(hwnd, hdc);
nCurChar = cch;
break;
default:
break;
}
SetCaretPos(nCaretPosX, nCaretPosY * dwCharY);
break;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case ID_FIO_FirstName:
MessageBox(hwnd, "F-3", "FirstName", 0);
break;
case ID_FIO_SecondName:
MessageBox(hwnd, "B-2", "SecondName", 0);
break;
case ID_FIO_Name:
MessageBox(hwnd, "A-1", "Name", 0);
break;
case ID_INFO_DeveloperStatus:
MessageBox(hwnd, "S", "DeveloperStatus", 0);
break;
case ID_INFO_LearningForm:
MessageBox(hwnd, "FORM", "LearningForm", 0);
break;
case ID_INFO_Group:
MessageBox(hwnd, "GR", "Group", 0);
break;
}
break;
case WM_CLOSE:
DestroyWindow(hwnd);
GlobalFree((HGLOBAL) pchInputBuf);
UnregisterHotKey(hwnd, 0xAAAA);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, Message, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
HWND hwnd;
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(GetModuleHandle(NULL),MAKEINTRESOURCE(IDI_MYICON));
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszMenuName = MAKEINTRESOURCE(IDR_MYMENU);
wc.lpszClassName = g_szClassName;
wc.hIconSm = (HICON)LoadImage(GetModuleHandle(NULL),MAKEINTRESOURCE(IDI_MYICON),IMAGE_ICON,16,16, 0);
if(!RegisterClassEx(&wc))
{
MessageBox(NULL, "Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
hwnd = CreateWindowEx(
WS_EX_OVERLAPPEDWINDOW,
g_szClassName,
"QWERTY",
WS_OVERLAPPEDWINDOW,
200, 100, 800, 600,
NULL, NULL, hInstance, NULL);
if(hwnd == NULL)
{
MessageBox(NULL, "Window Creation Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
MSG Msg;
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}
You use WM_PAINT incorrectly. You shouldn't send it manually. You need to call InvalidateRect(hwnd, NULL, true); instead of SendMessage(hwnd,WM_PAINT, 0, 0); and it will post a WM_PAINT message to your WndProc correctly.
When you minimize/maximize the window, Windows call internally something like InvalidateRect(hwnd, NULL, true);
Addition by manuell:
Also always call BeginPaint/EndPaint in WM_PAINT handler (you shouldn't break before calling BeginPaint/EndPaint).