Hooking in win32 - winapi

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.

Related

Is WM_CHAR message possible with a DialogBox() modal dialog?

I've been searching for the answer for hours, and a common solution is to respond to a WM_GETDLGCODE message, but I never get that message to respond to.
My example code is:
#include <windows.h>
#include "resource.h"
static LRESULT CALLBACK DlgProc(HWND hDlg, UINT uMsg,
WPARAM wParam, LPARAM lParam)
{
switch(uMsg) {
case WM_INITDIALOG:
return TRUE;
case WM_COMMAND:
switch (wParam) {
case IDOK:
case IDCANCEL:
EndDialog(hDlg, 0);
return TRUE;
}
break;
case WM_GETDLGCODE:
MessageBox(hDlg, "Got WM_GETDLGCODE", "DlgProc", MB_OK);
return DLGC_WANTCHARS;
case WM_CHAR:
MessageBox(hDlg, "Got WM_CHAR", "DlgProc", MB_OK);
break;
}
return FALSE;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szcmdline, int iCmdShow)
{
DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1),
NULL, DlgProc);
return 0;
}
The dialog is a single edit box (1st tab stop) with the Ok and Cancel buttons. The problem is, I get neither the WM_GETDLGCODE nor the WM_CHAR message. Before I continue down this path, I just want to know if this should work or not. Hooks is my next choice, but if WM_CHAR is available, it seemed like a less complicated solution if I want to keep the dialog modal.

Message loop not exiting when I close the window

