MDI child window and status bar C win32 API [closed] - c

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
I am creating an MDI Child Window after selecting "File -> Open" Menu.
In the "Open" switch statement I have the window creation function
HWND hwndChild = CreateWindowEx(WS_EX_MDICHILD, childClass, "MDI child", WS_CLIPCHILDREN, 0, 0, 400, 300, hWndClient, MAKEINTRESOURCE(IDM_FIRSTCHILD), GetModuleHandle(NULL), 0);
After that declaration I expected the child MDI window to show but nothing happens. The window handle hwndChild is also different from NULL and the error MessageBox doesn't show.
It seems that the error is in handling the WM_SIZE message inside the WndProc window function.
One solution is to put
return DefFrameProc(hwnd, hWndClient, msg, wParam, lParam);
instead of
return 0;
at the end of WndProc. But this solution is not compatible with the status bar, as it then disappears.
Here is the complete code:
#include <windows.h>
#include <commctrl.h> //per SBARS_SIZEGRIP e STATUSCLASSNAME
#define IDM_OPEN 100
#define IDM_EXIT 101
#define IDM_ABOUT 102
#define IDC_STATUSBAR 103
#define IDM_ARRANGE 104
#define IDM_CASCADE 105
#define IDM_TILE 106
#define IDM_CLOSEALL 107
#define IDM_CLOSE 108
#define ID_CLIENTWND 109
#define IDM_FIRSTCHILD 5000
const char frameClass[] = "myFrameClass";
const char clientClass[] = "mdiclient"; //funziona solo con "mdiclient"
const char childClass[] = "myChildClass";
HWND hWndStatusBar;
HWND hWndClient;
//STEP 5 Window Procedure
LRESULT CALLBACK WndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam){
switch (msg){
case WM_CREATE:
//STEP 6: Create Status Bar
hWndStatusBar = CreateWindowEx(0,STATUSCLASSNAME,NULL,
WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP,
0, 0, 0, 0,
hwnd,
(HMENU)IDC_STATUSBAR,
(HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE), NULL);
if(!hWndStatusBar){
MessageBox(NULL, "Failed To Create The Status Bar", "Error", MB_OK | MB_ICONERROR);
return 0;
}
// Establish the number of partitions or 'parts' the status bar will
// have, their actual dimensions will be set in the parent window's
// WM_SIZE handler.
RECT rc;
GetClientRect(hwnd, &rc);
int nHalf = rc.right / 2;
int nParts[4] = { nHalf, nHalf + nHalf / 3, nHalf + nHalf * 2 / 3, -1 };
SendMessage(hWndStatusBar, SB_SETPARTS, 4, (LPARAM)&nParts);
//crea la finestra CLIENT
CLIENTCREATESTRUCT ccs;
// Assign the 'Window' menu.
ccs.hWindowMenu = GetSubMenu(GetMenu(hwnd), 1); // uno perchè così la finestra appare nel secondo menu (partendo da zero) ovvero nel menu 'Windows'
ccs.idFirstChild = IDM_FIRSTCHILD;
// Create the client window. (quella che contiene i child!!)
hWndClient = CreateWindowEx(WS_EX_CLIENTEDGE, clientClass,//FUNZIONA SOLO CON "mdiclient"
NULL,
WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE,
0, 0, 0, 0,
hwnd,(HMENU)ID_CLIENTWND/*GetMenu(hwnd)*/,GetModuleHandle(NULL),&ccs);
if(hWndClient == NULL) {
MessageBox(NULL, "Client Window Creation Failed!","Error!",MB_ICONEXCLAMATION|MB_OK);
return 0;
}
break;
case WM_SIZE:
//resize the parts
GetClientRect(hwnd, &rc);
nHalf = rc.right / 2;
nParts[0] = nHalf; nParts[1] = nHalf + nHalf / 3; nParts[2] = nHalf + nHalf * 2 / 3; nParts[3] = -1;
SendMessage(hWndStatusBar, SB_SETPARTS, 4, (LPARAM)&nParts);
//resize the statusbar
SendMessage(GetDlgItem(hwnd, IDC_STATUSBAR), WM_SIZE,0,0);
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_COMMAND:
if(LOWORD(wParam) == IDM_EXIT) DestroyWindow(hwnd);
if(LOWORD(wParam) == IDM_ABOUT) MessageBox(hwnd,"ABOUT premuto","ciao",MB_OK);
if(LOWORD(wParam) == IDM_OPEN){
//apre il file...
//TODO
//crea la finestra CHILD
HWND hwndChild = CreateWindowEx(WS_EX_MDICHILD, //extended styles
childClass, //control 'class' name
"MDI child", //control caption
WS_CLIPCHILDREN, //control style
0, //position: left
0, //position: top
400, //width
300, //height
hWndClient, //parent window handle
//control's ID
MAKEINTRESOURCE(IDM_FIRSTCHILD),
GetModuleHandle(NULL), //application instance
0); //user defined info
if(hwndChild == NULL){
MessageBox(hwnd,"Impossibile creare la finestra Child","Errore",MB_ICONERROR);
}
}
break;
default:
//return DefWindowProc(hwnd,msg,wParam,lParam);
return DefFrameProc(hwnd, hWndClient, msg, wParam, lParam); //per tenere conto delle finestre figlie
}
return 0;
}
LRESULT CALLBACK ChildWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
switch(msg) {
case WM_CREATE :
// LPCREATESTRUCT lpCreateStruct = (LPCREATESTRUCT)lParam;
// LPMDICREATESTRUCT lpMDICreateStruct = (LPMDICREATESTRUCT)lpCreateStruct->lpCreateParams;
// FrameParam = lpMDICreateStruct->lParam;
break;
case WM_COMMAND:
break;
/* All other messages (a lot of them) are processed using default procedures */
default:
return DefMDIChildProc(hwnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)
{
WNDCLASSEX wc;
HWND hwnd;
MSG msg;
//STEP 1: Registering Frame Class Window
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;;
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_APPWORKSPACE+1);//(COLOR_WINDOW);
wc.hIconSm = LoadIcon(NULL,IDI_APPLICATION);
wc.lpszMenuName = NULL;
wc.lpszClassName = frameClass;
if(!RegisterClassEx(&wc)){
MessageBox(NULL,"Frame Window Registration Failed!","Error!",MB_ICONEXCLAMATION);
return 0;
}
//la classe CLIENT non la devo registrare?
//STEP 1.2: Registering Child Class Window
wc.lpfnWndProc = (WNDPROC)ChildWndProc;
wc.hIcon = (HICON)LoadImage(0,IDI_APPLICATION,IMAGE_ICON,0,0,LR_SHARED);
wc.hIconSm = LoadImage(hInstance,childClass,IMAGE_ICON,16,16,LR_DEFAULTCOLOR);
wc.hCursor = (HCURSOR)LoadImage(0,IDC_ARROW,IMAGE_CURSOR,0,0,LR_SHARED);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);//(COLOR_WINDOW);
wc.lpszMenuName = NULL;
wc.lpszClassName = childClass;
if(!RegisterClassEx(&wc)){
MessageBox(NULL,"Child Window Registration Failed!","Error!",MB_ICONEXCLAMATION);
return 0;
}
//STEP 2: Creating the Window
hwnd = CreateWindowEx(WS_EX_CLIENTEDGE,
frameClass,
"046 - Visualizer",
WS_VISIBLE|WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,CW_USEDEFAULT,600,400,
NULL,NULL,hInstance,NULL);
if(hwnd == NULL){
MessageBox(NULL,"Frame Window Creation Failed!","Error!",MB_ICONEXCLAMATION);
return 0;
}
ShowWindow(hwnd,nCmdShow);
UpdateWindow(hwnd);
//STEP 3: Create Menu
HMENU hMenu = CreateMenu(); //crea un menu vuoto
HMENU hSubMenu; //variabile usata per aggiungere sottomenu ti tipo POPUP
hSubMenu = CreatePopupMenu();
AppendMenu(hSubMenu,MF_STRING,IDM_OPEN,"&Open");
AppendMenu(hSubMenu,MF_STRING,IDM_CLOSE,"&Close");
AppendMenu(hSubMenu, MF_SEPARATOR, 0, NULL);
AppendMenu(hSubMenu,MF_STRING,IDM_EXIT,"&Exit");
AppendMenu(hMenu,MF_STRING|MF_POPUP,(UINT)hSubMenu,"&File"); //aggiunge il sottomenu al menu
hSubMenu = CreatePopupMenu();
AppendMenu(hSubMenu,MF_STRING,IDM_ARRANGE,"Arrange");
AppendMenu(hSubMenu,MF_STRING,IDM_CASCADE,"Cascade");
AppendMenu(hSubMenu,MF_STRING,IDM_TILE,"Tile");
AppendMenu(hSubMenu,MF_STRING,IDM_CLOSEALL,"Close All");
AppendMenu(hMenu,MF_STRING|MF_POPUP,(UINT)hSubMenu,"&Windows");
hSubMenu = CreatePopupMenu();
AppendMenu(hSubMenu,MF_STRING,IDM_ABOUT,"&About");
AppendMenu(hMenu,MF_STRING|MF_POPUP,(UINT)hSubMenu,"&Help");
if(hMenu == NULL){
MessageBox(NULL,"Menu Creation Failed!","Error!",MB_ICONEXCLAMATION);
return 0;
}
SetMenu(hwnd,hMenu); //setta il menu sulla finestra
//STEP 4: Message Loop
while(GetMessage(&msg,NULL,0,0) > 0){
if(hWndClient && TranslateMDISysAccel(hWndClient,&msg)) continue; //process the predefined MDI specific accelerator keys
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}

