Windows 10 (1607) - ElementFromPoint call duration near 2 ms.
After Windows 10 Creators Update (1703) - ElementFromPoint call duration near 500ms.
#include <Windows.h>
#include <UIAutomationClient.h>
IUIAutomation* g_automation = nullptr;
LRESULT CALLBACK LowLevelMouseProc(INT nCode, WPARAM wParam, LPARAM lParam)
{
POINT pt = ((PMSLLHOOKSTRUCT)lParam)->pt;
IUIAutomationElement *element = nullptr;
g_automation->ElementFromPoint(pt, &element);
return CallNextHookEx(0, nCode, wParam, lParam);
}
void main()
{
CoInitializeEx(NULL, COINIT_SPEED_OVER_MEMORY);
CoCreateInstance(__uuidof(CUIAutomation), NULL, CLSCTX_INPROC_SERVER,
__uuidof(IUIAutomation), (void**)& g_automation);
SetWindowsHookEx(WH_MOUSE_LL, (HOOKPROC)LowLevelMouseProc,
GetModuleHandle(nullptr), 0);
MSG message;
GetMessage(&message, (HWND)NULL, 0, 0);
}
Point is mouse click coordinates.
Too long ElementFromPoint - this is issue.
Looks like some changed in COM/UIAutomation processing?
Is exist some workaround get UI element from point?
Related
I try to track mouse with code like this:
#include <Windows.h>
#include <iostream>
HHOOK hMouseHook;
LRESULT CALLBACK mouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
MOUSEHOOKSTRUCT* pMouseStruct = (MOUSEHOOKSTRUCT*)lParam;
if (pMouseStruct != NULL)
printf("X: %d Y: %d\n", pMouseStruct->pt.x, pMouseStruct->pt.y);
return CallNextHookEx(hMouseHook, nCode, wParam, lParam);
}
int main() {
hMouseHook = SetWindowsHookEx(WH_MOUSE_LL, mouseProc, 0, 0);
MSG message;
while (GetMessage(&message, NULL, 0, 0)) {
TranslateMessage(&message);
DispatchMessage(&message);
}
UnhookWindowsHookEx(hMouseHook);
return 0;
}
And this works mostly good except one case. When I click on app console window mouse become very laggy. CPU usage is ok, and I can hit "enter" to "unfreeze" mouse. I can't figure out what I messed up.
I have an SDL2 program that opens an SDL window. In the main I create a hook for mouse events as
hMSHook = SetWindowsHookEx(WH_MOUSE_LL, MouseProc, NULL, NULL);
while outside of the main I have the custom mouse event handler
HHOOK hMSHook;
int xPosAbsolute = 0;
int yPosAbsolute = 0;
LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HC_ACTION)
{
auto &ms = *(const MOUSEHOOKSTRUCT *)lParam;
if (wParam == WM_MOUSEMOVE)
{
xPosAbsolute = ms.pt.x;
yPosAbsolute = ms.pt.y;
}
}
return CallNextHookEx(hMSHook, nCode, wParam, lParam);
}
When the window is in focus, the mouse coordinates xPosAbsolute and yPosAbsolute are properly updated as the mouse moves. However, if another window receives focus, these variables stop being updated.
My question is, how do I make SetWindowsHookEx() continue receiving WH_MOUSE_LL events while my window is not in focus?
EDIT:
Actually, as it turns out, SetWindowsHookEx only stopped receiving event updates when the window focus was lost to another window that was launched as administrator, while the SDL2 window of this program was launched as a regular user. Once the current program is launched as administrator as well, all updates continue to come in (using the above code), even when window focus is lost. So this turned out to be a non-issue.
Focus is not required but keep pumping messages is required.
The following is a mini Win32 window application with low level hook you can refer to. It will receive WM_MOUSEMOVE message in all existing threads running in the same desktop as the calling thread regardless of which window holds the focus.
LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HC_ACTION)
{
auto& ms = *(const MOUSEHOOKSTRUCT*)lParam;
if (wParam == WM_MOUSEMOVE)
{
OutputDebugString(L"\n WM_MOUSEMOVE \n");
OutputDebugStringA(to_string(counter++).c_str());
}
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return DefWindowProc(hWnd, message, wParam, lParam);
}
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
HHOOK hMSHook = SetWindowsHookEx(WH_MOUSE_LL, MouseProc, NULL, NULL);
WNDCLASSEXW wcex = {0};
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.lpfnWndProc = WndProc;
wcex.hInstance = hInstance;
wcex.lpszClassName = szWindowClass;
RegisterClassExW(&wcex);
HWND hWnd = CreateWindowW(szWindowClass, L"test", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
MSG msg;
// Main message loop:
while (GetMessage(&msg, nullptr, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
I want to use GDI+ with Pascal scripting which doesn't provide GDI+ natively, but I don't know why When using dll(shared), the process will not exit even when the window is destroyed, I mean to say I can still see the process running from the task manager though it doesn't have any window to see. The process remains at idle i.e without any resource usage
In my dll, for every new hwnd i am hooking my own wndproc and on WM_Paint message i am drawing the specified objects that are so far are requested to be drawn
I am exporting DrawRectangle symbol for drawing and compiling for 32-bit
my dll is
#include <Windows.h>
#include <gdiplus.h>
using namespace Gdiplus;
#include <objidl.h>
#pragma comment(lib, "Gdiplus.lib")
#include <functional>
#include <map>
#include <memory>
#include <vector>
#define DLL_EXPORT(RETURN_TYPE) \
extern "C" __declspec(dllexport) RETURN_TYPE __stdcall
void msg(const char *str) { MessageBoxA(nullptr, str, "Message", 0); }
void msg(const wchar_t *str) { MessageBoxW(nullptr, str, L"Message", 0); }
class _GdiManager {
public:
_GdiManager() {
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);
}
~_GdiManager() { GdiplusShutdown(gdiplusToken); }
private:
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
} GdiManager;
class DrawableObject {
public:
virtual void draw(Gdiplus::Graphics &Graphics) = 0;
virtual ~DrawableObject() = default;
};
namespace DrawableObjects {
class Rectangle : public DrawableObject {
public:
Rectangle(ARGB Color, int X, int Y, int Width, int Height)
: m_X{X}, m_Y{Y}, m_Width{Width}, m_Height{Height}, m_Brush{Color} {}
void draw(Gdiplus::Graphics &graphics) override {
graphics.FillRectangle(&m_Brush, m_X, m_Y, m_Width, m_Height);
}
private:
int m_X, m_Y, m_Width, m_Height;
Gdiplus::SolidBrush m_Brush;
};
} // namespace DrawableObjects
LRESULT MasterWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
class Painter {
public:
Painter(HWND hWnd) : m_WindowHandle{hWnd}, m_Graphics{hWnd} {
m_OriginalWindowProc = (WNDPROC)GetWindowLongW(m_WindowHandle, GWL_WNDPROC);
SetWindowLongW(m_WindowHandle, GWL_WNDPROC, (LONG)MasterWindowProc);
}
LRESULT CallOriginalWndProc(HWND hwnd, UINT uMsg, WPARAM wParam,
LPARAM lParam) {
return CallWindowProcW(m_OriginalWindowProc, hwnd, uMsg, wParam, lParam);
}
LRESULT Paint(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
if (uMsg == WM_PAINT) {
for (auto &o : m_Objects)
o->draw(m_Graphics);
} else if (uMsg == WM_DESTROY) {
PostQuitMessage(0);
}
return 0;
}
std::vector<std::unique_ptr<DrawableObject>> &Objects() { return m_Objects; }
private:
HWND m_WindowHandle;
Gdiplus::Graphics m_Graphics;
WNDPROC m_OriginalWindowProc;
std::vector<std::unique_ptr<DrawableObject>> m_Objects;
};
std::map<HWND, std::unique_ptr<Painter>> windowPaint;
LRESULT MasterWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
auto &p = windowPaint[hwnd];
auto r = p->CallOriginalWndProc(hwnd, uMsg, wParam, lParam);
p->Paint(hwnd, uMsg, wParam, lParam);
return r;
}
auto &insertPainter(HWND hwnd) {
auto &my_painter = windowPaint[hwnd];
if (!my_painter)
my_painter = std::make_unique<Painter>(hwnd);
return my_painter;
}
DLL_EXPORT(int)
DrawRectangle(HWND hwnd, ARGB LineColor, int startX, int startY, int width,
int height) {
auto &my_painter = insertPainter(hwnd);
my_painter->Objects().push_back(std::make_unique<DrawableObjects::Rectangle>(
LineColor, startX, startY, width, height));
return 0;
}
the host program:
//#include "gdi.cpp"
#include <ObjIdl.h>
#include <Windows.h>
#include <cassert>
#include <gdiplus.h>
using namespace Gdiplus;
#pragma comment(lib, "Gdiplus.lib")
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, PSTR, INT iCmdShow) {
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 = TEXT("GettingStarted");
RegisterClass(&wndClass);
hWnd = CreateWindow(TEXT("GettingStarted"), // window class name
TEXT("Getting Started"), // 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);
auto dll = LoadLibraryW(L"isGDI.dll");
assert(dll);
auto DrawRectangle = (int(__stdcall *)(
HWND, DWORD, int, int, int, int))GetProcAddress(dll, "DrawRectangle");
assert(DrawRectangle);
DrawRectangle(hWnd, 0xffff0000, 0, 0, 100, 100);
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
FreeLibrary(dll);
return msg.wParam;
} // WinMain
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
LPARAM lParam) {
return DefWindowProc(hWnd, message, wParam, lParam);
} // WndProc
also, the program will work as expected if I call DrawRectangle directly through the source code (without using DLL)
i view that you use global object GdiManager inside dll. this meant that it destructor called from DLL_PROCESS_DETACH, so inside LoaderLock critical section. in destructor you call GdiplusShutdown. when GdiplusShutdown called inside LoaderLock and GDI+ use background thread ( suppressBackgroundThread = FALSE - this is your case) this is always cause deadlock:
GdiplusShutdown signal to background thread exit (set Globals::ThreadQuitEvent) and then wait for background thread exit. thread when exiting try enter to LoaderLock and hung here - because it hold by thread which call GdiplusShutdown. so main thread hung in wait for backgound thread and bacground thread hung in enter LoaderLock critical section.
we can try use suppressBackgroundThread = TRUE, but in this case need call NotificationUnhook. if do this on DLL_PROCESS_DETACH already UB (based on implementation) this can look like ok, or hung, or fail (inside this called for example DestroyWindow which is also error from dll entry, also error if process detach will be called on different thread (compare dll attach) - so window will be created inside NotificationHook on another thread)
correct solution here will be export 2 additional functions from dll , say Start and Stop, and from first call GdiplusStartup and from second GdiplusShutdown. call Start just after dll load and Stop before unload
You are not telling the current thread (the app) to exit. Use PostQuitMessage in the WndProc associated with the window:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
LPARAM lParam) {
if (message == WM_DESTROY)
PostQuitMessage(0);
else
return DefWindowProc(hWnd, message, wParam, lParam);
return 0;
}
I can't seem to find what I am looking for which is a way to alter the OS's mouse clicks. To specify this would be on Windows.
The goal is to limit the number of mouse clicks a user can register within a period of time.
I think the function that you are looking for is SetWindowsHookEx. Here is a quick example.
#include <windows.h>
const DWORD desireddelay = 10;
LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
static DWORD previoustimestamp = 0;
if ((nCode == 0) && (wParam == WM_RBUTTONDOWN))
{
if ((((MSLLHOOKSTRUCT*)lParam)->time - previoustimestamp) < desireddelay)
{
return 1; //Non-Zero Swallows the keystroke. 0 Allows it. Always CallNextHookEx if you are not swallowing it.
}
else
{
previoustimestamp = ((MSLLHOOKSTRUCT*)lParam)->time;
}
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
int main()
{
HHOOK hook = SetWindowsHookEx(WH_MOUSE_LL, MouseProc, GetModuleHandle(NULL), NULL);
MSG msg;
bool done = false;
while (GetMessage(&msg, NULL, NULL, NULL)) {}
UnhookWindowsHookEx(hook);
return 0;
}
It's easy to get system menu on console application (GetSystemMenu) and add some own entries (AppendMenu). But then these menu items are useless for the app. Is there any way to get into message stream that would identify what menu item was clicked ?
I've tried to hook to console window but without any result, I mean the WH_SYSMSGFILTER, all is compiling ok but there are no messages shown the hook function is not run by the system.
Next thing was ReadConsoleInput and this works partially, that is it shows mouse events on the system menu, but there is no information in MENU_EVENT_RECORD structure about what menu item was clicked.
These are my attempts all in one snippet, here the console should be flooded with messages, but only those from ReadConsoleInput appear, but these doesn't contain any useful information. No matter if user clicks on first or second added menu item there are only two codes shown 278 (0x116) WM_INITMENU and 287 (0x11F) WM_MENUSELECT, but there is no way I know to get to the wParam of WM_MENUSELECT message.
#include <windows.h>
#include <stdio.h>
HHOOK sysMsgFilterHook;
LRESULT CALLBACK SysMsgFilterCallback(int nCode, WPARAM wParam, LPARAM lParam) {
printf("%i\n", nCode);
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
static LRESULT CALLBACK consoleWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
printf("%u\n", uMsg);
WNDPROC origProc = (WNDPROC) GetProp(hWnd, "origProc");
return CallWindowProc(origProc, hWnd, uMsg, wParam, lParam );
}
int main() {
SetLastError(0);
HWND console_hwnd = GetConsoleWindow();
HMENU console_hMenu = GetSystemMenu(console_hwnd, FALSE);
HINSTANCE console_hinstance = (HINSTANCE)GetWindowLong(console_hwnd, GWL_HINSTANCE);
DWORD console_processid = GetWindowThreadProcessId(console_hwnd, NULL);
HANDLE console_input_handle = GetStdHandle(STD_INPUT_HANDLE);
AppendMenu(console_hMenu, MF_STRING | MF_CHECKED, NULL, "test menu item");
AppendMenu(console_hMenu, MF_STRING | MF_CHECKED, NULL, "yet another menu item");
WNDPROC origProc = (WNDPROC)SetWindowLongPtr(console_hwnd, GWL_WNDPROC, (LONG_PTR)&consoleWndProc);
SetProp(console_hwnd, "origProc", (HANDLE)origProc);
sysMsgFilterHook = SetWindowsHookEx(
WH_SYSMSGFILTER,
(HOOKPROC)SysMsgFilterCallback,
console_hinstance,
console_processid
);
DWORD numEvents = 0;
INPUT_RECORD input;
while(ReadConsoleInput(console_input_handle, &input, 1, &numEvents)) {
//printf("input.EventType: %i\n", input.EventType);
if (input.EventType == MENU_EVENT) {
printf("input.Event.MenuEvent.dwCommandId %i\n", input.Event.MenuEvent.dwCommandId);
}
}
//printf("GetLastError: %lu\n", GetLastError());
UnhookWindowsHookEx(sysMsgFilterHook);
system("pause");
return 0;
}
I've succeeded with creating hook for mouse events, that is WH_MOUSE_LL. But all other hooks do not work.
What I intend to accomplish is to get some sort of WM_MENUCOMMAND message and then get rest with GetMenuItemInfo.
I've heard that the hooking procedure should be in another dll, but how to do that ? are there any working snippets ?