-1

been trying to make a basic auto clicker and cannot seem to get MouseInput to click the mouse. I believe it's probably something simple causing this issue. No errors occur it just won't do what it is intended to do.

Could somebody take a look at it and tell me whats wrong with it?

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


void loop()
{


    int cps;
   std::cout << "Enter desired clicks per second: ";
    std::cin >> cps;


    bool toggled = false;
    while (true)
    {
        static bool bPressedToggle = false;
        if (GetKeyState(VK_TAB) < 0)
            bPressedToggle = true;
        else if (bPressedToggle)
        {
            bPressedToggle = false;
            toggled = !toggled;

            std::cout << (toggled ? "Toggled" : "Disabled") << std::endl;

            if (!toggled) continue;
        }


        if (toggled && (GetAsyncKeyState(VK_LBUTTON) & (1 << 16)))
        {
            POINT pntCurrentCursor;
            GetCursorPos(&pntCurrentCursor);

            INPUT inpMouseInput;
            inpMouseInput.type = INPUT_MOUSE;
            inpMouseInput.mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
            inpMouseInput.mi.dx = pntCurrentCursor.x;
            inpMouseInput.mi.dy = pntCurrentCursor.y;
            SendInput(1, &inpMouseInput, sizeof(INPUT)); // DOWN

            RtlZeroMemory(&inpMouseInput, sizeof(INPUT));


            inpMouseInput.type = INPUT_MOUSE;
            inpMouseInput.mi.dwFlags = MOUSEEVENTF_LEFTUP;
            inpMouseInput.mi.dx = pntCurrentCursor.x;
            inpMouseInput.mi.dy = pntCurrentCursor.y;
            SendInput(1, &inpMouseInput, sizeof(INPUT)); // UP

            Sleep( 1000 / cps);


        }
         //Sleep(1);
    }
}

