Why do I get a WM_MOUSELEAVE after enabling window? - c

After enabling a disabled child window, I try to turn mouse tracking on in WM_ENABLE only if mouse cursor is hovering over the window using TrackMouseEvent() with dwFlags of TRACKMOUSEEVENT structure set to TME_LEAVE. TrackMouseEvent() returns TRUE, but then right after calling it I get a WM_MOUSELEAVE message. This happens only under 2 conditions. With first condition, move cursor outside of child window, press Enter key to disable the window, then move cursor over the child window and press the Space key. With second condition, move the cursor over window, press Enter key to disable it, then before pressing the Space key move the cursor 1 pixel or more and then press the Space key. If you retest the second condition, but instead of moving cursor before you press the Space key, if you press the Space key right after you press the Enter key, mouse tracking is turned on properly. I've tried really hard to fix this but I've not been lucky so far. Can somebody please fix this code and explain why mouse tracking is being canceled when I'm trying to turn it on?
#include <windows.h>
const WCHAR g_szChildClassName[] = L"Childclass////";
HINSTANCE g_hInst;
LRESULT CALLBACK ChildProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static BOOL bMouseTracking = FALSE;
switch(msg)
{
case WM_PAINT:
{
HDC hdc;
PAINTSTRUCT ps;
hdc = BeginPaint(hwnd, &ps);
if(hdc)
{
HBRUSH hbr = CreateSolidBrush(bMouseTracking?RGB(255, 0, 0):RGB(0, 0, 255));
if(hbr)
{
FillRect(hdc, &ps.rcPaint, hbr);
DeleteObject(hbr);
}
EndPaint(hwnd, &ps);
}
}
break;
case WM_MOUSEMOVE:
if(!bMouseTracking)
{
TRACKMOUSEEVENT tme = { 0 };
tme.cbSize = sizeof(TRACKMOUSEEVENT);
tme.dwFlags = TME_LEAVE;
tme.hwndTrack = hwnd;
bMouseTracking = TrackMouseEvent(&tme);
InvalidateRect(hwnd, 0, TRUE);
}
break;
case WM_MOUSELEAVE:
bMouseTracking = FALSE;
InvalidateRect(hwnd, 0, TRUE);
break;
case WM_ENABLE:
if(wParam)
{
RECT rc;
if(GetWindowRect(hwnd, &rc))
{
POINT pt;
if(GetCursorPos(&pt))
if(PtInRect(&rc, pt))
{
TRACKMOUSEEVENT tme = { 0 };
tme.cbSize = sizeof(TRACKMOUSEEVENT);
tme.dwFlags = TME_LEAVE;
tme.hwndTrack = hwnd;
//TrackMouseEvent() posts WM_MOUSELEAVE if conditions 1 and 2 are met, even though I'm trying to turn
//mouse tracking on and the cursor is over the child window. It doesn't make sense
//The problems is this piece of code right here /* bMouseTracking = TrackMouseEvent(&tme); */
//It should turn tracking on but it doesn't it cancels it even though WS_DISABLED has already been removed
//at this point
bMouseTracking = TrackMouseEvent(&tme);
InvalidateRect(hwnd, 0, TRUE);
}
}
} else {
if(bMouseTracking) {
////////If you comment everything from here ...
TRACKMOUSEEVENT tme = { 0 };
tme.cbSize = sizeof(TRACKMOUSEEVENT);
tme.dwFlags = TME_LEAVE | TME_CANCEL;
tme.hwndTrack = hwnd;
//if(TrackMouseEvent(&tme)) PostMessage(hwnd, WM_MOUSELEAVE, 0, 0); //Commented this line out to do things a bit differently with the same results
if(TrackMouseEvent(&tme)) { //If this succeeds it means mouse tracking was canceled
bMouseTracking = FALSE;
InvalidateRect(hwnd, 0, TRUE);
}
////////all the way down to here the result is the same
//If you comment everything in this block out then you have another problem which can be tested with this condition:
//With window enabled move mouse over window, then press the ENTER key. The color should change
//from red to blue but it doesn't. It will change to blue though if you move the mouse 1 or more pixels after you've pressed the ENTER key
}
}
break;
case WM_DESTROY:
bMouseTracking = FALSE;
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static HWND hChild;
switch(msg)
{
case WM_CREATE:
hChild = CreateWindowEx(0, g_szChildClassName, 0, WS_VISIBLE | WS_CHILD, 4, 4, 240, 80, hwnd, 0, g_hInst, 0);
break;
case WM_KEYDOWN:
if(wParam == VK_SPACE) EnableWindow(hChild, TRUE);
else if(wParam == VK_RETURN) EnableWindow(hChild, FALSE);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
const TCHAR szClassName[] = L"abccccccc";
WNDCLASSEX wc;
HWND hwnd;
MSG msg;
SecureZeroMemory(&wc, sizeof(WNDCLASSEX));
wc.cbSize = sizeof(WNDCLASSEX);
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hInstance = hInstance;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = szClassName;
if(!RegisterClassEx(&wc)) return 0; //Register main window class
SecureZeroMemory(&wc, sizeof(WNDCLASSEX));
wc.cbSize = sizeof(WNDCLASSEX);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hInstance = hInstance;
wc.lpfnWndProc = ChildProc;
wc.lpszClassName = g_szChildClassName;
if(!RegisterClassEx(&wc)) return 0; //Register child window class
g_hInst = hInstance;
hwnd = CreateWindowEx(0, szClassName, L"Test", WS_OVERLAPPEDWINDOW, 40, 40, 420, 200, 0, 0, hInstance, 0);
if(!hwnd) return 0;
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while(GetMessage(&msg, 0, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
EDIT: You can't see the cursor in the pictures cause I used screen capture and it doesn't capture the cursor. In the first picture the cursor is outside of the child window and in the second picture the cursor is inside of the child window
ENTER key pressed when cursor is outside of child window
SPACE key pressed after the ENTER key was previously pressed and cursor is hovering over child window

I don't know if this is a shortcoming of the documentation or a bug in TrackMouseEvent, but it looks like TrackMouseEvent doesn't expect to be called within your WM_ENABLE handler.
To avoid that, try posting a message from WM_ENABLE and calling TrackMouseEvent from that:
case WM_ENABLE:
PostMessage(hwnd, WM_USER, wParam, lParam);
break;
case WM_USER:
if(wParam)
{
RECT rc;
if(GetWindowRect(hwnd, &rc))
{
POINT pt;
if(GetCursorPos(&pt))
if(PtInRect(&rc, pt))
{
TRACKMOUSEEVENT tme = { 0 };
tme.cbSize = sizeof(TRACKMOUSEEVENT);
tme.dwFlags = TME_LEAVE;
tme.hwndTrack = hwnd;
//TrackMouseEvent() posts WM_MOUSELEAVE if conditions 1 and 2 are met, even though I'm trying to turn
//mouse tracking on and the cursor is over the child window. It doesn't make sense
//The problems is this piece of code right here /* bMouseTracking = TrackMouseEvent(&tme); */
//It should turn tracking on but it doesn't it cancels it even though WS_DISABLED has already been removed
//at this point
bMouseTracking = TrackMouseEvent(&tme);
InvalidateRect(hwnd, 0, TRUE);
}
}
} else {
if(bMouseTracking) {
////////If you comment everything from here ...
TRACKMOUSEEVENT tme = { 0 };
tme.cbSize = sizeof(TRACKMOUSEEVENT);
tme.dwFlags = TME_LEAVE | TME_CANCEL;
tme.hwndTrack = hwnd;
//if(TrackMouseEvent(&tme)) PostMessage(hwnd, WM_MOUSELEAVE, 0, 0); //Commented this line out to do things a bit differently with the same results
if(TrackMouseEvent(&tme)) { //If this succeeds it means mouse tracking was canceled
bMouseTracking = FALSE;
InvalidateRect(hwnd, 0, TRUE);
}
////////all the way down to here the result is the same
//If you comment everything in this block out then you have another problem which can be tested with this condition:
//With window enabled move mouse over window, then press the ENTER key. The color should change
//from red to blue but it doesn't. It will change to blue though if you move the mouse 1 or more pixels after you've pressed the ENTER key
}
}
break;

Related

Win32 - Make part of window a translucent while another part opaque?

I want one part of a window to display an image with a certain opacity while the other part to work as normal. Both parts shouldn't pass clicks through.
I have tried making the main window a certain colour then using SetLayeredWindowAttributes to make that colour transparent(so only the client area is transparent). Then having a child window over it with my translucent image. However the clicks pass through the window(even though I don't have WS_EX_TRANSPARENT). Alphablend doesn't seem to works since the bitmap isn't 32bmp. So now I am trying to use updatelayeredwindows but I am having trouble setting the region to update it.
case WM_CREATE:
hbmp = (HBITMAP)LoadImageA(NULL, "courtyard.bmp", IMAGE_BITMAP, 1920, 1080, LR_LOADFROMFILE);
HDC hdc = CreateCompatibleDC(NULL);
HBITMAP hbmp_old = (HBITMAP)SelectObject(hdc, hbmp);
POINT dcOffset = {0, 0};
SIZE size = {600, 395};
BLENDFUNCTION bf = {AC_SRC_OVER, 0, 100, 0};
RECT wrect;
GetClientRect(hwnd, &wrect);
wrect.top = wrect.top + 43;
UPDATELAYEREDWINDOWINFO info = {sizeof(info), GetDC(NULL), NULL, &size, hdc, &dcOffset, 0, &bf, ULW_ALPHA, &wrect};
UpdateLayeredWindowIndirect(hwnd, &info);
SelectObject(hdc, hbmp_old);
DeleteDC(hdc);
DeleteObject(hbmp);
HWND hbutton = CreateWindowExA(0,
"BUTTON",
"X",
WS_VISIBLE | WS_CHILD | BS_FLAT,
10,
10,
100,
100,
hwnd,
(HMENU)NULL,
NULL,
(LPVOID)NULL);
break;
The parts out of wrect are just a black translucent colour.
Here is my main window:
hwnd = CreateWindowExA(WS_EX_OVERLAPPEDWINDOW | WS_EX_LAYERED,
window_name,
window_title,
WS_OVERLAPPEDWINDOW,
(monitor_dimension.width - window_width) / 2,
(monitor_dimension.height - window_height) / 2 - 75,
window_width,
window_height,
(HWND)NULL,
(HMENU)NULL,
hInstance,
(LPVOID)NULL);
I get the result using SetLayeredWindowAttributes(hWnd, RGB(255, 0, 0), 0, LWA_COLORKEY); without manifest and the clicks don't pass through.
The following code adapted from SetLayeredWindowAttributes() causes mouse clicks to go through after minimizing window | WinAPI.
// WindowsProject2.cpp : Defines the entry point for the application.
//
#include "framework.h"
#include "WindowsProject2.h"
#define MAX_LOADSTRING 100
// 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
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_WINDOWSPROJECT2, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WINDOWSPROJECT2));
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_WINDOWSPROJECT2));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(255, 0, 0));
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_WINDOWSPROJECT2);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
//
// 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,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
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_CREATE:
{
SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
SetLayeredWindowAttributes(hWnd, RGB(255, 0, 0), 0 /*doesn't matter*/, LWA_COLORKEY);
HWND hbutton = CreateWindowExA(0,
"BUTTON",
"X",
WS_VISIBLE | WS_CHILD | BS_FLAT,
10,
10,
100,
100,
hWnd,
(HMENU)NULL,
NULL,
(LPVOID)NULL);
/*HWND hbackground = CreateWindowEx(0,
L"STATIC",
NULL,
WS_CHILD | WS_VISIBLE | SS_BITMAP,
0,
0,
600,
400,
hWnd,
NULL,
NULL,
(LPVOID)NULL);
setImage(hbackground, L"test.bmp", 600, 400);*/
//SetWindowLong(hbackground, GWL_EXSTYLE, GetWindowLong(hbackground, GWL_EXSTYLE) | WS_EX_LAYERED);
//SetLayeredWindowAttributes(hbackground, 0, (255 * 50) / 100, LWA_ALPHA);
}
break;
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
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_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}

