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?