How to add event hook for mouse operations in c++ win32? - c++

I wanted to add a system foreground event hook to track the active window and track mouse clicks(right-click) within the active window. I add eventHook for EVENT_SYSTEM_FOREGROUND, which is working as expected, my hook:
HWINEVENTHOOK hHook = SetWinEventHook (EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND,
NULL, MyEventHandler, 0, 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
where MyEventHandler is
void CALLBACK MyEventHandler (
HWINEVENTHOOK hWinEventHook,
DWORD event,
HWND hwnd,
LONG idObject,
LONG idChild,
DWORD idEventThread,
DWORD dwmsEventTime
)
{ ... }
How to add a mouse event handler to track mouse operations like right click?

Related

Make a event for a mouse button even if application is minimized

I want to make an application that responds to a mouse button so I done this:
case WM_LBUTTONDOWN:
MessageBox(
NULL,
(LPCWSTR)L"HALLOOOO",
(LPCWSTR)L"Worked",
MB_ICONASTERISK | MB_OK | MB_DEFBUTTON2
);
break;
but the problem is that this only works if the user clicks on the window and I want it to work even with the window minimized
this work even if the application is minimized
GetKeyState(VK_LBUTTON);
but if I put this in a loop if I press it once it will detect 1 million times because it will just check if the key is down and if I add delay using Sleep(250) it may work but sometimes it will not detect anything even if the user pressed the key
I want my app to be able to detect if a key is pressed even if it's minimized how can I do this?
Since you already have a window, call SetWindowsHookEx with WH_MOUSE_LL.
The API is documented here and the parameters are explained.
HHOOK SetWindowsHookExW(
[in] int idHook,
[in] HOOKPROC lpfn,
[in] HINSTANCE hmod,
[in] DWORD dwThreadId
);
The lpfn hook procedure can be defined as follows:
HWND hmain_window;
HHOOK hhook;
LRESULT CALLBACK mouse_proc(int code, WPARAM wparam, LPARAM lparam)
{
if (code == HC_ACTION && lparam)
{
if (wparam == WM_LBUTTONDOWN)
{
//MOUSEHOOKSTRUCT* mstruct = (MOUSEHOOKSTRUCT*)lparam;
static int i = 0;
std::wstring str = L"mouse down " + std::to_wstring(i++);
SetWindowText(hmain_window, str.c_str());
}
}
return CallNextHookEx(hhook, code, wparam, lparam);
}
int APIENTRY wWinMain(HINSTANCE hinst, HINSTANCE, LPWSTR, int)
{
...
RegisterClassEx(...);
hmain_window = CreateWindow(...);
hhook = SetWindowsHookEx(WH_MOUSE_LL, mouse_proc, hinst, 0);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
UnhookWindowsHookEx(hhook);
return 0;
}
You can try SetWindowsHookEx with parameter WH_MOUSE_LL or WH_MOUSE.
This article shows how to install a keyboard hook. You can replace WH_KEYBOARD with WH_MOUSE to install a mouse hook and use this document to handle the callback.

c++ active window handle don't match m_hwnd of my MFC dialog based app

Can someone knows which handle to use (instead of m_hWnd) to match the active window handle reported by the hook callback?
I thought the m_hWnd of my MFC dialog based application would match the active handle window reported by the hook callback.
Below a small example (MFC dialog based application) displaying its main window handle in the title bar (m_hWnd) and the the active window handle in a CEdit control.
I set a hook to detect when the active window changes
h_event_hook = SetWinEventHook(EVENT_OBJECT_FOCUS, EVENT_OBJECT_FOCUS, NULL,
&window_change_hook, 0, 0, WINEVENT_OUTOFCONTEXT);
void CALLBACK window_change_hook(HWINEVENTHOOK hWinEventHook, DWORD event,
HWND hwnd, LONG idObject, LONG idChild,
DWORD dwEventThread, DWORD dwmsEventTime)
{
::PostMessage(h_this_app_wnd, WM_UPDATE_ACTIVE_WINDOW_CEDIT, (WPARAM)0, (LPARAM)hwnd);
}
The dialog title displays its handle as follow:
CString hwnd_text;
hwnd_text.Format("MonitorActiveWindow (%p)", m_hWnd);
SetWindowText(hwnd_text);
Below the MWE (minimal working example) that reproduce the problem:
#include "stdafx.h"
#include "MonitorActiveWindow.h"
#include "MonitorActiveWindowDlg.h"
#include "afxdialogex.h"
#include <string>
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
#define WM_UPDATE_ACTIVE_WINDOW_CEDIT WM_APP + 0x1001
static HWND h_this_app_wnd;
static HWINEVENTHOOK h_event_hook;
void CALLBACK window_change_hook(HWINEVENTHOOK hWinEventHook, DWORD event,
HWND hwnd, LONG idObject, LONG idChild,
DWORD dwEventThread, DWORD dwmsEventTime)
{
::PostMessage(h_this_app_wnd, WM_UPDATE_ACTIVE_WINDOW_CEDIT, (WPARAM)0, (LPARAM)hwnd);
}
LRESULT CMonitorActiveWindowDlg::OnUpdateActiveWindowCedit(WPARAM w_param, LPARAM l_param)
{
const auto h_wnd = reinterpret_cast<HWND>(l_param);
CString text;
text.Format("%p", h_wnd);
GetDlgItem(IDC_EDIT1)->SetWindowText(text);
return 0;
}
BOOL CMonitorActiveWindowDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
// Display the main window handle
CString hwnd_text;
hwnd_text.Format("MonitorActiveWindow (%p)", m_hWnd);
SetWindowText(hwnd_text);
// Keep a copy of m_hWnd just so the callback can call PostMessage
h_this_app_wnd = m_hWnd;
// Set a hook to detect when the active window changes
h_event_hook = SetWinEventHook(EVENT_OBJECT_FOCUS, EVENT_OBJECT_FOCUS, NULL,
&window_change_hook, 0, 0, WINEVENT_OUTOFCONTEXT);
return TRUE; // return TRUE unless you set the focus to a control
}
void CMonitorActiveWindowDlg::OnDestroy()
{
CDialogEx::OnDestroy();
UnhookWinEvent(h_event_hook);
}
CMonitorActiveWindowDlg::CMonitorActiveWindowDlg(CWnd* pParent /*=NULL*/)
: CDialogEx(IDD_MONITORACTIVEWINDOW_DIALOG, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CMonitorActiveWindowDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_EDIT1, m_output_cedit);
}
BEGIN_MESSAGE_MAP(CMonitorActiveWindowDlg, CDialogEx)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_DESTROY()
ON_MESSAGE(WM_UPDATE_ACTIVE_WINDOW_CEDIT, OnUpdateActiveWindowCedit)
END_MESSAGE_MAP()
void CMonitorActiveWindowDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}
HCURSOR CMonitorActiveWindowDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
The edit control appears to be read-only edit control. This control get default focus, and the window handle for this edit control is reported. You can change the edit control style to "Disabled" so that it doesn't steal focus from parent dialog.
If you are not interested in focus window, you can look for foreground window EVENT_SYSTEM_FOREGROUND, example
SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND,
NULL, &window_change_hook, 0, 0, WINEVENT_OUTOFCONTEXT);
...
void CALLBACK window_change_hook(HWINEVENTHOOK, DWORD, HWND hwnd, LONG, LONG, DWORD, DWORD)
{
CString str;
GetWindowText(GetAncestor(hwnd, GA_ROOT), str.GetBuffer(100), 100);
str.ReleaseBuffer();
::SetDlgItemText(h_this_app_wnd, IDC_EDIT1, str);
}
Just if someone is interested, below the solution I will use for my server to be able to send a message to the instance of the specified application that is on the top. It consists to modify the callback to retrieve the main window handle from the hwnd received.
HWND get_real_parent(HWND h_wnd)
{
const auto h_parent = GetAncestor(h_wnd, GA_PARENT);
if (!h_parent || h_parent == GetDesktopWindow())
return nullptr;
return h_parent;
}
HWND get_main_window_handle(HWND h_wnd)
{
auto h_main_window = h_wnd;
auto h_parent_window = h_wnd;
while (h_parent_window != nullptr)
{
h_parent_window = get_real_parent(h_parent_window);
if (h_parent_window != nullptr)
{
h_main_window = h_parent_window;
}
}
return h_main_window;
}
void CALLBACK window_change_hook(HWINEVENTHOOK hWinEventHook, DWORD event,
HWND hwnd, LONG idObject, LONG idChild,
DWORD dwEventThread, DWORD dwmsEventTime)
{
hwnd = get_main_window_handle(hwnd);
::PostMessage(h_this_app_wnd, WM_UPDATE_ACTIVE_WINDOW_CEDIT, (WPARAM)0, (LPARAM)hwnd);
}

