I created toplevel window, but for unknown reasons my WNDPROC does not receive WM_LBUTTONDOWN/WM_LBUTTONUP nor WM_MOUSEMOVE messages.
Any suggestions?
Relevant code:
WNDCLASSEX wc = {0};
Wc.cbSize = 48;
Wc.cbWndExtra = 80;
Wc.hCursor = LoadCursorW(g_hInstance, MAKEINTRESOURCEW(1002));
Wc.style = CS_GLOBALCLASS | CS_DBLCLKS;
Wc.lpszClassName = Ici[dItemIndex].sInit;
Wc.lpfnWndProc = Ici[dItemIndex].wInit;
RegisterClassExW(&Wc);
g_hRuler1 = CreateWindowExW(WS_EX_TOOLWINDOW,
RULER_CONTROL,
L"",
WS_POPUP|WS_VISIBLE|0x1,
100 ,100, 40, RECTHEIGHT(g_rScreen),
NULL, NULL, hInst, NULL);
LRESULT WINAPI Ruler_Window(HWND hWindow, UINT uWindow, WPARAM wParam, LPARAM lParam)
{
if (uWindow == WM_GETMINMAXINFO)
{
goto DODEFAULT;
}
if (uWindow == WM_NCCREATE)
{
g_pGRI = RULER_ALLOCATE();
RULER_SET_POINTER(hWindow, (LONG_PTR)g_pGRI);
return 1L;
}
g_pGRI = RULER_GET_POINTER(hWindow);
g_pGRI->hWindow = hWindow;
switch(uWindow)
{
case WM_CREATE:
{
return Ruler_OnCreate(wParam, lParam);
}
case WM_PAINT:
{
return Ruler_OnPaint(wParam, lParam);
}
case WM_MOUSEMOVE:
{
return Ruler_OnMouseMove(wParam, lParam);
}
case WM_DESTROY:
{
return Ruler_OnDestroy(wParam, lParam);
}
case WM_SETCURSOR:
{
return Ruler_OnSetCursor(wParam, lParam);
}
case WM_LBUTTONDOWN:
{
return Ruler_OnLeftButtonDown(wParam, lParam);
}
case WM_LBUTTONUP:
{
return Ruler_OnLeftButtonUp(wParam, lParam);
}
case GM_SETINDICATORS:
{
return Ruler_OnSetIndicators(wParam, lParam);
}
DODEFAULT:
return DefWindowProcW(hWindow, uWindow, wParam, lParam);
}
}
Spy++ messages of window after left button click.
I think that the problem here is that you are most likely not calling DefWindowProc from your custom wndProc method (Ruler_Window).
Your code structure in your wndProc is a little... interesting.
You handle the messages you are interested in in a switch statement (with the exception of WM_NCCREATE, which is fine), but your switch statement doesn't actually have a default: entry... instead you have created a label called DODEFAULT, which you only reference once - when the message is WM_GETMINMAXINFO you goto DODEFAULT. Ignoring the issue of using a goto here, you basically do not handle any messages other than those listed in your code, and more importantly, you don't pass other messages to the default handler.
A very quick, very simple rewrite of your Ruler_Window method:
LRESULT WINAPI Ruler_Window(HWND hWindow, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_NCCREATE)
{
g_pGRI = RULER_ALLOCATE();
RULER_SET_POINTER(hWindow, (LONG_PTR)g_pGRI);
return DefWindowProcW(hWindow, uMsg, wParam, lParam);
}
// not sure what g_pGRI is, guessing it's a global?
// should this actually be passed into the handlers below?
g_pGRI = RULER_GET_POINTER(hWindow);
g_pGRI->hWindow = hWindow;
switch(uMsg)
{
case WM_CREATE:
return Ruler_OnCreate(wParam, lParam);
case WM_PAINT:
return Ruler_OnPaint(wParam, lParam);
case WM_MOUSEMOVE:
return Ruler_OnMouseMove(wParam, lParam);
case WM_DESTROY:
return Ruler_OnDestroy(wParam, lParam);
case WM_SETCURSOR:
return Ruler_OnSetCursor(wParam, lParam);
case WM_LBUTTONDOWN:
return Ruler_OnLeftButtonDown(wParam, lParam);
case WM_LBUTTONUP:
return Ruler_OnLeftButtonUp(wParam, lParam);
case GM_SETINDICATORS:
return Ruler_OnSetIndicators(wParam, lParam);
default:
break;
}
return DefWindowProcW(hWindow, uMsg, wParam, lParam);
}
Note, I also changed the name of the message parameter to be uMsg, as it makes reading the code much easier, IMHO.
I suspect that the problem is that you are not calling DefWindowProc for WM_NCCREATE and WM_CREATE. This means that the window is never setting up its client areas, so the messages are coming in as WM_NC*.
You should always pass WM_NCCREATE and WM_CREATE on to DefWindowProc.
Related
I found the following on CodeProject. It makes sense except if the sub-classed control doesn't handle the OCM_ message which means the default handling of the original message never occurs. Is there an elegant solution instead of having to always sync up the messages this function sends with the sub-classed windows procedures?
LRESULT DefParentProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam)
{
switch (umsg) {
case WM_NOTIFY:
{
NMHDR* nmhdr = (NMHDR*)lparam;
if (nmhdr->hwndFrom != NULL)
return SendMessage(nmhdr->hwndFrom, umsg + OCM__BASE, wparam, lparam);
break;
}
// All of these provide the control's HHWND in LPARAM
case WM_COMMAND:
case WM_CTLCOLORBTN:
case WM_CTLCOLOREDIT:
case WM_CTLCOLORDLG:
case WM_CTLCOLORLISTBOX:
case WM_CTLCOLORMSGBOX:
case WM_CTLCOLORSCROLLBAR:
case WM_CTLCOLORSTATIC:
case WM_VKEYTOITEM:
case WM_CHARTOITEM:
if (lparam != 0)
return SendMessage((HWND)lparam, umsg + OCM__BASE, wparam, lparam);
break;
// All of these provide ID of the control in WPARAM:
case WM_DRAWITEM:
case WM_MEASUREITEM:
case WM_DELETEITEM:
case WM_COMPAREITEM:
if (wparam != 0) {
HWND hwndControl = GetDlgItem(hwnd, wparam);
if (hwndControl)
return SendMessage(hwndControl, umsg + OCM__BASE, wparam, lparam);
}
break;
// Note we do not reflect WM_PARENTNOTIFY -> OCM_PARENTNOTIFY as that
// usually does not make much sense.
}
return DefWindowProc(hwnd, umsg, wparam, lparam);
}
There is not really a clean solution here.
You could ensure that all child controls subtract OCM__BASE when calling DefWindowProc() for unhandled OCM_... messages.
LRESULT WINAPI DefChildProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam)
{
switch (umsg) {
...
}
if ((umsg >= OCM__BASE) && (umsg <= OCM__MAX)) {
umsg -= OCM__BASE;
}
return DefWindowProc(hwnd, umsg, wparam, lparam);
}
Otherwise, you could have each OCM_... message carry a pointer to a struct in its WPARAM or LPARAM, where the struct contains the real WPARAM/LPARAM and the output LRESULT, and then each child control can return TRUE if a given OCM_... message is handled. The parent can then call DefWindowProc() with the original WM_... message if SendMessage(OCM_...) returns FALSE.
struct OCMInfo
{
LPARAM lParam;
LRESULT lResult;
};
LRESULT WINAPI DefParentProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam)
{
switch (umsg) {
case WM_NOTIFY:
{
NMHDR* nmhdr = (NMHDR*)lparam;
if (nmhdr->hwndFrom != NULL)
{
OCMInfo info;
info.lParam = lParam;
info.lResult = 0;
if (SendMessage(nmhdr->hwndFrom, umsg + OCM__BASE, wparam, (LPARAM)&info))
return info.lResult;
}
break;
}
// All of these provide the control's HHWND in LPARAM
case WM_COMMAND:
case WM_CTLCOLORBTN:
case WM_CTLCOLOREDIT:
case WM_CTLCOLORDLG:
case WM_CTLCOLORLISTBOX:
case WM_CTLCOLORMSGBOX:
case WM_CTLCOLORSCROLLBAR:
case WM_CTLCOLORSTATIC:
case WM_VKEYTOITEM:
case WM_CHARTOITEM:
if (lparam != 0)
{
OCMInfo info;
info.lParam = lParam;
info.lResult = 0;
if (SendMessage((HWND)lparam, umsg + OCM__BASE, wparam, (LPARAM)&info))
return info.lResult;
}
break;
// All of these provide ID of the control in WPARAM:
case WM_DRAWITEM:
case WM_MEASUREITEM:
case WM_DELETEITEM:
case WM_COMPAREITEM:
if (wparam != 0) {
HWND hwndControl = GetDlgItem(hwnd, wparam);
if (hwndControl)
{
OCMInfo info;
info.lParam = lParam;
info.lResult = 0;
if (SendMessage(hwndControl, umsg + OCM__BASE, wparam, (LPARAM)&info))
return info.lResult;
}
}
break;
// Note we do not reflect WM_PARENTNOTIFY -> OCM_PARENTNOTIFY as that
// usually does not make much sense.
}
return DefWindowProc(hwnd, umsg, wparam, lparam);
}
LRESULT WINAPI DefChildProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam)
{
switch (umsg) {
case OCM__NOTIFY:
{
OCMInfo* info = (OCMInfo*)lparam;
NMHDR* nmhdr = (NMHDR*)(info->lparam);
if (...) {
...
info->lResult = ...;
return TRUE;
}
break;
}
case OCM__COMMAND:
{
OCMInfo* info = (OCMInfo*)lparam;
if (...) {
...
info->lResult = ...;
return TRUE;
}
break;
}
...
}
return DefWindowProc(hwnd, umsg, wparam, lparam);
}
How can I handle individual items being clicked in a MS Windows treeview ?
My windows proc has :
LRESULT CALLBACK WndProcTreeView(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
PAINTSTRUCT paintStruct;
HDC hDC;
switch (message)
{
case WM_PAINT:
{
hDC = BeginPaint(hwnd, &paintStruct);
EndPaint(hwnd, &paintStruct);
break;
}
case WM_NOTIFY:
{
switch (reinterpret_cast<LPNMHDR>(lParam)->code) {
case NM_CLICK:
MessageBox(nullptr, "click", "click", MB_OK);
}
}
default:
{
return DefWindowProc(hwnd, message, wParam, lParam);
break;
}
}
Which outputs a message box when I click on the treeview control. How can I handle individual elements ?
Example of a treeview item being added to the list :
std::string vTxt = std::string("Vertex count : ") + std::to_string(mesh.v.size());
tvinsert.hInsertAfter = mesh_items[mesh_items.size() - 1];
tvinsert.hParent = mesh_items[mesh_items.size() - 1];
tvinsert.item.mask = TVIF_TEXT;
tvinsert.item.pszText = (LPSTR)vTxt.c_str();
mesh_items_sub.push_back((HTREEITEM)SendMessage(hwnd, TVM_INSERTITEM, 0, (LPARAM)&tvinsert));
I have seen using SendDlgItemMessage instead (which gives an ID as LOWORD(wParam) inside the windows proc) but it requires IDs set in a resource file - which I don't know how to create.
Two things I needed for my code to work : first give each item a lparam value and changing TVIF_TEXT as item's mask to TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM (TVIF_PARAM allowing for an lparam to be passed to the window proc thus identifying the controller).
Working code excerpt :
TV_INSERTSTRUCT tvinsert;
// ...
tvinsert.hInsertAfter = Root;
tvinsert.hParent = Root;
tvinsert.item.pszText = std::string("some text...").c_str();
tvinsert.item.lParam = ID_SOME_ID; // << #defined constant or plain int
tvinsert.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
root_sub.push_back((HTREEITEM)SendMessage(hwnd, TVM_INSERTITEM, 0, (LPARAM)&tvinsert));
// window proc code below
LRESULT CALLBACK WndProcTreeView(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
PAINTSTRUCT paintStruct;
HDC hDC;
switch (message)
{
case WM_PAINT:
{
hDC = BeginPaint(hwnd, &paintStruct);
EndPaint(hwnd, &paintStruct);
break;
}
case WM_NOTIFY:
{
LPNM_TREEVIEW pntv = (LPNM_TREEVIEW)lParam;
if (pntv->hdr.code == TVN_SELCHANGED) {
switch (pntv->itemNew.lParam) {
case ID_SOME_ID:
std::cout << "ID_SOME_ID selected caught here..." << std::endl;
break;
}
}
}
default:
{
return DefWindowProc(hwnd, message, wParam, lParam);
break;
}
}
return 0;
}
Good explanation/example here (in french) http://chgi.developpez.com/windows/treeview/
I really do not know what i am doing wrong.
I would like to create a dialogbox.
It is just does not show.
Declaration
BOOL CALLBACK NewDlgProc(HWND main, UINT msg, WPARAM wParam, LPARAM lParam);
Diagproc:
BOOL CALLBACK NewDlgProc(HWND main, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_INITDIALOG:
return TRUE;
case WM_COMMAND:
{
}
break;
default:
return FALSE;
}
return TRUE;
}
In windproc wm_command:
case ID_MENU_NEW:
{
int NewDlg = DialogBox(GetModuleHandle(NULL),
MAKEINTRESOURCE(ID_DIALOG_NEW), main, NewDlgProc);
if(NewDlg == 0)
MessageBox(main, "Cannot create dialogbox", "Error", MB_OK | MB_ICONERROR);
}
break;
resource.h:
#define ID_DIALOG_NEW 201
dlg.rc
#include <windows.h>
#include "resource.h"
ID_DIALOG_NEW DIALOG DISCARDABLE 100,100, 100,100
STYLE DS_MODALFRAME | WS_VISIBLE | WS_CHILD | WS_CAPTION
CAPTION "Add new"
FONT 8, "Ms Sans Serif"
BEGIN
END
A dialog can have an owner or be unowned, but it cannot have a parent. Consequently, you cannot have the WS_CHILD window style for a dialog. Replace that with WS_POPUP, and everyone will be happy.
Relevant quote from the MSDN (About Dialog Boxes : Modal Dialog Boxes):
An application must not create a modal dialog box having the WS_CHILD style.
How come in your dialog procedure you are not calling DefDlgProc() for the messages you are not handling?
BOOL CALLBACK NewDlgProc(HWND main, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_INITDIALOG:
return TRUE;
case WM_COMMAND:
{
}
break;
default:
return DefDlgProc(main, msg, wParam, lParam); // need default processing!!!!!!!!!!
}
return TRUE;
}
I'm writing a notepad program and would like the user to be able to press ctrl+n, ctrl+s, ctrl+o, etc, But I'm not even getting a response for the WM_KEYDOWN case. This is how my function is setup:
LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch( msg )
{
case WM_CREATE:
// CREATE STUFF HERE
break;
case WM_SIZE:
// RESIZE STUFF HERE
break;
case WM_COMMAND:
// COMMAND ACTIONS HERE
break;
case WM_NOTIFY:
// NOTIFICATIONS HERE
break;
case WM_KEYDOWN:
MessageBox( hwnd, "OK", "OK", MB_OK );
break;
case WM_CLOSE:
// CLOSE WINDOW HERE
break;
case WM_DESTROY:
// DESTROY WINDOW
break;
default:
return DefWindowProc( hwnd, msg, wParam, lParam );
}
return 0;
}
Anybody know what I'm doing wrong? Am I supposed to put the WM_KEYDOWN case somewhere else?
I'm installing a hook within my application to get the standard EDIT context menu (with undo/copy/edit/paste/etc.). I need to insert a new menu item for my application.
I've set a windows hook, but I can't seem to get the HMENU for the context menu. This is where I set the hook:
g_hHook = SetWindowsHookEx(WH_CALLWNDPROC, HookCallWndProc, NULL, GetCurrentThreadId());
Here is my callback function:
LRESULT CALLBACK HookCallWndProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HC_ACTION)
{
LPCWPSTRUCT cwps = (LPCWPSTRUCT)lParam;
switch(cwps->message)
{
case WM_CREATE:
{
WCHAR szClass[128];
GetClassName(cwps->hwnd, szClass, 127);
if (wcscmp(szClass, L"#32768") == 0)
{
LPCREATESTRUCT lpcs = (LPCREATESTRUCT)cwps->lParam;
HMENU hMenu = GetMenu(cwps->hwnd);
// hMenu is 0x0
//MENUINFO info;
//ZeroMemory(&info, sizeof(MENUINFO));
//info.cbSize = sizeof(info);
//GetMenuInfo(hMenu, &info);
MessageBox(NULL, L"Test", L"Test", NULL);
}
break;
}
}
}
return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
I also tried setting the hook with WH_CALLWNDPROCRET, but this one doesn't even capture the WM_CREATE message for the menu.
Does anyone know how to obtain the HMENU for this particular situation?
Thanks,
Kevin
You can send the MN_GETHMENU message to get the HMENU:
case WM_CREATE:
{
WCHAR szClass[128];
GetClassName(cwps->hwnd, szClass, 127);
if (wcscmp(szClass, L"#32768") == 0)
{
// Must delay MN_GETHMENU...
PostMessage(g_hDlg,WM_APP,(WPARAM)cwps->hwnd,(LPARAM)HookCallWndProc);
}
break;
}
...
LRESULT CALLBACK MyWindow(HWND hwnd,UINT msg,WPARAM wp,LPARAM lp)
{
switch(msg)
{
case WM_APP:
if (lp == (LPARAM)HookCallWndProc) // Just making sure it is our special message
{
HMENU hMenu = (HMENU) SendMessage((HWND)wp,MN_GETHMENU,0,0);
if (hMenu)
{
AppendMenu(hMenu,MF_STRING,666,L"Hello SO");
}
}
break;
This is a bit hacky but hacks are pretty much unavoidable when customizing controls like this...