0

I'm trying to do something like this but only with the mouse:

require 'win32api'
BlockInput = Win32API.new("user32", "BlockInput", ["I"], "I")

BlockInput.call(1)
sleep 5
BlockInput.call(0)

I'm trying to use SetWindowsHookEx but it say that work but nothing happens, i can continue moving and clicking my mouse, this is the code:

require 'win32/api'

# SetWindowsHookEx CONSTANTS
WH_MOUSE = 7
WH_MOUSE_LL = 14

# MouseProc or LowLevelMouseProc CONSTANTS
WM_MOUSEMOVE = 0x0200
WM_LBUTTONDOWN = 0x0201
WM_LBUTTONUP = 0x0202
WM_MOUSEWHEEL = 0x020A
WM_MOUSEHWHEEL = 0x020E
WM_RBUTTONDOWN = 0x0204
WM_RBUTTONUP = 0x0205

SetWindowsHookEx = Win32::API.new('SetWindowsHookEx', ['I', 'K', 'L', 'I'], 'L', 'user32')
CallNextHookEx = Win32::API.new('CallNextHookEx', ['I', 'I', 'L', 'P'], 'I', 'user32')
UnhookWindowsHookEx = Win32::API.new('UnhookWindowsHookEx', ['I'], 'I', 'user32')
GetCurrentThreadId = Win32::API.new('GetCurrentThreadId', ['V'], 'I', 'kernel32')

MouseProc = Win32::API::Callback.new('ILP', 'L') do |nCode, wParam, lParam|
    # https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms644988(v=vs.85)
    if nCode < 0 # If nCode is less than zero, the hook procedure must return the value returned by CallNextHookEx.
        return CallNextHookEx.call(0, nCode, wParam, lParam)
    elsif wParam == WM_MOUSEMOVE or wParam == WM_LBUTTONDOWN or wParam == WM_LBUTTONUP
        # If the hook procedure processed the message, it may return a nonzero value to prevent the system from
        # passing the message to the rest of the hook chain or the target window procedure.
        return 1
    else
        # If nCode is greater than or equal to zero, and the hook procedure did not process the message,
        # it is highly recommended that you call CallNextHookEx and return the value it returns;
        # otherwise, other applications that have installed WH_MOUSE_LL hooks will not receive hook
        # notifications and may behave incorrectly as a result.
        return CallNextHookEx.call(0, nCode, wParam, lParam)
    end
end

threadID = GetCurrentThreadId.call
hMod = nil
# If the function succeeds, the return value is the handle to the hook procedure.
p hhk = SetWindowsHookEx.call(WH_MOUSE, MouseProc, hMod, threadID) #=> 166134929 (Good)
# But nothing happens, i can continue moving and clicking my mouse.
sleep 5
p UnhookWindowsHookEx.call(hhk) #=> 1 (Good)

if someone knows how to do it or how to do it in another way I would greatly appreciate it.

Edit: I'm running the code with admin access on Windows 7 (32 Bits)

I'm trying to use GetMessage() following Strive Sun - MSFT's answer but i have some problems passing MSG pointer

GetMessage = Win32::API.new('GetMessage', ['P', 'L', 'L', 'L'], 'I', 'user32')
point = [0, 0].pack('LL')
hwnd = uint = wparam = lparam = dword = 0
buf = [hwnd, uint, wparam, lparam, dword, point, dword].pack('JIL2Sa8S')
# J > hwnd, I > uint, L2 > wparam lparam, S > dword, a8 > point
GetMessage.call(buf, nil, 0, 0) #=> Stuck, i need to use Manager Task to close it.
Andrek
  • 105
  • 1
  • 6

1 Answers1

1

Your code lack a message loop.

Please refer: MouseProc callback function

An application-defined or library-defined callback function used with the SetWindowsHookEx function. The system calls this function whenever an application calls the GetMessage or PeekMessage function and there is a mouse message to be processed.

Why must SetWindowsHookEx be used with a windows message queue?

As Hans said that, the low-level hooks, WH_KEYBOARD_LL and WH_MOUSE_LL are different from all the other hooks. They don't require a DLL to be injected into the target process. Instead, Windows calls your hook callback directly, inside your own process. To make that work, a message loop is required. There is no other mechanism for Windows to make callbacks on your main thread, the callback can only occur when you've called Get/PeekMessage() so that Windows is in control.

A global hook like WH_KEYBOARD is very different. It requires a DLL and the callback occurs within the process that processes the keyboard message. You need some kind of inter-process communication to let your own program be aware of this. Named pipes are the usual choice. Which otherwise of course requires that this injected process pumps a message loop. It wouldn't get keyboard messages otherwise.

Favor a low-level hook, they are much easier to get going. But do pump or it won't work. And beware of timeouts, if you're not responsive enough then Windows will kill your hook without notice.

If you need to use WH_MOUSE globally, please inject the DLL. If you don't want to use DLL, please use WH_MOUSE_LL instead of WH_MOUSE.

For more details ,please refer : Use WH_MOUSE globally without DLL

This is C++ code and works. Because I am not familiar with Ruby, I cannot provide modified Ruby code.

#include <Windows.h>
#include <iostream>

using namespace std;

HHOOK mouseHook;

LRESULT __stdcall MouseHookCallback(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (nCode >= 0)
    {
        switch (wParam)
        {
        case WM_MOUSEWHEEL :
            return 1;
        case WM_LBUTTONDOWN:
            return 1;
        case WM_LBUTTONUP:
            return 1;
        case WM_MOUSEMOVE:
            return 1;
        }
    }
    return CallNextHookEx(mouseHook, nCode, wParam, lParam);
}

void SetHook()
{
    if (!(mouseHook = SetWindowsHookEx(WH_MOUSE_LL, MouseHookCallback, NULL, 0)))
    {
        cout << "Failed to install mouse hook!" << endl;
    }
}

void ReleaseHook()
{
    UnhookWindowsHookEx(mouseHook);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    SetHook();
    MSG msg;

    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}
Strive Sun
  • 5,988
  • 1
  • 9
  • 26
  • Thanks for your answer, it is so useful, i'm trying to use GetMessage() but i have some problems passing MSG pointer, if someone can help me i would appreciate it. I edited the main post to add the code. – Andrek Nov 18 '19 at 13:37
  • Maybe knowing the size of MSG could be useful, can you tell me it? – Andrek Nov 19 '19 at 11:47
  • @Andrek Do you mean `sizeof(MSG)`? Its value is 28. – Strive Sun Nov 20 '19 at 02:58
  • I really don't know what to do, i'm passing to my GetMessage a 28 bytesize string but it keeps stuck, well thanks a lot :). – Andrek Nov 20 '19 at 15:09