To handle WM_SIZE in MDI frame, see documentation for DefFrameProc
WM_SIZE
Resizes the MDI client window to fit in the new frame window's client
area. If the frame window procedure sizes the MDI client window to a
different size, it should not pass the message to the DefWindowProc
function.
If you are resizing the frame's client are, return zero. If you are not resizing then pass to DefWindowProc (not DefFrameProc)
To create a MDI child window, use MDICREATESTRUCT and SendMessage( ... WM_MDICREATE ... ), instead of CreateWindowEx(WS_EX_MDICHILD...
Suggestion to fix the procedure:
I am using a slightly different method to handle defaults. Also IDM_EXIT should call WM_CLOSE
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static MDICREATESTRUCT mdicreate;
static HINSTANCE hinstance;
switch(msg)
{
case WM_CREATE:
{
hinstance = (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE);
CLIENTCREATESTRUCT ccs = { 0 };
ccs.hWindowMenu = GetSubMenu(GetMenu(hwnd), 1);
ccs.idFirstChild = IDM_FIRSTCHILD;
hWndClient = CreateWindow("MDICLIENT", "",
WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE,
0, 0, 0, 0, hwnd, NULL, 0, &ccs);
CreateWindow(STATUSCLASSNAME, NULL, WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP,
0, 0, 0, 0, hwnd, (HMENU)IDC_STATUSBAR, hinstance, NULL);
return 0;
}
case WM_SIZE:
{
RECT rc;
GetClientRect(hwnd, &rc);
int nHalf = rc.right / 2;
int nParts[4];
nParts[0] = nHalf;
nParts[1] = nHalf + nHalf / 3;
nParts[2] = nHalf + nHalf * 2 / 3;
nParts[3] = -1;
SendMessage(GetDlgItem(hwnd, IDC_STATUSBAR), SB_SETPARTS, 4, (LPARAM)&nParts);
SendMessage(GetDlgItem(hwnd, IDC_STATUSBAR), WM_SIZE, 0, 0);
RECT statusRect;
GetClientRect(GetDlgItem(hwnd, IDC_STATUSBAR), &statusRect);
rc.bottom -= statusRect.bottom;
MoveWindow(hWndClient, 0, 0, rc.right, rc.bottom, TRUE);
return 0;
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_COMMAND:
if(LOWORD(wParam) == IDM_EXIT)
{
SendMessage(hwnd, WM_CLOSE, 0, 0);
return 0;
}
if(LOWORD(wParam) == IDM_ABOUT)
MessageBox(hwnd, "ABOUT premuto", "ciao", MB_OK);
if(LOWORD(wParam) == IDM_OPEN)
{
mdicreate.szClass = childClass;
mdicreate.szTitle = "Title";
mdicreate.hOwner = hinstance;
mdicreate.x = CW_USEDEFAULT; //change this to a small number
mdicreate.y = CW_USEDEFAULT;
mdicreate.cx = CW_USEDEFAULT;
mdicreate.cy = CW_USEDEFAULT;
mdicreate.style = 0;
mdicreate.lParam = 0;
SendMessage(hWndClient, WM_MDICREATE, 0, (LPARAM)
(LPMDICREATESTRUCT)&mdicreate);
}
break;
}
return DefFrameProc(hwnd, hWndClient, msg, wParam, lParam);
}

I have solved sizing the MDI client area in WM_SIZE routine.
I'm posting the answer and don't delete the question because it may help someone else with my problem in future.
I have put
return DefFrameProc(hwnd, hWndClient, msg, wParam, lParam);
at the end of WndProc and added the code for resizing MDI client window that calculate also the presence of the status bar:
// get client area of frame window
RECT clientRect;
GetClientRect(hwnd, &clientRect);
int frame_dx = clientRect.right - clientRect.left;
int frame_dy = clientRect.bottom - clientRect.top;
// get client area of status bar
RECT statusRect;
GetClientRect(hWndStatusBar, &statusRect);
int status_dy = statusRect.bottom - statusRect.top;
// position the MDIClient such that it doesn't overlap with status bar
MoveWindow(hWndClient, 0, 0, frame_dx, frame_dy - status_dy, TRUE);
//resize the statusbar
SendMessage(GetDlgItem(hwnd, IDC_STATUSBAR), WM_SIZE,0,0);
return 0;
At the end I have put return 0; because the WndProc have not to return DefFrameProc and automatically size MDI client over the whole client area, and also over the status bar making it disappear.
I post the whole corrected source code here:
#include <windows.h>
#include <commctrl.h> //per SBARS_SIZEGRIP e STATUSCLASSNAME
#define IDM_OPEN 100
#define IDM_EXIT 101
#define IDM_ABOUT 102
#define IDC_STATUSBAR 103
#define IDM_ARRANGE 104
#define IDM_CASCADE 105
#define IDM_TILE 106
#define IDM_CLOSEALL 107
#define IDM_CLOSE 108
#define ID_CLIENTWND 109
#define IDM_FIRSTCHILD 5000
const char frameClass[] = "myFrameClass";
const char clientClass[] = "mdiclient"; //funziona solo con "mdiclient"
const char childClass[] = "myChildClass";
HWND hWndStatusBar = NULL;
HWND hWndClient = NULL;
//STEP 5 Window Procedure
LRESULT CALLBACK WndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam){
switch (msg){
case WM_CREATE:
{
//STEP 6: Create Status Bar
hWndStatusBar = CreateWindowEx(0,STATUSCLASSNAME,NULL,
WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP,
0, 0, 0, 0,
hwnd,
(HMENU)IDC_STATUSBAR,
(HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE), NULL);
if(!hWndStatusBar){
MessageBox(NULL, "Failed To Create The Status Bar", "Error", MB_OK | MB_ICONERROR);
return 0;
}
// Establish the number of partitions or 'parts' the status bar will
// have, their actual dimensions will be set in the parent window's
// WM_SIZE handler.
RECT rc;
GetClientRect(hwnd, &rc);
int nHalf = rc.right / 2;
int nParts[4] = { nHalf, nHalf + nHalf / 3, nHalf + nHalf * 2 / 3, -1 };
SendMessage(hWndStatusBar, SB_SETPARTS, 4, (LPARAM)&nParts);
//crea la finestra CLIENT
CLIENTCREATESTRUCT ccs;
// Assign the 'Window' menu.
ccs.hWindowMenu = GetSubMenu(GetMenu(hwnd), 1); // uno perchè così la finestra appare nel secondo menu (partendo da zero) ovvero nel menu 'Windows'
ccs.idFirstChild = IDM_FIRSTCHILD;
// Create the client window. (quella che contiene i child!!)
hWndClient = CreateWindowEx(WS_EX_CLIENTEDGE, clientClass,//FUNZIONA SOLO CON "mdiclient"
NULL,
WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE,
0, 0, 0, 0,
hwnd,(HMENU)ID_CLIENTWND/*GetMenu(hwnd)*/,GetModuleHandle(NULL),&ccs);
if(hWndClient == NULL) {
MessageBox(NULL, "Client Window Creation Failed!","Error!",MB_ICONEXCLAMATION|MB_OK);
return 0;
}
break;
}
case WM_SIZE:
{
//resize the parts
RECT rc;
GetClientRect(hwnd, &rc);
int nHalf = rc.right / 2;
int nParts[4] = { nHalf, nHalf + nHalf / 3, nHalf + nHalf * 2 / 3, -1 };
SendMessage(hWndStatusBar, SB_SETPARTS, 4, (LPARAM)&nParts);
//resize MDI
// get client area of frame window
RECT clientRect;
GetClientRect(hwnd, &clientRect);
int frame_dx = clientRect.right - clientRect.left;
int frame_dy = clientRect.bottom - clientRect.top;
// get client area of status bar
RECT statusRect;
GetClientRect(hWndStatusBar, &statusRect);
int status_dy = statusRect.bottom - statusRect.top;
// position the MDIClient such that it doesn't overlap with status bar
MoveWindow(hWndClient, 0, 0, frame_dx, frame_dy - status_dy, TRUE);
//resize the statusbar
SendMessage(GetDlgItem(hwnd, IDC_STATUSBAR), WM_SIZE,0,0);
return 0; //ritorna zero altrimenti la Frame Window ridisegna il MDI client spalmandolo su tutta la client area andando sopra alla status bar
}
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_COMMAND:
if(LOWORD(wParam) == IDM_EXIT) DestroyWindow(hwnd);
if(LOWORD(wParam) == IDM_ABOUT) MessageBox(hwnd,"ABOUT premuto","ciao",MB_OK);
if(LOWORD(wParam) == IDM_OPEN){
//apre il file...
//TODO
//crea la finestra CHILD
// HWND hwndChild = CreateWindowEx(WS_EX_MDICHILD, //extended styles
// childClass, //control 'class' name
// "MDI child", //control caption
// WS_CLIPCHILDREN|WS_CLIPSIBLINGS, //control style
// 0, //position: left
// 0, //position: top
// 400, //width
// 300, //height
// hWndClient, //parent window handle
// //control's ID
// MAKEINTRESOURCE(IDM_FIRSTCHILD),
// GetModuleHandle(NULL), //application instance
// 0); //user defined info
//
// if(hwndChild == NULL){
// MessageBox(hwnd,"Impossibile creare la finestra Child","Errore",MB_ICONERROR);
// }
//metodo alternativo da StackOverflow
MDICREATESTRUCT mdicreate = {0};
mdicreate.szClass = childClass;
mdicreate.szTitle = "class_mdichild";
mdicreate.hOwner = (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE); // <- hinstance
mdicreate.x = CW_USEDEFAULT;
mdicreate.y = CW_USEDEFAULT;
mdicreate.cx = CW_USEDEFAULT;
mdicreate.cy = CW_USEDEFAULT;
mdicreate.style = 0;
mdicreate.lParam = 0;
HWND hwndChild = (HWND)SendMessage(hWndClient, WM_MDICREATE, 0,(LPARAM)(LPMDICREATESTRUCT)&mdicreate);
if(hwndChild == NULL) {
MessageBox(hwnd, "Impossibile creare la finestra Child", "Errore", MB_ICONERROR);
}
}
break;
default:
//return DefWindowProc(hwnd,msg,wParam,lParam);
return DefFrameProc(hwnd, hWndClient, msg, wParam, lParam); //per tenere conto delle finestre figlie
}
return DefFrameProc(hwnd, hWndClient, msg, wParam, lParam);
}
LRESULT CALLBACK ChildWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
switch(msg) {
case WM_CREATE :
// LPCREATESTRUCT lpCreateStruct = (LPCREATESTRUCT)lParam;
// LPMDICREATESTRUCT lpMDICreateStruct = (LPMDICREATESTRUCT)lpCreateStruct->lpCreateParams;
// FrameParam = lpMDICreateStruct->lParam;
break;
case WM_COMMAND:
break;
/* All other messages (a lot of them) are processed using default procedures */
default:
return DefMDIChildProc(hwnd, msg, wParam, lParam);
}
return DefMDIChildProc(hwnd, msg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)
{
WNDCLASSEX wc;
HWND hwnd;
MSG msg;
//STEP 1: Registering Frame Class Window
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;;
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_APPWORKSPACE+1);//(COLOR_WINDOW);
wc.hIconSm = LoadIcon(NULL,IDI_APPLICATION);
wc.lpszMenuName = NULL;
wc.lpszClassName = frameClass;
if(!RegisterClassEx(&wc)){
MessageBox(NULL,"Frame Window Registration Failed!","Error!",MB_ICONEXCLAMATION);
return 0;
}
//la classe CLIENT non la devo registrare?
//STEP 1.2: Registering Child Class Window
wc.lpfnWndProc = (WNDPROC)ChildWndProc;
wc.hIcon = (HICON)LoadImage(0,IDI_APPLICATION,IMAGE_ICON,0,0,LR_SHARED);
wc.hIconSm = LoadImage(hInstance,childClass,IMAGE_ICON,16,16,LR_DEFAULTCOLOR);
wc.hCursor = (HCURSOR)LoadImage(0,IDC_ARROW,IMAGE_CURSOR,0,0,LR_SHARED);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);//(COLOR_WINDOW);
wc.lpszMenuName = NULL;
wc.lpszClassName = childClass;
if(!RegisterClassEx(&wc)){
MessageBox(NULL,"Child Window Registration Failed!","Error!",MB_ICONEXCLAMATION);
return 0;
}
//STEP 2: Creating the Window
hwnd = CreateWindowEx(WS_EX_CLIENTEDGE,
frameClass,
"046 - Visualizer",
WS_VISIBLE|WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,CW_USEDEFAULT,600,400,
NULL,NULL,hInstance,NULL);
if(hwnd == NULL){
MessageBox(NULL,"Frame Window Creation Failed!","Error!",MB_ICONEXCLAMATION);
return 0;
}
ShowWindow(hwnd,nCmdShow);
UpdateWindow(hwnd);
//STEP 3: Create Menu
HMENU hMenu = CreateMenu(); //crea un menu vuoto
HMENU hSubMenu; //variabile usata per aggiungere sottomenu ti tipo POPUP
hSubMenu = CreatePopupMenu();
AppendMenu(hSubMenu,MF_STRING,IDM_OPEN,"&Open");
AppendMenu(hSubMenu,MF_STRING,IDM_CLOSE,"&Close");
AppendMenu(hSubMenu, MF_SEPARATOR, 0, NULL);
AppendMenu(hSubMenu,MF_STRING,IDM_EXIT,"&Exit");
AppendMenu(hMenu,MF_STRING|MF_POPUP,(UINT)hSubMenu,"&File"); //aggiunge il sottomenu al menu
hSubMenu = CreatePopupMenu();
AppendMenu(hSubMenu,MF_STRING,IDM_ARRANGE,"Arrange");
AppendMenu(hSubMenu,MF_STRING,IDM_CASCADE,"Cascade");
AppendMenu(hSubMenu,MF_STRING,IDM_TILE,"Tile");
AppendMenu(hSubMenu,MF_STRING,IDM_CLOSEALL,"Close All");
AppendMenu(hMenu,MF_STRING|MF_POPUP,(UINT)hSubMenu,"&Windows");
hSubMenu = CreatePopupMenu();
AppendMenu(hSubMenu,MF_STRING,IDM_ABOUT,"&About");
AppendMenu(hMenu,MF_STRING|MF_POPUP,(UINT)hSubMenu,"&Help");
if(hMenu == NULL){
MessageBox(NULL,"Menu Creation Failed!","Error!",MB_ICONEXCLAMATION);
return 0;
}
SetMenu(hwnd,hMenu); //setta il menu sulla finestra
//STEP 4: Message Loop
while(GetMessage(&msg,NULL,0,0) > 0){
if(hWndClient && TranslateMDISysAccel(hWndClient,&msg)) continue; //process the predefined MDI specific accelerator keys
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}

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