I am new to programming with the Win32 API, and have been trying to figure out why this application is not returning when I close the window.
#include <windows.h>
LRESULT CALLBACK WindowProc(
_In_ HWND hwnd,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd)
{
// Register window class
WNDCLASS wc = {};
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = L"Window Class";
RegisterClass(&wc);
// Create window
HWND window = CreateWindowEx(
0,
wc.lpszClassName,
L"Window",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
// Show window
ShowWindow(window, nShowCmd);
// Main Program Loop
MSG msg = {};
while (msg.message != WM_QUIT)
{
if (PeekMessage(&msg, window, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return msg.wParam;
}
LRESULT CALLBACK WindowProc(
_In_ HWND hwnd,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
I stepped through the code with a debugger and saw that after destroying the window, the value of the message was WM_PAINT, and so the program was continuously looping, but I don't understand why WM_QUIT is not being posted.
By providing window as an argument to PeekMessage you are telling it you only want to retrieve messages posted or sent to that window.
But WM_QUIT is a thread message - it's not associated with any given window. To retrieve it you need to call PeekMessage with nullptr for the window filter.
In addition to Jonathan Potter's answer, your message loop should be using GetMessage() instead of PeekMessage():
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
GetMessage() blocks the calling thread until a message arrives, and returns FALSE when WM_QUIT is received and the hWnd parameter is NULL.
By using PeekMessage(), you are running a tight busy loop, and the contents of msg are indeterminate when PeekMessage() returns FALSE when no message is available.

Win32 Subclassing - About Messages

So I'm practicing subclassing a predefined window class in Win32 so I can define my own custom message proc for the predefined classes (e.g. making a custom WndProc for a button class) and I got it to work for the most part but I can't get the WM_COMMAND messages to be automatically sent to the new message proc. I can set up a case in the parent window for WM_COMMAND and check the wParam then call the respective new message proc for the child window that sent the WM_COMMAND message but I would prefer for this to be done automatically like all the other commands. As far as I can tell all the other WM_'s I have tried in the custom message proc I defined get passed to it automatically from the parent window's message proc, but the WM_COMMAND messages won't unless I explicitly redirect them. I have a feeling WM_COMMAND messages will always be sent to the parent window when subclassing the way I have it set up, but if someone could explain why this is the case or what I would need to do in order to direct all messages belonging to the button window to the custom WndProc I defined, it would be much appreciated.
Code:
#include <Windows.h>
#include <windowsx.h>
#define IDC_BUTTON 0
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
LRESULT CALLBACK WndProcButton (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
HWND g_hwndButton = NULL;
WNDPROC g_wndProcButtonOrigianl = NULL;
BOOL g_bSeeingMouse = FALSE;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR szCmdLine, int iCmdShow)
{
static TCHAR szClassName[] = TEXT ("HelloWin") ;
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ;
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szClassName ;
if (!RegisterClass (&wndclass))
{
MessageBox (NULL, TEXT ("This program requires Windows NT!"),
szClassName, MB_ICONERROR) ;
return 0 ;
}
hwnd = CreateWindow (szClassName, // window class name
TEXT ("The Hello Program"), // window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // initial x position
CW_USEDEFAULT, // initial y position
CW_USEDEFAULT, // initial x size
CW_USEDEFAULT, // initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL) ; // creation parameters
ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}
LRESULT CALLBACK WndProcButton (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch( message )
{
case WM_COMMAND:
MessageBox( hwnd, TEXT( "Test box" ), TEXT( "Test box" ), MB_OK );
SetFocus( g_hwndButton );
break;
default:
if( !g_bSeeingMouse && GetCapture() == hwnd )
{
g_bSeeingMouse = TRUE;
SetWindowText( hwnd, L"Ok +mouse" );
}
else if( g_bSeeingMouse && GetCapture() != hwnd )
{
g_bSeeingMouse = FALSE;
SetWindowText( hwnd, L"Ok" );
}
break;
}
return CallWindowProc( g_wndProcButtonOrigianl, hwnd, message, wParam, lParam );
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc ;
PAINTSTRUCT ps ;
RECT rect ;
switch (message)
{
case WM_CREATE:
g_hwndButton = CreateWindow( L"Button", // predefined class
L"Ok", // button text
( WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON ), // styles
// Size and poition values are given explicitly, because
// the CW_USEDEFAULT constant gives zero values for buttons.
10, // starting x position
10, // starting y position
100, // button width
30, // button height
hwnd, // parent window
(HMENU)IDC_BUTTON, // no menu
(HINSTANCE)GetWindowLongPtr( hwnd, GWLP_HINSTANCE ),
NULL ); // pointer not needed
SetFocus( g_hwndButton );
g_wndProcButtonOrigianl = (WNDPROC)SetWindowLongPtr( g_hwndButton, GWLP_WNDPROC, (LONG_PTR)WndProcButton );
return 0 ;
case WM_PAINT:
hdc = BeginPaint (hwnd, &ps) ;
GetClientRect (hwnd, &rect) ;
DrawText (hdc, TEXT ("Hello, Windows 98!"), -1, &rect,
DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;
EndPaint (hwnd, &ps) ;
return 0 ;
case WM_DESTROY:
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
By design, WM_COMMAND and WM_NOTIFY messages are sent to a control's parent window. There is no way to make the API automatically send them to the control that generated them. The parent window must handle them, or forward them, as needed. There is no avoiding that.
In the VCL framework originally written by Borland and now owned by Embarcadero, it uses a simple system for forwarding such messages. When any parent window receives one of these messages, it increments the message ID by an offset that puts the message into the user-defined range, and then forwards the message to the child HWND specified in the message. So WM_COMMAND becomes CN_COMMAND and WM_NOTIFY becomes CN_NOTIFY, which the child control's window procedure can look for.
For example:
#define MY_COMMAND (WM_COMMAND + some_offset)
#define MY_NOTIFY (WM_NOTIFY + some_offset)
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_COMMAND:
if (lParam != 0)
SendMessage((HWND)lParam, MY_COMMAND, wParam, lParam);
return 0;
case WM_NOTIFY:
SendMessage(((NMHDR*)lParam)->hwndFrom, MY_NOTIFY, wParam, lParam);
return 0;
...
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
LRESULT CALLBACK WndProcButton (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch( message )
{
case MY_COMMAND:
...
case MY_NOTIFY:
...
}
return CallWindowProc( g_wndProcButtonOrigianl, hwnd, message, wParam, lParam );
}

How to handle the message-only window to get the messages from the console window?

I'm trying to know when a console window has moved so I created a new message-only window to get the messages of the console but I don't know if it is working because the message is apparently never being received.
#define WINVER 0x0501
#include <windows.h>
#include <iostream>
WNDPROC glpfnConsoleWindow; // NEW
using namespace std;
LRESULT APIENTRY MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_SIZE:
cout<<"Window moved"<<endl;
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
// NEW
return CallWindowProc(glpfnConsoleWindow, hwnd, uMsg, wParam, lParam);
//return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
HWND hwnd;
MSG Msg;
const char lpcszClassName[] = "messageClass";
WNDCLASSEX WindowClassEx;
// == NEW
HWND consHwnd;
consHwnd = GetConsoleWindow();
glpfnConsoleWindow = (WNDPROC)SetWindowLong(consHwnd, GWL_WNDPROC, (LONG)MainWndProc);
// ==
ZeroMemory(&WindowClassEx, sizeof(WNDCLASSEX));
WindowClassEx.cbSize = sizeof(WNDCLASSEX);
WindowClassEx.lpfnWndProc = MainWndProc;
WindowClassEx.hInstance = hInstance;
WindowClassEx.lpszClassName = lpcszClassName;
if (RegisterClassEx(&WindowClassEx) != 0)
{
// Create a message-only window
hwnd = CreateWindowEx(0, lpcszClassName, NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, hInstance, NULL);
if (hwnd != NULL)
cout<<"Window created"<<endl;
else
UnregisterClass(lpcszClassName, hInstance);
}
ShowWindow(hwnd,nCmdShow);
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return (int)Msg.wParam;
}
Perhaps you should process WM_MOVE, which, according to the documentation, is sent after the window has been moved. WM_SIZE is sent when the size changes.
I think your problem is that the WM_MOVE and WM_SIZE messages are going to the console window rather than to your hidden window.
I suspect you'll have to call GetConsoleWindow to get the console window handle, and then call SetWindowLong to attach your window proc to the console window. Be sure to pass messages on to the original window proc.

WM_MOUSEHOVER in a global hook

I have implemented custom tooltips for a couple of controls in my application (MFC) but I would like to make it general for all the controls.
Right now I am calling TrackMouseEvent in WM_MOUSEMOVE and then catching WM_MOUSEHOVER (both in the overwritten function WindowProc of the control). But this way I have to duplicate code for every control. So my intention was to set a global hook for mouse events and there ask the control for the message to display in the tooltip.
The problem is that I am not able to catch WM_MOUSEHOVER in a global hook. This is the code:
hMouseHook = SetWindowsHookEx( WH_MOUSE,
CallWndMouseProc,
NULL,
AfxGetThread()->m_nThreadID);
hMainHook = SetWindowsHookEx( WH_CALLWNDPROC,
CallWndProc,
NULL,
AfxGetThread()->m_nThreadID);
LRESULT CALLBACK CallWndMouseProc( int nCode,
WPARAM wParam,
LPARAM lParam )
{
if(nCode == HC_ACTION)
{
MOUSEHOOKSTRUCT* pwp = (MOUSEHOOKSTRUCT*)lParam;
TRACE( _T("message: %x hwnd: %x x: %d y: %d\n"),
wParam,
pwp->hwnd,
pwp->pt.x,
pwp->pt.y);
TRACKMOUSEEVENT eventTrack;
eventTrack.cbSize = sizeof(TRACKMOUSEEVENT);
eventTrack.dwFlags = TME_HOVER;
eventTrack.dwHoverTime = HOVER_DEFAULT;
eventTrack.hwndTrack = pwp->hwnd;
_TrackMouseEvent(&eventTrack);
if(wParam == WM_MOUSEHOVER)
{
AfxMessageBox(_T("CallWndMouseProc: WM_MOUSEHOVER"));
}
}
// let the messages through to the next hook
return CallNextHookEx( hMouseHook,
nCode,
wParam,
lParam);
}
LRESULT CALLBACK CallWndProc( int nCode,
WPARAM wParam,
LPARAM lParam )
{
if(nCode == HC_ACTION)
{
CWPSTRUCT *pData = (CWPSTRUCT*)lParam;
if(pData->message == WM_MOUSEHOVER)
{
AfxMessageBox(_T("CallWndProc: WM_MOUSEHOVER"));
}
}
// let the messages through to the next hook
return CallNextHookEx( hMainHook,
nCode,
wParam,
lParam);
}
Both hooks are being call for the rest of messages and I am sure WM_MOUSEHOVER is being sent because it's capture in the WindowProc function. For instance this is the WindowProc function for a custom CListBox:
LRESULT CMyListBox::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
if(message == WM_MOUSEHOVER)
{
AfxMessageBox(_T("WindowProc: WM_MOUSEHOVER"));
}
return CListBox::WindowProc(message, wParam, lParam);
}
Ok. So what I am missing? It's just not possible to capture this message in a global hook? Is there other way to make this without having to put the same code in every single control?
Thanks.
Javier
WM_MOUSEHOVER is posted to the thread's message queue, so you won't see it with WH_CALLWNDPROC (that's for sent messages). WH_MOUSE does get posted messages, so I find it a little strange that you aren't seeing it... Perhaps the hook only gets low level mouse input messages? Try a WH_GETMESSAGE hook.

Resources