How to make window non transparent? - c

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.

Related

Custom WM_APP message isn't posted in the message queue

I am trying to send a custom message (WM_APP + 1) when WM_SIZE is send to the window procedure. I want to be able to catch it from an other function using PeekMessage and do somethings. But when I test it the messages seem to not be send to the queue. Adding some printf statements shows me that it goes to the window procedure. The weird thing is that when I step through the code in the debugger it works fine but when I'm running normally, it goes back to not working.
Working example program with the problem, resize the window to test:
#include <windows.h>
#include <stdio.h>
#pragma comment(lib, "user32.lib")
#define OBG_EVENT_QUIT 0
#define OBG_EVENT_RESIZE 1
#define OBG_EVENT_NO -1
#define OBG_EVENT_UNKNOWN -2
//user defined event
#define OBG_WM_RESIZE (WM_APP + 1)
typedef union
{
int type;
struct
{
int type;
int width;
int height;
} resizeEvent;
} obg_event;
LRESULT CALLBACK obgpf_DefaultWindowCallback(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
{
LRESULT result = 0;
switch(message)
{
case WM_CLOSE:
{
PostMessageA(window, WM_QUIT, 0, 0);
} break;
//this should be handled by OBGGetEvent
case OBG_WM_RESIZE:
{
printf("MESSAGE WENT THROUGH. DON'T WANT THIS\n");
} break;
case WM_SIZE:
{
PostMessageA(window, OBG_WM_RESIZE, wParam, lParam);
} break;
default:
{
result = DefWindowProc(window, message, wParam, lParam);
} break;
}
return result;
}
int OBGGetEvent(obg_event *event)
{
int moreMessages = 0;
MSG message;
if(PeekMessage(&message, 0, 0, 0, PM_REMOVE))
{
moreMessages = 1;
switch(message.message)
{
case WM_QUIT:
{
event->type = OBG_EVENT_QUIT;
} break;
case OBG_WM_RESIZE:
{
event->type = OBG_EVENT_RESIZE;
event->resizeEvent.type = OBG_EVENT_RESIZE;
event->resizeEvent.width = LOWORD(message.lParam);
event->resizeEvent.height = HIWORD(message.lParam);
} break;
default:
{
event->type = OBG_EVENT_UNKNOWN;
TranslateMessage(&message);
DispatchMessage(&message);
} break;
}
}
else
{
event->type = OBG_EVENT_NO;
}
return moreMessages;
}
int main()
{
HINSTANCE instance = GetModuleHandleA(0);
WNDCLASSEX windowClass = {0};
windowClass.cbSize = sizeof(windowClass);
windowClass.style = CS_HREDRAW | CS_VREDRAW;
windowClass.lpfnWndProc = obgpf_DefaultWindowCallback;
windowClass.hInstance = instance;
windowClass.lpszClassName = "testClass";
windowClass.hIcon = LoadIcon(0, IDI_APPLICATION);
windowClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
windowClass.hIconSm = LoadIcon(0, IDI_APPLICATION);
windowClass.hCursor = LoadCursorA(0, IDC_ARROW);
HWND window;
if(RegisterClassEx(&windowClass))
{
window = CreateWindowEx(0,
windowClass.lpszClassName,
"test window",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT,
CW_USEDEFAULT,
500,
300,
0,
0,
instance,
0);
if(window)
{
int appIsRunning = 1;
obg_event event = {0};
event.type = -1;
while(appIsRunning)
{
while(OBGGetEvent(&event))
{
if(event.type == OBG_EVENT_QUIT)
{
printf("event quit\n");
appIsRunning = 0;
break;
}
else if(event.type == OBG_EVENT_RESIZE)
{
printf("window resized: width %d height %d\n", event.resizeEvent.width, event.resizeEvent.height);
}
}
Sleep(33);
}
}
else
{
printf("window error\n");
}
}
else
{
printf("windowClass error\n");
}
return 0;
}
I tried doing this with SendMessage instead of PeekMessage but the same thing happened. Not sure what I'm missing or misunderstanding but any help is appreciated!
EDIT: added a complete working program that reproduces the problem
Thank you for the example code.
The behaviour you're seeing is because when the window is being resized using the mouse, the OS enters a modal message loop to process mouse input. During this loop, your message loop doesn't run - which means your PeekMessage() and the special message handling doesn't run either. Instead, messages are simply dispatched as normal to your window procedure.
There are two solutions that come to mind immediately but how you deal with this really depends on the design of your program and why you want to process size events in this way.
The first idea I had is to keep track of whether you're in a modal sizing loop or not, and defer posting the notification message until the loop is finished. An example of how to do that using your provided window procedure is below.
The second solution is to simply call your resize event handler directly whenever you get WM_SIZE (or, if you must go through the event system, put the handler for it in the window procedure rather than using PostMessage).
Example code for the first suggestion:
LRESULT CALLBACK obgpf_DefaultWindowCallback(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
{
LRESULT result = 0;
static bool fResized = false;
static bool fInSizeLoop = false;
switch(message)
{
case WM_CLOSE:
{
PostMessageA(window, WM_QUIT, 0, 0);
} break;
//this should be handled by OBGGetEvent
case OBG_WM_RESIZE:
{
printf("MESSAGE WENT THROUGH. DON'T WANT THIS\n");
} break;
case WM_SIZE:
{
if (fInSizeLoop) // in modal size loop, defer notification
fResized = true;
else
PostMessageA(window, OBG_WM_RESIZE, wParam, lParam);
} break;
case WM_ENTERSIZEMOVE:
fInSizeLoop = true; // begin modal size loop
break;
case WM_EXITSIZEMOVE:
fInSizeLoop = false; // left modal size loop
// post resize notification now
if (fResized) {
RECT rc;
GetClientRect(window, &rc);
PostMessageA(window, OBG_WM_RESIZE, 0, MAKELPARAM(rc.right - rc.left, rc.bottom - rc.top));
fResized = false;
}
break;
default:
{
result = DefWindowProc(window, message, wParam, lParam);
} break;
}
return result;
}
(sorry, this is C++ code and I just noticed you had tagged as C - but the principle is the same).

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.

Why do I get a WM_MOUSELEAVE after enabling window?

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;

Drawing text on a custom button in windows api

i'm trying to make a custom windows 8 style button using windows C API. however when i try to draw the text on the button after i redraw the button nothing visible happens!
I even tried creating a static window as a child of the button and sending WS_ERASEBKGND message to it when i redraw the button but as soon as i redraw the button for the first time it disappears!
here is the window procedure for my custom button:
LRESULT CALLBACK ButtonWindowProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
//static HWND static_text_handle;
HDC hdc;
PAINTSTRUCT ps;
DWORD color;
HFONT font,holdFont;
static RECT rect;
wchar_t test[] = L"test";
TRACKMOUSEEVENT tme = {sizeof(TRACKMOUSEEVENT),TME_LEAVE,hwnd,HOVER_DEFAULT};
/*static*/ HBRUSH brush = CreateSolidBrush(RGB(20,30,40));
/*static*/ HBRUSH clicked_brush = CreateSolidBrush(RGB(40,50,60));
/*static*/ HBRUSH hover_brush = CreateSolidBrush(RGB(70,80,90));
//TrackMouseEvent(&tme);
switch(msg)
{
case WM_CREATE:
//static_text_handle = CreateWindowW(L"Static",L"test",WS_CHILD | WS_VISIBLE,0,0,20,20,hwnd,NULL,NULL,NULL);
//brush = ((struct custom_button_colors*)(lParam))->default_color;
//clicked_brush = ((struct custom_button_colors*)(lParam))->push_color;
//hover_brush = ((struct custom_button_colors*)(lParam))->hover_color;
break;
case WM_LBUTTONDOWN:
SendMessage(hwnd,WM_ERASEBKGND,(WPARAM)GetDC(hwnd),(LPARAM)LPARAM_L_DOWN);
break;
case WM_LBUTTONUP:
SendMessage(hwnd,WM_ERASEBKGND,(WPARAM)GetDC(hwnd),(LPARAM)LPARAM_L_UP);
break;
case WM_MOUSEMOVE:
//Beep(1000,1000);
TrackMouseEvent(&tme);
if (GetAsyncKeyState(VK_LBUTTON))
{
SendMessage(hwnd,WM_ERASEBKGND,(WPARAM)GetDC(hwnd),(LPARAM)LPARAM_L_DOWN);
}
else
{
SendMessage(hwnd,WM_ERASEBKGND,(WPARAM)GetDC(hwnd),(LPARAM)LPARAM_MOUSEHOVER);
}
break;
case WM_MOUSELEAVE:
SendMessage(hwnd,WM_ERASEBKGND,(WPARAM)GetDC(hwnd),(LPARAM)-1);
break;
case WM_ERASEBKGND:
GetClientRect(hwnd,&rect);
if (lParam == LPARAM_L_DOWN)
{
FillRect((HDC)wParam,&rect,clicked_brush);
}
else if (lParam == LPARAM_MOUSEHOVER)
{
FillRect((HDC)wParam,&rect,hover_brush);
break;
}
else
{
POINT mousepos;
GetCursorPos(&mousepos);
if (WindowFromPoint(mousepos) == hwnd)
{
FillRect((HDC)wParam,&rect,hover_brush);
}
else
{
FillRect((HDC)wParam,&rect,brush);
}
}
hdc = BeginPaint(hwnd,&ps);
color = GetSysColor(COLOR_BTNFACE);
SetBkColor(hdc,color);
font = CreateFontW(25, 0, 0, 0, FW_MEDIUM, 0, 0, 0, 0,0, 0, ANTIALIASED_QUALITY, 0, L"Tahoma");
holdFont = SelectObject(hdc, font);
TextOutW(hdc,200,200,test,lstrlenW(test));
SelectObject(hdc,holdFont);
DeleteObject(font);
EndPaint(hwnd,&ps);
break;
default:
return DefWindowProc(hwnd,msg,wParam,lParam);
}
return 0;
}
I'm an absolute beginner in windows programming so sorry if my question is trivial.
1) Do not use BeginPaint/EndPaint in WM_ERASEBKGND. Move that code in a WM_PAINT handler.
2) Use SetWindowLong and the GWL_USERDATA index for storing the button state (UP or DOWN) and use GetWindowLong for retrieving the current state.
3) Use SetCapture for capturing the mouse, starting with WM_LBUTTONDOWN. Update the state of the button (UP or DOWN) according to mouse position
4) Use RedrawWindow when you need painting to occur

Resources