int main()
{
    loop();

    return 0;
} ```
jrcrash
  • 51
  • 1
  • 4
  • If you need autoclicker then consider using function SendMessage. It is a more direct way to do it instead of manipulating the mouse. You probably have some targets. – armagedescu Mar 27 '20 at 12:19
  • Yes, well for the general purpose it's all fine and well. I need to be using MouseInput for bypass reasons. – jrcrash Mar 27 '20 at 12:47
  • @arm: There's *nothing* more direct about sending input messages. In fact, [you can't simulate keyboard input with PostMessage](https://devblogs.microsoft.com/oldnewthing/20050530-11/?p=35513). – IInspectable Mar 27 '20 at 13:00
  • @armagedescu You can't fake input using `SendMessage`, as we state on this site multiple times every single day – David Heffernan Mar 27 '20 at 13:00
  • @DavidHeffernan It could be automated testing of the UI for instance. I don't know the purpose of this code, in fact I don't care. All the input can be faked. Some cases sending messages is too complex. But there are different functions. For instance just SetWindowText if it is targeted some text inputs. – armagedescu Mar 27 '20 at 13:08
  • @IInspectable in fact the OP did not ask about simulating keyboard input. If it was the question I would suggest SetWindowText or some more direct functions. – armagedescu Mar 27 '20 at 13:12
  • @jrcrash You try to do something in some improper way. What exactly you would like to bypass? And how exactly your mouse manipulations are going to work better than sending targeted messages? – armagedescu Mar 27 '20 at 13:16
  • @arm: It doesn't matter whether it's keyboard or mouse input, nor does it matter, whether you call `PostMessage` or `SendMessage`. The core issue remains the same: You are prank calling into a foreign message dispatcher. And that *will* fail. Now if the OP really wanted to write a test automation tool, there's [UI Automation](https://learn.microsoft.com/en-us/windows/win32/winauto/entry-uiauto-win32) specifically for that use case. – IInspectable Mar 27 '20 at 13:55
  • @IInspectable The message dispatcher matters for PostMessage. But with send SendMessage it works very well. I have just verified. Also any direct winapi function like SetWindowText works even better. – armagedescu Mar 27 '20 at 14:54
  • So does anyone have an idea on how to solve my issue here. – jrcrash Mar 27 '20 at 15:10
  • @arm: I'm not going to repeat the blog post here. As for `SendMessage`: Once you're crossing thread boundaries, you aren't directly calling into the window procedure any more. A cross-thread sent message needs to be dispatched. That requires the receiving thread to call a message retrieval function (like `GetMessage`). And when the receiver decides to call `GetKeyState` for `VK_LBUTTON`, the prank call trips over. Likewise, the implementer of a control may not even care about the text you try to `SetWindowText`. – IInspectable Mar 27 '20 at 15:24
  • Injected input is trivially easy to identify (using a low-level mouse hook, for example) and filter. If you need to bypass some sort of protection, input injected using `SendInput` is likely to get rejected. – IInspectable Mar 27 '20 at 15:27
  • @IInspectable the implementers of UI uses standard windows controls 99% of cases. We don't speak about developing antiviruses there and fighting malware. Even if there is an antivirus, it usually doesn't care passes the most messages. – armagedescu Mar 27 '20 at 16:56
  • So for general purposes all the winapi works. About dispatching messages, talking in terms of winapi it is the DispatchMessage which stays usually in message loops. If we are talking about the sistem it is routing messages. When you call SendMessage the message is routed. Also when calling PostMessage it is routed as well, but is routed to a message queue of some thread instead. – armagedescu Mar 27 '20 at 16:57
  • @arm: Cross-thread inbound sent messages aren't dispatched by `DispatchMessage` (they never even materialize as a `MSG` structure). Cross-thread inbound sent messages are dispatched by one of the message retrieval functions, [or `SendMessage`](https://devblogs.microsoft.com/oldnewthing/20040608-00/?p=38983). – IInspectable Mar 27 '20 at 17:04
  • @IInspectable That's we are not talking about dispatching. We are talking about routing. – armagedescu Mar 27 '20 at 17:06
  • @arm: It doesn't matter. At all. I made my point: You cannot fake input using `SendMessage` or `PostMessage`. If you are interested to find out, how an attempt to do so can fail, you have the information. – IInspectable Mar 27 '20 at 17:09
  • There's an obvious bug in the code: `GetCursorPos` returns absolute screen coordinates. Passing those coordinates into the [MOUSEINPUT](https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-mouseinput) structure without the `MOUSEEVENTF_ABSOLUTE` flag causes them to be interpreted as relative movement. If you don't want to move the mouse, simply pass `0` for `dx` and `dy`. There's also a more subtle bug. See [this Q&A](https://stackoverflow.com/q/46744894/1889329) to find out more. – IInspectable Mar 28 '20 at 06:12
  • Also, as implemented, [GetKeyStates](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getkeystate) doesn't return a meaningful value. As documented: *"The key status returned from this function changes as a thread reads key messages from its message queue."* The code posted doesn't read messages from its message queue, so the state never gets updated. You would have to run a message loop. Which necessitates a complete re-architecture of the code. You will no longer be able to run the input injection code in a loop. A timer can solve this. – IInspectable Mar 28 '20 at 06:22

1 Answers1

1

As a supplement, the screen coordinates returned by GetCursorPos need to be converted to absolute and passed to SendInput:

dx = (x * 65536) / GetSystemMetrics(SystemMetric.SM_CXSCREEN);
dy = (y * 65536) / GetSystemMetrics(SystemMetric.SM_CYSCREEN);

And @IInspectable had provided enough useful information, for this I have created a simple demo for reference only.

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

#define IDT_MOUSETRAP 100

HWND hWnd;
bool toggled = false;
int cps;
HANDLE hThread;
BOOL flag = true;

VOID CALLBACK MyTimerProc(HWND hwnd, UINT message, UINT idTimer, DWORD dwTime)
{
    POINT pntCurrentCursor;
    GetCursorPos(&pntCurrentCursor);

    INPUT inpMouseInput;
    inpMouseInput.type = INPUT_MOUSE;
    inpMouseInput.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_LEFTDOWN;
    inpMouseInput.mi.dx = (pntCurrentCursor.x * 65536) / GetSystemMetrics(SM_CXSCREEN);;
    inpMouseInput.mi.dy = (pntCurrentCursor.y * 65536) / GetSystemMetrics(SM_CYSCREEN);;

    SendInput(1, &inpMouseInput, sizeof(INPUT)); // DOWN

    RtlZeroMemory(&inpMouseInput, sizeof(INPUT));

    inpMouseInput.type = INPUT_MOUSE;
    inpMouseInput.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_LEFTUP;
    inpMouseInput.mi.dx = pntCurrentCursor.x;
    inpMouseInput.mi.dy = pntCurrentCursor.y;
    SendInput(1, &inpMouseInput, sizeof(INPUT)); // UP  
}


DWORD WINAPI MyThreadFunction(LPVOID lpParam)
{
    UINT uResult = SetTimer(hWnd, IDT_MOUSETRAP, 1000 / cps, (TIMERPROC)MyTimerProc);

    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, NULL, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return 0;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static bool bPressedToggle = false;

    DWORD ThreadId;
    switch (message)
    {
    case WM_CREATE:
    {       
        std::cout << "Enter desired clicks per second: ";
        std::cin >> cps;              
    }
    break;
    case WM_KEYDOWN:
    {              
        if (GetKeyState(VK_TAB) < 0)
        {            
            bPressedToggle = true;           
            if (bPressedToggle)
            {
                bPressedToggle = false;
                toggled = !toggled; 

                std::cout << (toggled ? "Toggled" : "Disabled") << std::endl;
            }
            if (toggled == true && flag == true)
            {
                flag = false;
                hThread = CreateThread(NULL, 0, MyThreadFunction, NULL, 0, &ThreadId);
            }
            else if (toggled == false && flag == false)
            {
                KillTimer(hWnd, IDT_MOUSETRAP);                 
                flag = true;
            }
        }

    }
    break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}
void Createyourwindow()
{
    WNDCLASSEXW wcex = { 0 };

    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = GetModuleHandle(NULL);
    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszClassName = L"MyClass";
    RegisterClassExW(&wcex);
    hWnd = CreateWindowW(L"MyClass", L"window", WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, GetModuleHandle(NULL), NULL);


    ShowWindow(hWnd, SW_SHOW);
    UpdateWindow(hWnd);
    MSG msg;

    // Main message loop:
    while (GetMessage(&msg, NULL, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, NULL, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
}

int main()
{     
    Createyourwindow();     
    return 0;
}

Note: Because the message loop of the window is needed, the mouse focus needs to be inside the window when pressing the TAB key.

Strive Sun
  • 5,988
  • 1
  • 9
  • 26