如何在Windows中通过鼠标钩子设置光标位置?

3
我正在尝试创建一个小应用程序,可以检测鼠标光标何时移动到屏幕边缘,并将其移动到相反的边缘,以创建连续的桌面效果(如果有意义的话)。
以下是来自他人的一些代码(鼠标钩子部分),我通过添加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;
}

你可能想尝试一些更大的数字来进行测试,而不仅仅是-1900。 - drescherjm
@drescherjm 这是-1900,因为它在我左手监视器的左边大约20个像素。我的printf输出以下内容:“Trigger -1917. Response 1”等等。我认为“Response 1”表示调用没有失败,但鼠标没有移动。这可能与单独的线程有关吗?如果我在程序开始时(钩子之前)插入SetCursorPos调用,则调用成功。 - Steve Whitfield
你的KeyboardEvent函数并不完全正确。当nCode < 0时,你需要立即返回CallNextHookEx。也许这不是SetCursorPos失败的原因,但挂钩函数必须按照文档工作。 - Alex F
当许多事情异步执行时,它们开始工作。我会尝试从KeyboardEvent向主应用程序线程发布用户定义的消息,并在那里执行SetCursorPos。 - Alex F
请再次阅读Hans Passant关于错误类型转换的评论。 - Alex F
显示剩余3条评论
2个回答

4
这似乎是在钩子过程中调用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/上。


谢谢Peter,我试了一下,它起作用了。我知道代码很糟糕,只是想做一个不好的原型,以后可能会制作其他东西,但我无法弄清楚为什么SetCursorPos不起作用。再次感谢,Steve。顺便说一句,我不知道codereview,肯定会很有用! - Steve Whitfield

0

看起来你需要翻译坐标。 根据文档,你需要调用ClientToScreenScreenToClient来转换点。我不知道哪个窗口是参考。你需要将该窗口句柄作为参数传递给点(作为输出参数),然后使用修改后的点与SetCursorPos一起使用。

请查看示例

如果你想要屏幕,你需要执行GetDC(NULL)并将返回的句柄传递给ClientToScreen


抱歉,我不确定我理解了。您指的是哪些文档?我为什么需要一个窗口?我只想让这个应用程序成为一个简单的监视全局鼠标位置的应用程序。事件方法正确地接收屏幕坐标中的鼠标位置。SetCursorPos需要屏幕坐标。我为什么需要翻译? - Steve Whitfield

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接