英文:
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
替代 SendMessage
。SendMessage
适用于带有消息循环的窗口句柄。你还提供了一个窗口句柄为 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 <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;
}
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, &simInput, sizeof(INPUT));
You can retrieve window handles with FindWindow
using either the classname or window name.
HWND hTarget = FindWindowA(NULL,"MyWindowName");
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
标志,鼠标输入事件是在当前鼠标位置注入的。 - “鼠标点击”由两个输入事件组成:鼠标按下,然后是鼠标松开。
SendInput
是mouse_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("SendMessageW failed: %u\n", 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 "mouse click" 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("SendInput failed: %u\n", 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 of0,
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 ofmouse_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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论