I'm trying to hook into Windows' sizing events. I wrote the following hook:
__declspec(dllexport)
LRESULT CALLBACK HookProcedure(int nCode, WPARAM wParam, LPARAM lParam)
{
MSG *msg = (MSG *)lParam;
wchar_t text[1014];
GetWindowText(msg->hwnd, text, 1024);
if (wcscmp(text, L"Untitled - Notepad") == 0)
{
if (nCode == HCBT_MOVESIZE)
{
FILE *file;
fopen_s(&file, "C:\\Users\\Me\\hooklog.txt", "a+");
fprintf(file, "Move or size.\n");
fclose(file);
}
else
{
FILE *file;
fopen_s(&file, "C:\\Users\\Me\\hooklog.txt", "a+");
fprintf(file, "Something else.\n");
fclose(file);
}
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
And I'm tring to install it using:
HHOOK hhk = SetWindowsHookEx(WH_CBT, hookProc, hModule, 0);
DWORD err = GetLastError();
The same technique works for other types of hooks, but fot WH_CBT is not working. All the variables (hookProc, hModule ad hhk after the call, and err is 0) involved have reasonable values and there are no signs of error. In the log file nothing shows up.
Working version
I had noticed that nCode usage depends on the type of hook, but I completely forgot to check the other parameters; as Hans noticed lParam doesn't point to a MSG for WH_CBT with nCode == HCBT_MOVESIZE; instead, lParam points to the new RECT and the HWND can be retrieved from wParam.
__declspec(dllexport)
LRESULT CALLBACK HookProcedure(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HCBT_MOVESIZE)
{
wchar_t text[1014];
GetWindowText((HWND)wParam, text, 1024);
if (wcscmp(text, L"Untitled - Notepad") == 0)
{
RECT *rect = (RECT *)lParam;
FILE *file;
fopen_s(&file, "C:\\Users\\Me\\hooklog.txt", "a+");
fprintf(file, "Move or size (%d, %d, %d, %d) for target window.\n", rect->left, rect->right, rect->top, rect->bottom);
fclose(file);
}
else
{
FILE *file;
fopen_s(&file, "C:\\Users\\Me\\hooklog.txt", "a+");
fprintf(file, "Move or size for some other window.\n");
fclose(file);
}
}
else
{
FILE *file;
fopen_s(&file, "C:\\Users\\Me\\hooklog.txt", "a+");
fprintf(file, "Something else.\n");
fclose(file);
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
Your assumption that the passed lParam is a pointer to a MSG is just wrong. Check the MSDN article for the callback, scroll to the bottom. You'll see that when nCode == HCBT_MOVESIZE then lParam is a pointer to RECT.
wParam gives you the handle to the window.
Code defensively, never ignore winapi function return values.
Related
initially i was able to print something when i pressed only right mouse button using
if (wParam == WM_RBUTTONDOWN)but now , i want the same effect, i want to print something when right mouse button + Ctrl key is pressed. how can i acheive that ?
i have tried this
LRESULT CALLBACK MainWindow::mouseProc(int Code, WPARAM wParam, LPARAM lParam)
{
auto& ms = *(const MSLLHOOKSTRUCT*)lParam;
MSLLHOOKSTRUCT* pMouseStruct = (MSLLHOOKSTRUCT*)lParam;
if (pMouseStruct != nullptr)
{
if (wParam == WM_RBUTTONDOWN & MK_CONTROL) // Here, i added MK_CONTROL but it doesn't work
{
qDebug() << "Print something when Right mouse button and Ctrl button is pressed togather";
}
}
return CallNextHookEx(NULL, Code, wParam, lParam);
}
UPDATE
when i want to try the case where only Ctrl is pressed and it should print something, it still doesn't work
LRESULT CALLBACK MainWindow::mouseProc(int Code, WPARAM wParam, LPARAM lParam)
{
auto& ms = *(const MSLLHOOKSTRUCT*)lParam;
MSLLHOOKSTRUCT* pMouseStruct = (MSLLHOOKSTRUCT*)lParam;
if (pMouseStruct != nullptr)
{
if (wParam == MK_CONTROL) // Here, i added only MK_CONTROL but it doesn't work
{
qDebug() << "Print something when Ctrl button is pressed ";
}
}
return CallNextHookEx(NULL, Code, wParam, lParam);
}
what am i missing here ?
First of all, if you want to capture the right button + ctrl, you can check the state of the Ctrl key (whether it is pressed) when WM_RBUTTONDOWN is detected.
LRESULT CALLBACK mouseProc(int Code, WPARAM wParam, LPARAM lParam)
{
auto& ms = *(const MSLLHOOKSTRUCT*)lParam;
MSLLHOOKSTRUCT* pMouseStruct = (MSLLHOOKSTRUCT*)lParam;
if (pMouseStruct != nullptr)
{
if (wParam == WM_RBUTTONDOWN && (GetAsyncKeyState(VK_LCONTROL)&0x8000)) //Left CONTROL key as example
{
std::cout << "ctrl + rbutton";
}
}
return CallNextHookEx(NULL, Code, wParam, lParam);
}
If you want to use a keyboard hook to hook only "Ctrl":
LRESULT CALLBACK keyboardProc(int Code, WPARAM wParam, LPARAM lParam)
{
KBDLLHOOKSTRUCT* pKeyboardStruct = (KBDLLHOOKSTRUCT*)lParam;
if (pKeyboardStruct != nullptr)
{
if (pKeyboardStruct->vkCode == VK_LCONTROL)
{
if(wParam == WM_KEYDOWN)
std::cout << " -ctrl- ";
}
}
return CallNextHookEx(NULL, Code, wParam, lParam);
}
void main(void)
{
HHOOK hmouse = SetWindowsHookEx(WH_MOUSE_LL, mouseProc, hInstance, 0);
HHOOK hkeyboard = SetWindowsHookEx(WH_KEYBOARD_LL, keyboardProc, hInstance, 0);
MSG msg;
while (GetMessage(&msg, 0, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
UnhookWindowsHookEx(hmouse);
UnhookWindowsHookEx(hkeyboard);
return;
};
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);
}
Its been asked before, but what I found was either mfc, or was not for a superclassed control, or maybe my keyword choice was not good(my vocabulary is limited)
I am developing in pure win32 and c++ in Devc++. I have a few Edit controls superclassed where Tab key presses and Return key presses are controled, but whenever I press Tab there is this Ding sound which is very annoying, not sure it is there because I am doing something wrong or what. Anyway, how do I remove it ?
This is my first attempt at superclassing, so if you have any other advice regarding the code, please let me know
Code:
LRESULT APIENTRY EditSuperClassWndProc ( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
{
switch (message)
{
case WM_KEYDOWN:
if ( isMyScreen )
{
if ( (int)wParam == VK_RETURN )
{
DWORD wparam = MAKELONG( 0, BN_CLICKED );
SendMessage( GetParent( hwnd ), WM_COMMAND, (WPARAM)wparam, (LPARAM)hButton );
}
else if ( (int)wParam == VK_TAB )
{
HWND nextInLine;
if( hwnd == hEditP )
nextInLine = hEditL;
else if ( hwnd == hEditL )
nextInLine = hEditP;
SendMessage( nextInLine, EM_SETSEL, (WPARAM)0, (LPARAM)-1 );
SetFocus( nextInLine );
}
else
{
return CallWindowProc( oldWndProc, hwnd, message, wParam, lParam );
}
}
return CallWindowProc( oldWndProc, hwnd, message, wParam, lParam );
break;
default:
return CallWindowProc( oldWndProc, hwnd, message, wParam, lParam );
}
}
Try adding the following code to your switch block:
case WM_CHAR:
if (wParam == VK_TAB)
{
return 0;
}
else
{
return CallWindowProc(oldWndProc, hwnd, message, wParam, lParam );
}
break;
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...
I want any window to close as soon as the mouse hovers on the close button on its non client area. I tried to trap wm_ncmousemove using WH_GETMESSAGE in SetWindowsHookEx and then
using SendMessage to send a WM_DESTROY message to the specified window but window is not closing. Any help????
LRESULT CALLBACK CallWndProc(int code, WPARAM wParam, LPARAM lParam)
{
MSG* msg = (MSG*) lParam;
if(code == HC_ACTION)
{
if(msg->message == WM_NCMOUSEMOVE)
{
if(msg->wParam == HTCLOSE)
{
SendMessage(hwndTarget, WM_DESTROY, wParam, lParam);
}
}
}
return CallNextHookEx(g_hkMsg, code, wParam, lParam);
}
INT WINAPI InstallW(HWND hwnd, HINSTANCE hInstance, LPWSTR lpCmdLine, int nCmdShow)
{
DWORD dwTarget = 0;
POINT point;
GetCursorPos(&point);
hwndTarget = WindowFromPoint(point);
dwTarget = GetWindowThreadProcessId(hwndTarget, NULL);
g_hkMsg = SetWindowsHookEx(WH_GETMESSAGE, CallWndProc, g_hInstance, 0);
if(g_hkMsg)
{
MessageBox(NULL, L"Message hook installed, press OK to uninstall.", L"HLHookTest", MB_ICONEXCLAMATION);
UnhookWindowsHookEx(g_hkMsg);
}
else
MessageBox(NULL, L"Hook installation failed.", L"HLHookTest", MB_ICONERROR);
return 0;
}
Send either WM_CLOSE or WM_SYSCOMMAND with wParam=SC_CLOSE instead.
WM_CLOSE and WM_SYSCOMMAND / SC_CLOSE ask the window to close. WM_DESTROY informs the window that it has been closed. Saying "You have been closed" to a window won't make it close.