How to use SetWinEventHook() function to get Active Window changed message

I have been working on a project which needs to detect current active window and get the active window title continuously.
Can anyone explain me how to use SetWinEventHook() function to get Active Window changed message.
[ i used GetForegroundWindow() function with a timer to get the active window. That approach is not very accurate because of the timer. So i need to use it with SetWinEventHook() function. can someone explain me how to do that? ]
hEvent = SetWinEventHook(EVENT_SYSTEM_FOREGROUND , EVENT_SYSTEM_FOREGROUND ,NULL,
WinEventProcCallback, 0, 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
VOID CALLBACK WinEventProcCallback ( HWINEVENTHOOK hWinEventHook, DWORD dwEvent, HWND hwnd, LONG idObject, LONG idChild, DWORD dwEventThread, DWORD dwmsEventTime)
{
/* how to get active window message */
}
I have found the solution. EVENT_SYSTEM_FOREGROUND Event is the missing piece. The system sends this event even if the foreground window has changed to another window. We can use this event to get the current active window.
VOID CALLBACK WinEventProcCallback ( HWINEVENTHOOK hWinEventHook, DWORD dwEvent, HWND hwnd, LONG idObject, LONG idChild, DWORD dwEventThread, DWORD dwmsEventTime)
{
if (dwEvent == EVENT_SYSTEM_FOREGROUND)
{
/* do something */
}
}

Adding statusbar in Win32 application

I want to add a statusbar to my Win32 application. I found out that I can use CreateStatusWindow function. I works fine until I re-size my window. See a part of my block of code:
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;
hInst = hInstance; // Store instance handle in our global variable
hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
if (!hWnd)
{
return FALSE;
}
CreateStatusWindow(WS_CHILD | WS_VISIBLE, _T("Welcome to SpyWindows"), hWnd, 9000);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
Here are two printscreens of my application main window:
What can I do to have a good status bar? (I also want to divide it in more areas)
The documentation mentions that the status bar will recompute its appropriate position and size when it receives a WM_SIZE message:
The window procedure automatically adjusts the size of the status bar
whenever it receives a WM_SIZE message. Typically, when the size of
the parent window changes, the parent sends a WM_SIZE message to the
status bar.
So, the simplest way to achieve this is to relay to the status bar the WM_SIZE messages received by the parent (with SendMessage(), from its window procedure). The message parameters do not matter, as the status bar does not use them in its computations.

Power state change notifications from a message-only window

I have a Visual Studio 2008 C++ project for Windows 7 where I would like to be notified of power state transitions (e.g. suspend, hibernate, resume, etc...). I have created a message-only window that watches for WM_POWERBROADCAST messages. Once the window is created, I suspend the PC.
For example (error checking omitted for brevity):
const TCHAR class_name[] = _T( "Power State Monitor" );
WNDCLASSEX wc = { 0 };
wc.cbSize = sizeof( WNDCLASSEX );
wc.lpfnWndProc = PowerStateMonitor;
wc.lpszClassName = class_name;
::RegisterClassEx( &wc );
::CreateWindowEx( 0, class_name, class_name, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, wc.hInstance, NULL );
::SetSuspendState( FALSE, FALSE, FALSE );
The WindowProc looks like this:
LRESULT CALLBACK PowerStateMonitor( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
if( uMsg == WM_POWERBROADCAST )
{
ATLTRACE( L"WM_POWERBROADCAST:\r\n");
return TRUE;
}
ATLTRACE( L"Default Handler: %#08x\r\n", uMsg );
return ::DefWindowProc( hwnd, uMsg, wParam, lParam );
}
I would expect to see WM_POWERBROADCAST: logged, but instead all I see are the typical window creation messages:
Default Handler: WM_GETMINMAXINFO
Default Handler: WM_NCCREATE
Default Handler: WM_NCCALCSIZE
Default Handler: WM_CREATE
Can anybody suggest what I can change to have my window correctly receive power change notifications?
Message only windows do not receive broadcast messages. You will have to use a hidden, normal window instead. From MSDN, emphasis mine:
Message-Only Windows
A message-only window enables you to send and receive messages. It is not visible, has no z-order, cannot be enumerated, and does not receive broadcast messages. The window simply dispatches messages.