How to hide the cursor completely?

When I call ShowCursor(0) (which by the way, does not take an HWND, this is weird), the cursor is hidden, but when I do a special action, for example resizing the window or drag-and-drop a file, the cursor corresponding to this action appears. I don't want to see the cursor whatever action the user is doing. If you wonder why, it's because I'd like to draw it with OpenGL.
I have a popup and layered window:
#define UNICODE
#include <windows.h>
#include <dwmapi.h>
LRESULT CALLBACK wnd_proc(HWND hWnd, UINT uMsg, WPARAM wp, LPARAM lp){
switch(uMsg){
case WM_CREATE: return 0;
case WM_NCHITTEST: return HTBOTTOM;
case WM_DESTROY: PostQuitMessage(0); return 0;
default: return DefWindowProc(hWnd, uMsg, wp, lp);
}
}
HDC hDC; HGLRC hRC;
int main(void){
WNDCLASSEX wcx = {};
wcx.cbSize = sizeof(WNDCLASSEX);
wcx.lpfnWndProc = wnd_proc;
wcx.lpszClassName = L"Win32Class";
wcx.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
wcx.hbrBackground = CreateSolidBrush(0x00000000);
wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
RegisterClassEx(&wcx);
HWND hWnd = CreateWindowEx(WS_EX_ACCEPTFILES | WS_EX_LAYERED, wcx.lpszClassName,
L"Win32Window", WS_POPUP | WS_VISIBLE, 50, 50, 800, 400, NULL, NULL, NULL, NULL);
SetLayeredWindowAttributes(hWnd, RGB(200, 0, 200), 0, LWA_COLORKEY);
ShowCursor(0);
/*DWM_BLURBEHIND blur = {0};
blur.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
blur.fEnable = 1;
blur.hRgnBlur = CreateRectRgn(0, 0, -1, -1);
DwmEnableBlurBehindWindow(hWnd, &blur);*/
MSG msg; while(GetMessage(&msg, NULL, 0, 0)){
DispatchMessage(&msg);
}
}
This is a truncated part of my test file. OpenGL is not included, but I just need to know how to delete the cursor.
If you compile and execute it, you can see that when your cursor is on the window, it is not visible, but when you resize the window the cursor appears, same as when you drag-and-drop a file.

