发送消息,点击光标焦点处。

huangapple go评论79阅读模式
英文:

SendMessage click where the cursor focus

问题

我想使用控制台应用程序来自动左键点击鼠标,所以我编写了以下代码:

int main()
{	
	Sleep(2000);
	
	SendMessage(0, WM_LBUTTONDOWN, 0, 0); 

return 0;
}

但是这不起作用,我的鼠标没有点击任何东西,我使用的是Windows 10,有什么建议吗?

尝试点击我放置鼠标光标的位置

英文:

I want use a console application to click left with my mouse automatically, so i do this code:

int main()
{	
	Sleep(2000);
	
	SendMessage(0, WM_LBUTTONDOWN, 0, 0); 

return 0;
}

and this is not work, my mouse not click on anything, am on windows 10, any idea please

Trying to click where i put my mouse cursor

答案1

得分: 4

使用 SendInput 替代 SendMessageSendMessage 适用于带有消息循环的窗口句柄。你还提供了一个窗口句柄为 0,这会将目标定位到你的控制台。

#include <Windows.h>
int main()
{
	INPUT simInput = { 0 };
	simInput.type = INPUT_MOUSE;
	simInput.mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
	SendInput(1, &simInput, sizeof(INPUT));
	
	simInput.mi.dwFlags = MOUSEEVENTF_LEFTUP;
	SendInput(1, &simInput, sizeof(INPUT));

	return 0;
}

这将首先模拟鼠标左键按下事件,然后模拟鼠标左键释放事件。

如果你想定位到特定坐标,可以在 INPUT 结构中指定坐标。

	INPUT simInput = { 0 };
	simInput.type = INPUT_MOUSE;
	
	simInput.mi.dx = 0; //x 坐标
	simInput.mi.dy = 0; //y 坐标

	simInput.mi.dwFlags = MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_ABSOLUTE ;

	SendInput(1, &simInput, sizeof(INPUT));

你可以使用 FindWindow 使用类名或窗口名称检索窗口句柄。

HWND hTarget = FindWindowA(NULL, "MyWindowName");
SendMessage(hTarget, WM_LBUTTONDOWN, 0, 0);
英文:

Use SendInput instead of SendMessage. SendMessage is geared towards window handles with message loops. You're also providing a window handle of 0, which is targeting your console.

#include &lt;Windows.h&gt;
int main()
{
	INPUT simInput = { 0 };
	simInput.type = INPUT_MOUSE;
	simInput.mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
	SendInput(1, &amp;simInput, sizeof(INPUT));
	
	simInput.mi.dwFlags = MOUSEEVENTF_LEFTUP;
	SendInput(1, &amp;simInput, sizeof(INPUT));

	return 0;
}

This will first simulate a down event on the left button. Then simulate an up event (mouse release) on the left button.

if you want to target specific coordinates you can tell the INPUT structure the coordinates.

	INPUT simInput = { 0 };
	simInput.type = INPUT_MOUSE;
	
	simInput.mi.dx = 0; //xposition
	simInput.mi.dy = 0; //yposition

	simInput.mi.dwFlags = MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_ABSOLUTE ;

	SendInput(1, &amp;simInput, sizeof(INPUT));

You can retrieve window handles with FindWindow using either the classname or window name.

HWND hTarget = FindWindowA(NULL,&quot;MyWindowName&quot;);
SendMessage(hTarget,WM_LBUTTONDOWN,0,0);

答案2

得分: 1

我的鼠标无法点击任何东西

这是可以预料的。该代码将0作为SendMessageW的接收者传递,这不是一个有效的窗口句柄。通过适当的错误处理,这将很容易看到:

int main() {
    ::SetLastError(0);
    auto const result = ::SendMessageW(0, WM_LBUTTONDOWN, 0, 0);
    if (result == 0) {
        auto const err = ::GetLastError();
        if (err != ERROR_SUCCESS) {
            printf("SendMessageW failed: %u\n", err);
       }
    }
}

当执行此程序时,它会打印以下内容:

SendMessageW failed: 1400

错误代码 1400 具有符号常量 ERROR_INVALID_WINDOW_HANDLE,翻译为 "无效的窗口句柄。" 这正是当传递无效的窗口句柄时所期望的结果。

现在你知道了问题出在哪里,可能会尝试修复即时问题并继续下去。但这是行不通的。正如我们现在应该都知道的那样:你不能使用PostMessage模拟键盘输入。相同的原则也适用于鼠标输入,而将PostMessage替换为SendMessage仅确保在目标线程挂起时,调用线程停止向前进展。重播输入并不等于重新处理它涵盖了在追求不适当解决方案时不可避免遇到的更多问题。

你要做的取决于最终目标。如果问题可以直接解决,假设它不隐藏任何其他要求,那么解决方案是使用SendInput API来合成输入:

