其实在写Minecraft-AFK之前我是没有接触过WindowsAPI的,也是在写这个程序时才了解到。不得不说自学的道路有些坎坷,踩了无数的坑。在此记录一下,一来自己忘记时可以翻翻博客,二来如果有人有疑问,也有和我一样踩了类似的坑,我这里也可以给他一些启发。由于对于WindowsAPI完全不了解,这是在里面选取对自己有用的东西,所以有些讲解可能不准确,如果有人能够指出来,我在这里表示感谢。
获取窗口句柄
简单说一下就行,获取窗口句柄比较简单。
既然是要后台挂机,那么肯定是向窗口发送消息,而不是向全局。窗口句柄可以看做是一个窗口的标识符,windowsAPI中保存窗口句柄的结构类型是HWND。
发送鼠标点击的消息
总体来说鼠标点击的实现还是很简单的,没有多少坑,简单讲一下。
发送鼠标消息有四种: keybd_event,SendInput,SendMessage,PostMessage。前两者发送全局的鼠标点击消息,而后两者通过窗口句柄发送鼠标点击的消息,以实现后台挂机。
SendMessage,PostMessage两者的参数是一样的:
HWND hWnd —— 要发送的窗口句柄
UINT Msg —— 要发送的消息
WPARAM wParam —— 附加消息wParam
LPARAM lParam —— 附加消息lParam
以SendMessage示例,PostMessage同理:
SendMessage(hwnd, WM_LBUTTONDOWN, 0, 0);
SendMessage(hwnd, WM_LBUTTONUP, 0, 0);
这样就可以发送鼠标抬起再按下的消息。
WM_LBUTTONDOWN和WM_LBUTTONUP是左键按下和抬起的消息,这两个参数的wParam附加消息表示是否同时按下其他系统按键,lParam则表示鼠标的坐标。在minecraft窗口中这两个都不用设置就行。
模拟发送按键消息
函数的选择
上文说过SendMessage和PostMessage可以向窗口发送按键消息。但是注意,键盘按键的消息只能用PostMessage。
在这里说一下SendMessage和PostMessage的区别。SendMessage发送了消息后会等待消息返回才结束,而PostMessage发送完消息就会结束。而键盘消息是没有返回值的,所以SendMessage无效。
具体实现
WM_KEYDOWN和WM_KEYUP消息分别模拟键盘的按下和抬起,wParam附加消息含有一个按键的虚拟码,表示需要操作的是哪个按键。
根据上文所说,我们可以写出下面的语句(刚开始我也是这么写的):
int vkCode = VkKeyScan('a');
PostMessage(hwnd, WM_KEYDOWN, vkCode, 0);
PostMessage(hwnd, WM_KEYUP, vkCode, 0);
我在其他窗口上测试成功,但是在Minecraft窗口上就不行,原来MC检测键盘是否按下不仅会检测虚拟码,还要检测扫描码。幸运的是,PostMessage是可以发送扫描码信息的,这个信息包含在lParam消息中。
lParam是一个32位的数,MSDN中有 WM_KEYDOWN 和 WM_KEUP 的lParam具体解释。
按照上面解释,可以写出如下代码:
int get_Lparam(int vk, bool flag)
{
int scanCode = MapVirtualKey(vk, MAPVK_VK_TO_VSC);
return flag | (scanCode << 16) | (flag << 30) | (flag << 31);
}
vk表示按键的虚拟码。
MapVirtualKey(vk, MAPVK_VK_TO_VSC) 可以获取对应虚拟码的扫描码。
flag为true表示WM_KEUP,为false表示WM_KEYDOWN。
于是我们再修改上面的代码:
PostMessage(hwnd, WM_KEYDOWN, vkCode, get_Lparam(vkCode, false) );
PostMessage(hwnd, WM_KEYUP, vkCode, get_Lparam(vkCode, true) );
这样在MC中就可以正常运行了。
写在最后
这些便是目前写Minecraft-AFK踩过的一些坑,这是我写给自己看的,记录自己写代码时 犯傻 遇到的困难,如果对大家有所帮助, 可以到我的Minecraft-AFK项目去点个star。