how to handle WM_NCHITTEST in a naked event loop?

I'm playing with Windows API trying to understand how it behaves, and I realized that I can remove WNDPROC altogether and handle things with a naked event loop, like this:
#include <Windows.h>
static struct {
HWND desktop;
HWND window;
} global;
int main(int argc, char **argv)
{
/* anonymous scope: register window class */
{
WNDCLASSEXW wcx;
wcx.cbSize = sizeof(wcx);
wcx.style = CS_HREDRAW | CS_VREDRAW;
wcx.lpfnWndProc = DefWindowProc;
wcx.cbClsExtra = sizeof(void *);
wcx.cbWndExtra = sizeof(void *);
wcx.hInstance = (HINSTANCE)GetModuleHandle(NULL);
wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
wcx.hbrBackground = (HBRUSH)COLOR_WINDOWFRAME;
wcx.lpszMenuName = NULL;
wcx.lpszClassName = L"MyWindow";
wcx.hIconSm = wcx.hIcon;
RegisterClassExW(&wcx);
}
global.desktop = GetDesktopWindow();
global.window = CreateWindowExW (
0,
L"MyWindow",
NULL,
WS_POPUPWINDOW | WS_VISIBLE,
0,
0,
320,
200,
global.desktop,
NULL,
(HINSTANCE)GetModuleHandle(NULL),
NULL
);
/* anonymous scope, event loop */
{
MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
if(msg.hwnd == global.window) {
if (msg.message == WM_PAINT) {
PAINTSTRUCT ps;
HDC hdc;
hdc = BeginPaint(msg.hwnd, &ps);
SelectObject(hdc, GetStockObject(BLACK_BRUSH));
Rectangle(hdc, 0, 0, 320, 200);
EndPaint(msg.hwnd, &ps);
} else {
DispatchMessage(&msg);
}
} else {
DispatchMessage(&msg);
}
}
}
return 0;
}
I wanted to go one step further and try making this window moveable using this technique, and got confused because I can't "return" from a message loop in the way i'm used to(the statement return hit;) does not make sense in this context.
Here is how I started, and got confused:
#include <Windows.h>
static struct {
HWND desktop;
HWND window;
} global;
int main(int argc, char **argv)
{
/* anonymous scope: register window class */
{
WNDCLASSEXW wcx;
wcx.cbSize = sizeof(wcx);
wcx.style = CS_HREDRAW | CS_VREDRAW;
wcx.lpfnWndProc = DefWindowProc;
wcx.cbClsExtra = sizeof(void *);
wcx.cbWndExtra = sizeof(void *);
wcx.hInstance = (HINSTANCE)GetModuleHandle(NULL);
wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
wcx.hbrBackground = (HBRUSH)COLOR_WINDOWFRAME;
wcx.lpszMenuName = NULL;
wcx.lpszClassName = L"MyWindow";
wcx.hIconSm = wcx.hIcon;
RegisterClassExW(&wcx);
}
global.desktop = GetDesktopWindow();
global.window = CreateWindowExW (
0,
L"MyWindow",
NULL,
WS_POPUPWINDOW | WS_VISIBLE,
0,
0,
320,
200,
global.desktop,
NULL,
(HINSTANCE)GetModuleHandle(NULL),
NULL
);
/* anonymous scope, event loop */
{
MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
if(msg.hwnd == global.window) {
if (msg.message == WM_PAINT) {
PAINTSTRUCT ps;
HDC hdc;
hdc = BeginPaint(msg.hwnd, &ps);
SelectObject(hdc, GetStockObject(BLACK_BRUSH));
Rectangle(hdc, 0, 0, 320, 200);
EndPaint(msg.hwnd, &ps);
} else if (msg.message == WM_NCHITTEST) {
LRESULT hit = DefWindowProc(msg.hwnd, msg.message, msg.wParam, msg.lParam);
if (hit == HTCLIENT) {
hit = HTCAPTION;
}
// return hit; // makes no sense here
} else {
DispatchMessage(&msg);
}
} else {
DispatchMessage(&msg);
}
}
}
return 0;
}
How can I simulate returning "hit" from the WM_NCHITTEST condition so that it moves the window like in the solution here: https://stackoverflow.com/a/7773941/2012715 ?
PS: I know that it's better to use a map(like std::unordered_map) rather than a long if/switch for scalability and readability, but I wanted to keep the example more direct.
You can't.
The code you have shown will NEVER work, because (Get|Peek)Message() returns only QUEUED messages. You cannot reply to a queued message, because the sender is not waiting for a reply, it put the message in the queue and moved on to other things. Only NON-QUEUED messages that are sent with the SendMessage...() family of functions can be replied to. (Get|Peek)Message() will NEVER return a sent message, but will internally dispatch it to the target window's message procedure (only messages sent from another thread will be dispatched, messages sent to a window by the same thread that owns the window will bypass the message queue completely).
WM_PAINT is a queued message, so your event loop sees it. But WM_NCHITTEST is not queued, so your message loop will NEVER see it directly, it can only be seen in a message procedure.
What you have shown is NOT the right way to handle a Windows UI message loop. Since you are creating a UI window, you MUST provide it with a message procedure (if not by RegisterClass/Ex(), then by SetWindowLong/Ptr(GWL_WNDPROC) or SetWindowSubclass()). But DO NOT use DefWindowProc() for that procedure if you need to process messages manually. Provide your own message procedure that calls DefWindowProc() (or CallWindowProc() in the case of GWL_WNDPROC, or DefSubclassProc() in the case of SetWindowSubclass()) for any unhandled messages, eg:
LRESULT WINAPI MyWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
SelectObject(hdc, GetStockObject(BLACK_BRUSH));
Rectangle(hdc, 0, 0, 320, 200);
EndPaint(hwnd, &ps);
return 0;
}
case WM_NCHITTEST: {
LRESULT hit = DefWindowProc(hwnd, uMsg, wParam, lParam);
if (hit == HTCLIENT) {
hit = HTCAPTION;
}
return hit;
}
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
int main(int argc, char **argv)
{
/* anonymous scope: register window class */
{
WNDCLASSEXW wcx;
wcx.cbSize = sizeof(wcx);
wcx.style = CS_HREDRAW | CS_VREDRAW;
wcx.lpfnWndProc = MyWndProc; // <--
wcx.cbClsExtra = sizeof(void *);
wcx.cbWndExtra = sizeof(void *);
wcx.hInstance = (HINSTANCE)GetModuleHandle(NULL);
wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
wcx.hbrBackground = (HBRUSH)COLOR_WINDOWFRAME;
wcx.lpszMenuName = NULL;
wcx.lpszClassName = L"MyWindow";
wcx.hIconSm = wcx.hIcon;
RegisterClassExW(&wcx);
}
global.desktop = GetDesktopWindow();
global.window = CreateWindowExW (
0,
L"MyWindow",
NULL,
WS_POPUPWINDOW | WS_VISIBLE,
0,
0,
320,
200,
global.desktop,
NULL,
(HINSTANCE)GetModuleHandle(NULL),
NULL
);
/* anonymous scope, event loop */
{
MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return 0;
}

MSDN (Owner Drawn) Listbox control freeze after scrolling

I'm working on an Owner-Drawn Listbox Control that i managed to create and populate without errors.
Here's my problem :
When i scroll it, the listbox and its parent window becomes unresponsive after scrolling for a few seconds.(with PgDown)
Note that :
There's a lot of items in it (more than 4k)
The messages are still being processed, i can monitor them on the console and they are being sent and received. Only difference is, WM_DRAWITEM is no longer sent...
The items of the listbox are added via LB_ADDSTRING
What i tried :
Using the PeekMessage function instead of the GetMessage
-> Program crashes after the list is filled
Redrawing the windows after the problem occurs (via a WM_LDOUBLECLICK event for example)
-> No effets
Code snippets :
Window Procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HBRUSH Brush;
HBRUSH BLANK_BRUSH;
HBRUSH BLUE_BRUSH;
Brush = CreateSolidBrush(RGB(163, 255, 249));
BLANK_BRUSH = CreateSolidBrush(RGB(255,255, 255));
BLUE_BRUSH = CreateSolidBrush(RGB(0,0, 255));
int tabs[13]={140,256,261,287,318,353,422,460,500,530,550,570,610};
switch(msg)
{
HDC hdc ;
PAINTSTRUCT ps ;
PMEASUREITEMSTRUCT pmis;
LPDRAWITEMSTRUCT Item;
RECT rect ;
rect.top=-50;
rect.bottom=0;
RECT rect2 ;
rect.top=10;
case WM_MEASUREITEM:
pmis = (PMEASUREITEMSTRUCT) lParam;
pmis->itemHeight = 17;
return TRUE;
break;
case WM_DRAWITEM:
Item = (LPDRAWITEMSTRUCT)lParam;
if (Item->itemState & ODS_FOCUS)
{
SetTextColor(Item->hDC, RGB(255,255,255));
SetBkColor(Item->hDC, RGB(0, 0, 255));
FillRect(Item->hDC, &Item->rcItem, (HBRUSH)BLUE_BRUSH);
}
else
{
SetTextColor(Item->hDC, RGB(0,0,0));
SetBkColor(Item->hDC, RGB(255, 255, 255));
FillRect(Item->hDC, &Item->rcItem, (HBRUSH)BLANK_BRUSH);
}
int len = SendMessage(Item->hwndItem , LB_GETTEXTLEN, (WPARAM)Item->itemID, 0);
if (len > 0)
{
LPCTSTR lpBuff = malloc((len+1)*sizeof(TCHAR));
len = SendMessage(Item->hwndItem , LB_GETTEXT, (WPARAM)Item->itemID, (LPARAM)lpBuff);
if (len > 0) {
TabbedTextOut(Item->hDC,Item->rcItem.left, Item->rcItem.top,(LPARAM)lpBuff,len,13,&tabs,140);
}
}
return TRUE;
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_PAINT:
hdc = BeginPaint (hwnd, &ps) ;
GetClientRect(hwnd, &rect);
SetBkColor(hdc, RGB(230,50,2));
SetBkMode(hdc,TRANSPARENT);
FillRect(hdc,&ps.rcPaint,Brush);
DrawText(hdc, TEXT("Liste des messages décodés: "), -1, &rect, DT_CENTER );
DrawText(hdc, TEXT("Numéro d'engin (4xxxy): "), -1, &rect, DT_LEFT );
EndPaint (hwnd, &ps);
return 0 ;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_ERASEBKGND:
return TRUE;
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
Message Loop
BOOL bRet;
while (1)
{
bRet = GetMessage(&Msg, NULL, 0, 0);
if (bRet > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
else if (bRet == 0){
break;
}
else
{
printf("error\n");
return -1;
}
}
return Msg.wParam;
Window creation
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszMenuName = NULL;
wc.lpszClassName = g_szClassName;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
RegisterClassEx(&wc);
hwnd = CreateWindowEx(WS_EX_CLIENTEDGE,g_szClassName,"List of messages",WS_OVERLAPPEDWINDOW,0, 0, 1500, 1000,NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
if(hwnd == NULL)
{
return 0;
}
hwnd2 = CreateWindowEx(0,"LISTBOX" ,"Data",WS_CHILD |WS_HSCROLL |WS_VSCROLL |WS_BORDER|WS_THICKFRAME | LBS_USETABSTOPS | LBS_OWNERDRAWFIXED | LBS_NOTIFY | LBS_HASSTRINGS,15,70,1450,450,hwnd,(HMENU) NULL,hInstance,NULL);
It looks like there's a timer somewhere, and if a keyboard touch stays too long down, it somehow messes everything up...
Has someone encountered a problem like this before or could help me understand what is going on ?
You have a significant GDI resource leak in your code.
At the top of your WndProc function you're creating three brushes, and you never delete them. These brushes are created every time your window processes a message.
You should only create the brushes when you actually need them for painting, and call DeleteObject to release them when you're done with them.

How to make window non transparent?

I want to make parent window as non transparent with RGB value as (99,99,99)? Previously my window was transparent but now i have requirement to make window as non transparent.
Mentioned below are the function related to my parent window:
ATOM MyRegisterClass(HINSTANCE hInstance)
{
LogEntry(L"Entered in myRegisterClass Function");
WNDCLASS CLASS_NAME_ONE_SEG_APP;
CLASS_NAME_ONE_SEG_APP.cbClsExtra = 0;
CLASS_NAME_ONE_SEG_APP.cbWndExtra = 0;
CLASS_NAME_ONE_SEG_APP.hbrBackground = 0;
CLASS_NAME_ONE_SEG_APP.hCursor = 0;
CLASS_NAME_ONE_SEG_APP.hIcon = 0;
CLASS_NAME_ONE_SEG_APP.hInstance = hInstance;
CLASS_NAME_ONE_SEG_APP.lpfnWndProc = (WNDPROC) WndProc;
CLASS_NAME_ONE_SEG_APP.lpszClassName = className;
CLASS_NAME_ONE_SEG_APP.lpszMenuName = 0;
CLASS_NAME_ONE_SEG_APP.style = 0;
LogEntry(L"Exiting from myRegisterClass Function");
return RegisterClass(&CLASS_NAME_ONE_SEG_APP);
}
Mentioned below is an InitInstance function in which i am creating the parent window.
handles.parent is my parent window.
bool WINAPI InitInstance(HINSTANCE hInstance, int nCmdShow)
{
LogEntry(L"Entered in InitInstance Function");
handles.parent = CreateWindowEx(0,
className,
windowName,
WS_VISIBLE | WS_POPUP,
0, 0,
coordinates.width, coordinates.height,
NULL,
NULL,
hInstance,
NULL);
if(handles.parent == NULL)
{
LogValue(L"Cannot Create Parent Window"
L"\nInitInstance Function terminated abnormally");
return false;
}
else
{
UpdateWindow(handles.parent);
ShowWindow(handles.parent, nCmdShow);
LogEntry(L"Exiting from InitInstance Function");
return true;
}
}
The below mentioned is a function for WM_PAINT:
case WM_PAINT:
LogEntry(L"Entred in WM_PAINT");
PaintWindow();
SetFocus(handles.parent);
LogEntry(L"Exited from WM_PAINT");
break;
This PaintWindow does the following.....
void PaintWindow()
{
LogEntry(L"Entered in PaintWindow Function");
HWND *handle = &handles.volUp;
//Paint Buttons on the window
for(register char i = MIN_BUTTON; i <= MAX_BUTTON; i++)
{
PaintButton( (INITIAL_BUTTON + i) , *handle, btns, i);
handle++;
}
//Paint the AXIS_VOL_ON_OFF Button According to its Status
if(volumeStatus.status == VOLUME_ON)
PaintButton(IDB_BTN_VOL_OFF, handles.volOnOff, btns, AXIS_VOL_ON_OFF);
else if(volumeStatus.status = VOLUME_MUTE)
PaintButton(IDB_BTN_VOL_ON, handles.volOnOff, btns, AXIS_VOL_ON_OFF);
//Paint Images on the window
if(handles.screenMode == SCREEN_MODE_OPERATION)
InsertImages();
LogEntry(L"Exited from PaintWindow Function");
}
Thanks in advance......
You need to give your class a non-null background brush
CLASS_NAME_ONE_SEG_APP.hbrBackground = CreateSolidBrush(RGB((99,99,99));
And in your WindowProc, you need to make sure that you pass the WM_ERASEBKGND message to DefWindowProc. (that part is probably already happening)
Later
Ok, your WM_PAINT code is doing some things wrong. It's important that you call BeginPaint and EndPaint when you handle the WM_PAINT message, and you have to use the HDC that you get from BeginPaint when you draw.
case WM_PAINT:
{
LogEntry(L"Entred in WM_PAINT");
PAINTSTRUCT ps;
HDC hdc = BeginPaint(&ps);
PaintWindow(hdc);
EndPaint(hwnd, &ps);
LogEntry(L"Exited from WM_PAINT");
}
break;
Your window is transparent because you never call BeginPaint in your WM_PAINT handler, so you never end up drawing anything to the screen.
See Drawing in the Client Area for a more complete example.

Resources