0

This Python code detects mouse wheel scrolls, it works perfectly (see Get Mouse Wheel Scroll using Win32api in Python):

import win32api, win32con
from ctypes import windll, CFUNCTYPE, c_int, c_void_p, wintypes, byref
user32 = windll.user32
def LowLevelMouseProc(nCode, wParam, lParam):
    if wParam == win32con.WM_MOUSEWHEEL:
        print(nCode, wParam, lParam)
    # win32api.mouse_event(win32con.MOUSEEVENTF_WHEEL, 0, 0, 1, 0)             # code-generated scrollwheels
CMPFUNC = CFUNCTYPE(c_void_p, c_int, wintypes.WPARAM, wintypes.LPARAM)
user32.SetWindowsHookExW.argtypes = [c_int, CMPFUNC, wintypes.HINSTANCE, wintypes.DWORD]
pointer = CMPFUNC(LowLevelMouseProc)
hook_id = user32.SetWindowsHookExW(win32con.WH_MOUSE_LL,pointer,win32api.GetModuleHandle(None), 0)
msg = wintypes.MSG()
while user32.GetMessageW(byref(msg), 0, 0, 0) != 0:
    user32.TranslateMessage(msg)
    user32.DispatchMessageW(msg)

It works but it does not distinguish between scrolling down and scrolling up. In both cases, I have:

0 522 3010120
0 522 3010120
0 522 3010120
0 522 3010120

How to distinguish up and down scrolls, by using win32api, ctypes, but no other third party library?

Also, based on some specific mouse behaviour, I'd like to trigger additional mousescrolls with:

def LowLevelMouseProc(nCode, wParam, lParam):
    if wParam == win32con.WM_MOUSEWHEEL:
        print(nCode, wParam, lParam)
    win32api.mouse_event(win32con.MOUSEEVENTF_WHEEL, 0, 0, 1, 0)    # TRIGGER HERE

Problem: these code-triggered fake mousewheels are detected as real WM_MOUSEWHEEL events, and they fall in the event loop / event listener, and generate themselves new events, which I don't want.

Question: how to avoid that this mousewheel event listener takes the code-generated scrolls in consideration?

Basj
  • 41,386
  • 99
  • 383
  • 673
  • 1
    check in `MSLLHOOKSTRUCT::flags` [`LLMHF_INJECTED`](https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-msllhookstruct) - *Testing LLMHF_INJECTED (bit 0) will tell you whether the event was injected.* – RbMm Sep 17 '21 at 10:18
  • inside [`LowLevelMouseProc`](https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms644986(v=vs.85)) - *lParam - A pointer to an MSLLHOOKSTRUCT structure.* – RbMm Sep 17 '21 at 10:34
  • 1
    yor declaration of MSLLHOOKSTRUCT is wrong. `DWORD mouseData;` is where ? – RbMm Sep 17 '21 at 11:07
  • Please note (as I commented on the other post) that you have ***UB***: [\[SO\]: C function called from Python via ctypes returns incorrect value (@CristiFati's answer)](https://stackoverflow.com/questions/58610333/c-function-called-from-python-via-ctypes-returns-incorrect-value/58611011#58611011), and the fact it works is pure (dumb) luck. – CristiFati Sep 18 '21 at 00:02
  • @CristiFati Can you suggest an edit to my current answer (that seems to work, is it just luck?) to correct it? It would be useful for future reference, thanks in advance! – Basj Sep 22 '21 at 16:28
  • @Basj: The *URL* that I indicated explains it. Specify *argtypes* and *restype* for all functions you call. If you're on *32bit*, then you're fine. But did every work? There were actually 2 questions, did also the up / down scroll differentiation work? – CristiFati Sep 22 '21 at 17:57
  • I'm on 64 bit @CristiFati, but it seems to work. Yes the scroll differientiation works now (it's in MSLLHOOKSTRUCT). Would you want to modify my answer to make it better (with *argtypes* and *restype*)? I'm not totally sure how it should be. Thanks! – Basj Sep 22 '21 at 20:17

1 Answers1

0

Following @RbMm's advice, here is a solution with MSLLHOOKSTRUCT:

import win32api, win32con, ctypes
from ctypes import windll, CFUNCTYPE, c_int, c_void_p, wintypes, byref, POINTER, Structure
user32 = windll.user32

class MSLLHOOKSTRUCT(Structure):
    _fields_ = [
        ("x", ctypes.c_long),
        ("y", ctypes.c_long),
        ("mouseData", ctypes.c_ulong),
        ("flags", ctypes.c_ulong),
        ("time", ctypes.c_ulong),
        ("dwExtraInfo", ctypes.c_ulong)
    ]

def LowLevelMouseProc(nCode, wParam, lParam):
    if wParam == win32con.WM_MOUSEWHEEL:
        injected = lParam.contents.flags & 0x00000001
        print(lParam.contents.x, lParam.contents.y, injected)
        if injected == 0:
            win32api.mouse_event(win32con.MOUSEEVENTF_WHEEL, 0, 0, 1, 0)

CMPFUNC = CFUNCTYPE(c_void_p, c_int, wintypes.WPARAM, POINTER(MSLLHOOKSTRUCT))
user32.SetWindowsHookExW.argtypes = [c_int, CMPFUNC, wintypes.HINSTANCE, wintypes.DWORD]
pointer = CMPFUNC(LowLevelMouseProc)
hook_id = user32.SetWindowsHookExW(win32con.WH_MOUSE_LL, pointer, win32api.GetModuleHandle(None), 0)
msg = wintypes.MSG()
while user32.GetMessageW(byref(msg), 0, 0, 0) != 0:
    user32.TranslateMessage(msg)
    user32.DispatchMessageW(msg)
Basj
  • 41,386
  • 99
  • 383
  • 673