Как я могу установить положение курсора с помощью крючка мыши в Windows?

Я пытаюсь создать небольшое приложение, которое будет определять, когда курсор мыши перемещается к краю экрана, и перемещать его к противоположному краю, чтобы создать непрерывный эффект рабочего стола, если это имеет смысл.

Ниже приведен код от кого-то другого (часть крючка мыши), который я адаптировал, добавив SetCursorPos для перемещения мыши в фиксированное положение на данный момент. Когда я запускаю его, SetCursorPos возвращает true, что, как я предполагаю, означает, что вызов выполнен успешно, но мышь не двигается. Я где-то читал что-то об ограничениях безопасности в более поздних версиях Windows, предотвращающих подобные вещи, что имело бы смысл, но источник был неясен, насколько это правда. Кто-нибудь знает, почему это не сработает?

Спасибо, код ниже:

#define _WIN32_WINNT 0x0400
#pragma comment( lib, "user32.lib" )

#include <windows.h>
#include <stdio.h>

HHOOK hMouseHook;

__declspec(dllexport) LRESULT CALLBACK KeyboardEvent (int nCode, WPARAM wParam, LPARAM lParam)
{
    MOUSEHOOKSTRUCT * pMouseStruct = (MOUSEHOOKSTRUCT *)lParam;
    if (pMouseStruct != NULL)
    {
        if (pMouseStruct->pt.x < -1900)
        {
            BOOL r = SetCursorPos(
                500,
                500
            );

            printf("Trigger %d.  Response %d", pMouseStruct->pt.x, r);
        }
    }

    return CallNextHookEx(hMouseHook, nCode, wParam, lParam);
}

void MessageLoop()
{
    MSG message;
    while (GetMessage(&message,NULL,0,0)) {
        TranslateMessage( &message );
        DispatchMessage( &message );
    }
}

DWORD WINAPI MyMouseLogger(LPVOID lpParm)
{
    HINSTANCE hInstance = GetModuleHandle(NULL);
    if (!hInstance) hInstance = LoadLibrary((LPCSTR) lpParm); 
    if (!hInstance) return 1;

    hMouseHook = SetWindowsHookEx (  
        WH_MOUSE_LL,
        (HOOKPROC) KeyboardEvent,  
        hInstance,                 
        NULL                       
        );
    MessageLoop();
    UnhookWindowsHookEx(hMouseHook);
    return 0;
}

int main(int argc, char** argv)
{
    HANDLE hThread;
    DWORD dwThread;

    hThread = CreateThread(NULL,NULL,(LPTHREAD_START_ROUTINE)
        MyMouseLogger, (LPVOID) argv[0], NULL, &dwThread);
    if (hThread)
        return WaitForSingleObject(hThread,INFINITE);
    else return 1;
}

person Steve Whitfield    schedule 21.01.2013    source источник
comment
Получаете ли вы какой-нибудь вывод из вашей printfs?   -  person drescherjm    schedule 21.01.2013
comment
Также вы можете попробовать что-то большее, чем -1900, просто для проверки.   -  person drescherjm    schedule 21.01.2013
comment
@drescherjm Это -1900, потому что это было примерно на 20 пикселей слева от моего левого монитора. Мой printf выдает следующий Trigger -1917. Ответ 1 и т. Д. Я считаю, что ответ 1 означает, что вызов не завершился ошибкой, но мышь не двигается. Может ли это быть связано с отдельным потоком? Если я вставлю вызов SetCursorPos прямо в начале программы (перед подключением), вызов завершится успешно.   -  person Steve Whitfield    schedule 21.01.2013
comment
Ваша функция KeyboardEvent неверна на 100%. Когда nCode ‹0, вам нужно немедленно вернуть CallNextHookEx. Возможно, это не причина сбоя SetCursorPos, но функция перехвата должна работать согласно документации.   -  person Alex F    schedule 21.01.2013
comment
Многие вещи начинают работать, когда они выполняются асинхронно. Я бы попытался отправить определяемое пользователем сообщение от KeyboardEvent в основной поток приложения и выполнить там SetCursorPos.   -  person Alex F    schedule 21.01.2013
comment
Видел этот код раньше. Я все еще не понимаю, почему вы вызываете функцию обратного вызова крючка мыши KeyboardEvent. Трансляция в MOUSEHOOKSTRUCT просто неверна, обратный вызов хуки мыши низкого уровня получает MSLLHOOKSTRUCT *   -  person Hans Passant    schedule 21.01.2013
comment
Я знаю, что имя функции неправильное и, вероятно, испорчено, но это не похоже на производственный код (просто небольшая игра), и это сработало, когда я впервые запустил его, поэтому решил не исправлять его. Спасибо, Алекс, попробую твое предложение.   -  person Steve Whitfield    schedule 21.01.2013
comment
Прочтите еще раз комментарий Ханса Пассанта о неправильном кастинге.   -  person Alex F    schedule 21.01.2013


Ответы (2)


Похоже, это проблема с вызовом SetCursorPos внутри вашей обработки хуков. Я предполагаю, что это явно запрещено в Vista / Windows 7, но мне не удалось найти никакой документации, подтверждающей это. Я немного изменил ваш код, чтобы опубликовать сообщение в ветке, когда он хочет переместить курсор, и выполнить фактический SetCursorPos внутри вашего сообщения. Как только это будет сделано, он будет работать нормально.

В вашей процедуре подключения:

if (pMouseStruct->pt.x < -1900)
    {
        PostThreadMessage( GetCurrentThreadId(), WM_USER, 0, 0 );
        printf("Trigger %d.  Response %d", pMouseStruct->pt.x, r);
    }

В вашем цикле сообщений:

while (GetMessage(&message,NULL,0,0)) {
    if( message.hwnd == NULL ) {
        if( message.message == WM_USER ) {
            SetWindowPos( 500, 500 );
        }
     } else {
         TranslateMessage( &message );
         DispatchMessage( &message );
     }
}

(Обратите внимание, что это всего лишь демонстрация, а не фактическое исправление.)

При этом существует множество серьезных проблем с вашим кодом. Я не думаю, что уместно вдаваться в подробности здесь, но я рекомендую вам разместить это на https://codereview.stackexchange.com/ < / а>.

person Peter Ruderman    schedule 21.01.2013
comment
Спасибо, Питер, попробовал, и это сработало. Я знаю, что код ужасен, он задумывался как плохой прототип для чего-то, что я, возможно, хотел бы сделать позже, но не мог понять, что SetCursorPos не будет работать. Еще раз спасибо, Стив. PS Про codereview не знал, может пригодится! - person Steve Whitfield; 21.01.2013

Похоже, вам нужно перевести координаты. согласно документам вам необходимо позвонить ClientToScreen или ScreenToClient, чтобы перевести точку. Я не знаю, какое окно является справочным. Вам нужно передать этот дескриптор окна как точку (как выходной параметр) как параметр. а затем используйте измененную точку с SetCursorPos

проверьте пример

Если вам нужен экран, вам нужно сделать GetDC(NULL) и передать возвращенный дескриптор в ClientToScreen

person Neel Basu    schedule 21.01.2013
comment
Извините, я не совсем понимаю. Какие документы вы имеете в виду? Зачем мне окно? Я просто хотел, чтобы это было простое приложение, которое отслеживает глобальное положение мыши. Метод события правильно получает позицию мыши в координатах экрана. SetCursorPos требует экранных координат. Зачем мне переводить? - person Steve Whitfield; 21.01.2013