WinAPI - Blurring a window fails

I want to blur a window, and I have used DwmEnableBlurBehindWindow(), DwmExtendFrameIntoClientArea() and DwmSetWindowAttribute() (to enable the client area rendering). However, it fails to blur, and instead makes the client area white.
Minimum reproducible code:
#include <dwmapi.h>
#include <gdiplus.h>
#include <stdio.h>
#include <windows.h>
HWND hwnd;
HRESULT enableNCRendering(HWND hWnd)
{
enum DWMNCRENDERINGPOLICY ncrp = DWMNCRP_ENABLED;
HRESULT hr = DwmSetWindowAttribute(hWnd,
DWMWA_NCRENDERING_POLICY,
&ncrp,
sizeof(ncrp));
if (!SUCCEEDED(hr))
printf("Failed 0\n");
return hr;
}
HRESULT EnableBlurBehind(HWND hwnd)
{
DWM_BLURBEHIND bb = {0};
bb.dwFlags = DWM_BB_ENABLE;
bb.fEnable = true;
bb.hRgnBlur = NULL;
HRESULT hr = DwmEnableBlurBehindWindow(hwnd, &bb);
if (!SUCCEEDED(hr))
printf("Failed 1\n");
return hr;
}
HRESULT ExtendIntoClientAll(HWND hwnd)
{
MARGINS margins = {-1};
HRESULT hr = DwmExtendFrameIntoClientArea(hwnd, &margins);
if (!SUCCEEDED(hr))
printf("Failed 2\n");
return hr;
}
ATOM MyRegisterClass(HINSTANCE hInst, LPCWSTR name, UINT styles, COLORREF bkg_colour, WNDPROC proc)
{
WNDCLASSEXW wc;
wc.cbSize = sizeof(WNDCLASSEXW);
wc.style = styles;
wc.lpfnWndProc = proc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInst;
wc.hIcon = LoadIconW(hInst, (LPCWSTR)IDI_APPLICATION);
wc.hCursor = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW);
wc.hbrBackground = (HBRUSH)CreateSolidBrush(bkg_colour); // CreateSolidBrush(RGB(255, 0, 0))
wc.lpszMenuName = NULL;
wc.hIconSm = LoadIconW(hInst, (LPCWSTR)IDI_APPLICATION);
wc.lpszClassName = (LPCWSTR)name;
return RegisterClassExW(&wc);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
switch (msg)
{
case WM_NCHITTEST:
return HTCAPTION;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProcW(hwnd, msg, wp, lp);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
MyRegisterClass(hInstance, L"Main", CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS, RGB(255, 0, 0), WndProc);
hwnd = CreateWindowExW(WS_EX_LAYERED, L"Main", L"main", WS_POPUP, 0, 0, 1000, 500, NULL, NULL, hInstance, NULL);
if (!hwnd)
return 1;
SetLayeredWindowAttributes(hwnd, 0, 100, LWA_ALPHA);
enableNCRendering(hwnd);
EnableBlurBehind(hwnd);
ExtendIntoClientAll(hwnd);
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while (GetMessageW(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
return msg.wParam;
}
I have also tried SetWindowCompositionAttributes() but it was undefined in Mingw, and I wasn't able to find much information on using that. It also doesn't give the desired effect and gives barely any blur:
#include <Winerror.h>
#include <dwmapi.h>
#include <gdiplus.h>
#include <stdio.h>
#include <windows.h>
HWND hwnd;
struct ACCENTPOLICY
{
int na;
int nf;
int nc;
int nA;
};
struct WINCOMPATTRDATA
{
int na;
PVOID pd;
ULONG ul;
};
typedef BOOL(WINAPI* pSetWindowCompositionAttribute)(HWND, WINCOMPATTRDATA*);
void makeBlur()
{
const HINSTANCE hm = LoadLibraryW(L"user32.dll");
if (hm)
{
const pSetWindowCompositionAttribute SetWindowCompositionAttribute = (pSetWindowCompositionAttribute)GetProcAddress(hm, "SetWindowCompositionAttribute");
if (SetWindowCompositionAttribute)
{
struct ACCENTPOLICY policy = {3, 0, 0, 0};
struct WINCOMPATTRDATA data = {19, &policy, sizeof(ACCENTPOLICY)};
SetWindowCompositionAttribute(hwnd, &data);
}
FreeLibrary(hm);
}
}
ATOM MyRegisterClass(HINSTANCE hInst, LPCWSTR name, UINT styles, COLORREF bkg_colour, WNDPROC proc)
{
WNDCLASSEXW wc;
wc.cbSize = sizeof(WNDCLASSEXW);
wc.style = styles;
wc.lpfnWndProc = proc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInst;
wc.hIcon = LoadIconW(hInst, (LPCWSTR)IDI_APPLICATION);
wc.hCursor = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW);
wc.hbrBackground = (HBRUSH)CreateSolidBrush(bkg_colour); // CreateSolidBrush(RGB(255, 0, 0))
wc.lpszMenuName = NULL;
wc.hIconSm = LoadIconW(hInst, (LPCWSTR)IDI_APPLICATION);
wc.lpszClassName = (LPCWSTR)name;
return RegisterClassExW(&wc);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
switch (msg)
{
case WM_NCHITTEST:
return HTCAPTION;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProcW(hwnd, msg, wp, lp);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
//getting accent colors
DWORD color = 0;
BOOL opaque = FALSE;
DwmGetColorizationColor(&color, &opaque);
BYTE blue = color;
color = color >> 8;
BYTE green = color;
color = color >> 8;
BYTE red = color;
color = color >> 8;
BYTE alpha = color;
MyRegisterClass(hInstance, L"Main", CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS, RGB(red, green, blue), WndProc);
hwnd = CreateWindowExW(WS_EX_LAYERED, L"Main", L"main", WS_POPUP, 0, 0, 1000, 500, NULL, NULL, hInstance, NULL);
if (!hwnd)
return 1;
SetLayeredWindowAttributes(hwnd, 0, 100, LWA_ALPHA);
makeBlur();
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while (GetMessageW(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
return msg.wParam;
}
How can I make my window blur?
The DwmEnableBlurBehindWindow documentation states:
Beginning with Windows 8, calling this function doesn't result in the blur effect, due to a style change in the way windows are rendered.
Aero Glass was officially discontinued in Windows 8. However, Aero Glass made a comeback (sort of) in Windows 10, as the Taskbar and some system notifications still use it. You have to use the undocumented SetWindowCompositionAttribute() API to enable the blur effect now, see:
How do you set the glass blend colour on Windows 10?
You are looking for the ACCENT_ENABLE_BLURBEHIND option.

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

No LVN_GETINFOTIP notifications in a LVS_REPORT Listview of text cells

I have a listview of texts and have total control over it, works like a dream.
Except I don't get any LVN_GETINFOTIP notifications by WM_NOTIFY. I get every other notifications message but not LVN_GETINFOTIP.
I have spent some time been Googling and trying whatever I can find. Some sites say that from Vista it works different but not what different. I don’t care if it works before Win10 (can live with it) and not at all if it works before Win7.
I really wonder what prevents the LVN_GETINFOTIP messages?
switch (((LPNMHDR)lParam)->code)
{
case LVN_GETINFOTIP:
CreateListView(
0,
LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP | LVS_EX_LABELTIP,
0,
3 * TAB_HIGHT,
OwnWindowWidth,
OwnWindowHight - (OBJ_WINDOW_HIGHT + (3 * TAB_HIGHT)),
hWnd,
(HMENU)IDC_MAIN_LISTVIEW,
(HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE));
static HWND CreateListView(
DWORD dwAddStyle,
DWORD dwAddExtendedListViewStyle,
int X,
int Y,
int nWidth,
int nHeight,
HWND hWndParent,
HMENU IdListWiew,
HINSTANCE hInst)
{
HWND hWndListView;
INITCOMMONCONTROLSEX icex;
memset(&icex, 0, sizeof(INITCOMMONCONTROLSEX));
// Ensure that the common control DLL is loaded.
icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
icex.dwICC = ICC_LISTVIEW_CLASSES;
InitCommonControlsEx(&icex);
// Create the list-view window in report view with label
// editing enabled.
hWndListView = CreateWindowEx(WS_EX_ACCEPTFILES, WC_LISTVIEW, L"",
WS_CHILD | LVS_REPORT | WS_VISIBLE | WS_BORDER | WM_NOTIFY | dwAddStyle,
X, Y, nWidth, nHeight,
hWndParent, IdListWiew, hInst, NULL);
if (hWndListView == NULL)
return NULL;
ListView_SetExtendedListViewStyle(hWndListView, dwAddExtendedListViewStyle);
return hWndListView;
}
Found the error myself!
The sample code for ListViews I once used hides the first column and so I can't get any TVN_GETINFOTIP because there is nothing to hover (Width of column 0 was 0). I managed to find this by reduction of code in a test program and read it real carefully. A bit embarrassed I asked. But this is coding life.
I have now a very compact sample code 160lines of a report ListView if it comes to use. (I rather this question to be removed).
#include <windows.h>
#include <CommCtrl.h>
#include <wchar.h>
#define IDC_MAIN_LISTVIEW 100
#define MAIN_LISTFIELDS 9
#define MAIN_LISTLINES 78
int iMainFieldsWidth[MAIN_LISTFIELDS] = { 50, 60, 78, 70, 100, 50, 50, 121, 112 };
WORD wMainFieldsAlign[MAIN_LISTFIELDS] = { LVCFMT_RIGHT,LVCFMT_LEFT, LVCFMT_LEFT,LVCFMT_LEFT,LVCFMT_LEFT, LVCFMT_RIGHT, LVCFMT_RIGHT, LVCFMT_LEFT, LVCFMT_LEFT };
WCHAR *pMainFieldsHeader[MAIN_LISTFIELDS] = { L"zero",L"one",L"Two",L"Three",L"Four",L"five",L"six",L"seven",L"eight" };
LRESULT WINAPI MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
{
CREATESTRUCT MainWindowCreateStruct;
WNDCLASS wc;
MSG msg;
HWND hWnd;
if (!hPrevInstance)
{
wc.lpszClassName = L"AppClass";
wc.lpfnWndProc = MainWndProc;
wc.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(hInstance, L"1");
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_ACTIVEBORDER + 1);
wc.lpszMenuName = L"AppMenu";
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
RegisterClass(&wc);
}
memset(&MainWindowCreateStruct, 0, sizeof(CREATESTRUCT));
hWnd = CreateWindowEx(WS_EX_ACCEPTFILES, L"AppClass",
L"LvTest",
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
&MainWindowCreateStruct
);
ShowWindow(hWnd, nCmdShow);
// Main message loop:
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT WINAPI MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CREATE:
{
// Create the list-view window in report view with label
// editing enabled.
HWND hLvWnd = CreateWindowEx(WS_EX_ACCEPTFILES, WC_LISTVIEW, L"",
WS_CHILD | LVS_REPORT | WS_VISIBLE | WS_BORDER,
0, 0, 1000, 500,
hWnd, (HMENU)IDC_MAIN_LISTVIEW, (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE), NULL);
if (hLvWnd == NULL)
return 0;
ListView_SetExtendedListViewStyle(hLvWnd, LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP | LVS_EX_LABELTIP);
ListView_DeleteAllItems(hLvWnd);
{
LVCOLUMN lvc;
int iCol = 0;
memset(&lvc, 0, sizeof(LVCOLUMN));
while (ListView_DeleteColumn(hLvWnd, 0)); // Nonvisual left column
// Initialize the LVCOLUMN structure.
// The mask specifies that the format, width, text, and
// subitem members of the structure are valid.
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
// Add the columns.
for (iCol = 0; iCol < MAIN_LISTFIELDS; iCol++)
{
lvc.iSubItem = iCol;
lvc.pszText = pMainFieldsHeader[iCol];
lvc.cchTextMax = MAX_PATH;
lvc.iImage = 0;
lvc.iOrder = 0;
lvc.cx = iMainFieldsWidth[iCol]; // width of column in pixels
lvc.fmt = wMainFieldsAlign[iCol]; // alignment of column
if (ListView_InsertColumn(hLvWnd, lvc.iSubItem, &lvc) == -1)
return 0;
}
}
{
LVITEM lvI;
DWORD index = 0;
memset(&lvI, 0, sizeof(LVITEM));
// Some code to create the list-view control.
// Initialize LVITEM members that are common to all
// items.
lvI.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE | LVIF_INDENT;
lvI.state = 0;
lvI.stateMask = 0;
// Initialize LVITEM members that are different for each item.
for (index = 0; index < MAIN_LISTLINES; index++)
{
lvI.iItem = index;
lvI.iImage = 0; // Icon
lvI.iIndent = 0;
lvI.iSubItem = 0;
// lvI.lParam = (LPARAM) &rgPetInfo[index];
lvI.pszText = LPSTR_TEXTCALLBACK; // sends an
// LVN_GETDISPINFO
// message.
if (ListView_InsertItem(hLvWnd, &lvI) == -1)
return 0;
}
}
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_NOTIFY:
switch (((LPNMHDR)lParam)->code)
{
case LVN_GETINFOTIP:
if (lParam)
{ // This is only working it looks, or actually the LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP | LVS_EX_LABELTIP of the CreateWindowEx isn't, would be nice to have
LPNMLVGETINFOTIP pGetInfoTip = (LPNMLVGETINFOTIP)lParam;
wcscpy(pGetInfoTip->pszText, L"This is an InfoTip Text\nand it takes newlines", pGetInfoTip->cchTextMax);
}
break;
case LVN_GETDISPINFO:
if (lParam)
{
NMLVDISPINFO *plvdi = ((NMLVDISPINFO *)lParam);
if ((plvdi->hdr.hwndFrom == GetDlgItem(hWnd, IDC_MAIN_LISTVIEW)) && (plvdi->item.iSubItem<MAIN_LISTFIELDS) && plvdi->item.cchTextMax)
wcsncpy(plvdi->item.pszText, pMainFieldsHeader[plvdi->item.iSubItem], plvdi->item.cchTextMax);
}
break;
default:
break;
}
break;
default:
return(DefWindowProc(hWnd, msg, wParam, lParam));
}
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.

Resources