int main() {
    ::Sleep(2000);

    // 使用`SendInput`注入“鼠标点击”
    INPUT inp[] = { { .type = INPUT_MOUSE, .mi = { .dwFlags = MOUSEEVENTF_LEFTDOWN } },
                    { .type = INPUT_MOUSE, .mi = { .dwFlags = MOUSEEVENTF_LEFTUP } } };
    constexpr auto count = ARRAYSIZE(inp);
    auto const result = ::SendInput(count, inp, sizeof(INPUT));
    if (result == 0) {
        auto const err = ::GetLastError();
        printf("SendInput failed: %u\n", err);
    }
}

这里有一些不太明显的细节:

  • INPUT结构在未显式命名的字段之外都被初始化为零(这是使用C++20的指定初始化语法,仅是为了提高可读性)。
  • 由于未设置MOUSEEVENTF_ABSOLUTE标志,鼠标输入事件是在当前鼠标位置注入的。
  • “鼠标点击”由两个输入事件组成:鼠标按下,然后是鼠标松开。SendInputmouse_event的改进版本,允许将多事件输入作为原子操作注入,因此它们不会与其他输入源的事件交错。将单元素数组传递给SendInput是个bug吗?介绍了细节。

从系统的角度来看,注入的输入与由物理输入设备生成的输入没有区别。在“鼠标光标当前位置的鼠标点击”的情况下,后果是当前鼠标位置下的窗口成为前台窗口,而之前的前台窗口失去前台激活状态。

如果这不是你想要的,那么除了忽略自定义自动化接口之外,你唯一的选择就是使用UI自动化

如果最终目标是欺骗游戏认为你非常出色,那么这是一个完全不同的挑战。我之前在这里详细介绍了涉及的复杂性。

英文:

> my mouse not click on anything

That is to be expected. The code is passing 0 as the recipient of SendMessageW, which isn't a valid window handle. With proper error handling this would have been easy to see:

int main() {
    ::SetLastError(0);
    auto const result = ::SendMessageW(0, WM_LBUTTONDOWN, 0, 0);
    if (result == 0) {
        auto const err = ::GetLastError();
        if (err != ERROR_SUCCESS) {
            printf(&quot;SendMessageW failed: %u\n&quot;, err);
       }
    }
}

When executed, this program prints the following:

> SendMessageW failed: 1400

Error code 1400 has the symbolic constant ERROR_INVALID_WINDOW_HANDLE, and translates to "Invalid window handle." Precisely what one would expect, when passing an invalid window handle.

Now that you know what's wrong, it would be tempting to fix the immediate issue, and continue down that route. But that's not going to work. As we all (should) know (by now): You can't simulate keyboard input with PostMessage. The same principles apply to mouse input, and exchanging PostMessage with SendMessage merely makes sure that the calling thread stops making forward progress when the destination thread hangs. Replaying input is not the same as reprocessing it covers more issues you'll invariably run into when pursuing a solution that isn't.

What you'll want to do instead depends on the ultimate goal. If the question is to be taken at face value, assuming that it doesn't conceal any additional requirements, the solution is to synthesize input using the SendInput API:

int main() {
    ::Sleep(2000);

    // Inject &quot;mouse click&quot; using `SendInput`
    INPUT inp[] = { { .type = INPUT_MOUSE, .mi = { .dwFlags = MOUSEEVENTF_LEFTDOWN } },
                    { .type = INPUT_MOUSE, .mi = { .dwFlags = MOUSEEVENTF_LEFTUP } } };
    constexpr auto count = ARRAYSIZE(inp);
    auto const result = ::SendInput(count, inp, sizeof(INPUT));
    if (result == 0) {
        auto const err = ::GetLastError();
        printf(&quot;SendInput failed: %u\n&quot;, err);
    }
}

There are a few details here that aren't immediately obvious:

  • The INPUT structures are zero-initialized except for the fields explicitly named (this is using C++20 designated initializer syntax, just because lengthy sequences of 0, don't cut it for readability).
  • Since the MOUSEEVENTF_ABSOLUTE flag isn't set, the mouse input event is injected using the current mouse position.
  • A "mouse click" consists of two input events: Mouse down, followed by mouse up. SendInput is an improved version of mouse_event, that allows injecting multi-event input as an atomic operation, so they won't get interspersed with events from other input sources. Is it a bug to pass a single-element array to SendInput? covers the details.

As far as the system is concerned, injected input is treated no different from input generated by a physical input device. In case of a "mouse click at the current mouse cursor position" the consequence is, that the window under the current mouse position becomes the foreground window, and the previous foreground window loses foreground activation.

If that is not something you want, your only other option (ignoring custom automation interfaces) is to use UI Automation.

If the ultimate goal is to cheat a game into thinking that you totally rock, then that's a whole different challenge. I covered the complexities involved previously here.

huangapple
  • 本文由 发表于 2023年6月26日 07:25:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/76552787.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定