通过C++可以模拟鼠标点击和键盘输入的操作,进而可以实现一些比较有趣的功能。
GetAsyncKeyState函数适用于鼠标按钮。但是,它会检查物理鼠标按钮的状态,而不是物理按钮映射到的逻辑鼠标按钮。例如,调用GetAsyncKeyState (VK_LBUTTON) 始终返回物理鼠标左键的状态,无论它是映射到逻辑鼠标左键还是逻辑鼠标右键。
SHORT GetAsyncKeyState(
[in] int vKey
);
#define KEY_DOWN(VK_NONAME) ((GetAsyncKeyState(VK_NONAME) & 0x8000) ? 1:0)
#define KEYDOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
#define KEYUP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)
while(TRUE){
printf("鼠标左键是否按下:");
if(KEY_DOWN(VK_LBUTTON))printf("是");
else printf("否");
printf("\n");
printf("鼠标右键是否按下:");
if(KEY_DOWN(VK_RBUTTON))printf("是");
else printf("否");
printf("\n");
printf("鼠标滚轮键是否按下:");
if(KEY_DOWN(VK_MBUTTON))printf("是");
else printf("否");
printf("\n");
Sleep(10);
system("cls");
}
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
switch (msg.message)
{
case WM_KEYDOWN:
if ((GetAsyncKeyState(VK_ESCAPE) & 0x01) && bRunning)
{
Stop();
}
break;
}
}
检索指定虚拟键的状态。状态指定按键是向上、向下还是切换(开、关——每次按下键时交替)。
一个虚拟钥匙。如果所需的虚拟键是字母或数字(A 到 Z、a 到 z 或 0 到 9), 则 nVirtKey必须设置为该字符的 ASCII 值。对于其他键,它必须是虚拟键码。
返回值指定指定虚拟键的状态,如下:
如果高位为1,则key为down;否则,它就起来了。
如果低位为 1,则键被切换。某个键(例如 CAPS LOCK 键)在打开时会被切换。如果低位为 0,则该键处于关闭状态且未切换。切换键时键盘上的切换键指示灯(如果有)将亮起,当键未切换时将熄灭。
应用程序调用GetKeyState以响应键盘输入消息。此函数在生成输入消息时检索键的状态。要检索所有虚拟键的状态信息,请使用GetKeyboardState函数。
要检索单个键的状态信息,请使用GetKeyState函数。无论是否已从消息队列中检索到相应的键盘消息,要检索单个键的当前状态,请使用GetAsyncKeyState函数。
注意:::GetKeyState()只能在键盘消息处理程序中使用,因为它只有在线程从消息队列中读取键盘消息时才会报告被查询键的状态,如果需要在键盘消息处理程序以外查询按键状态,则需要使用::GetAsyncKeyState()来代替。
SHORT GetKeyState(
[in] int nVirtKey
);
#define KEY_ISPRESSED_CTRL (0x8000 & GetKeyState(VK_CONTROL)) != 0
#define KEY_ISPRESSED_SHIFT (0x8000 & GetKeyState(VK_SHIFT)) != 0
#define KEY_ISPRESSED_ALT (0x8000 & GetKeyState(VK_MENU)) != 0
#define KEY_ISPRESSED(Key) (GetKeyState(Key) & 0x8000) != 0
#define IsKeyPressed(nVirtKey) ((GetKeyState(nVirtKey) & (1<<(sizeof(SHORT)*8-1))) != 0)
#define IsKeyToggled(nVirtKey) ((GetKeyState(nVirtKey) & 1) != 0)
mouse_event函数合成鼠标运动和按钮点击。
::GetCursorPos,获取当前鼠标的位置;
::SetCursorPos,移动鼠标到某一位置;
mouse_event,鼠标动作,单击等。
mouse_even只能够发送前台消息,即仅对当前激活的窗体有效。它最好配合SetCursorPos(x,y)函数一起使用。
void mouse_event(
[in] DWORD dwFlags,
[in] DWORD dx,
[in] DWORD dy,
[in] DWORD dwData,
[in] ULONG_PTR dwExtraInfo
);
POINT lpPoint;
GetCursorPos(&lpPoint);
SetCursorPos(lpPoint.x, lpPoint.y);
mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);
mouse_event(MOUSEEVENTF_LEFTUP,0,0,0,0);
::SetCursorPos(10,10);
mouse_event(MOUSEEVENTF_LEFTDOWN|MOUSEEVENTF_LEFTUP,0,0,0,0);是在当前位置单击一次
class MOUSE
{
private:
//坐标变量
POINT point;
public:
//移动类函数
void Move(int x,int y);
void RelativeMove(int cx,int cy);
void SavePos();
void RestorePos();
//锁定启用类
void Lock();
void Unlock();
//动作类
void LBClick();
void LBDbClick();
void LBDown();
void LBUp();
void RBClick();
void RBDbClick();
void RBDown();
void RBUp();
void MBClick();
void MBDbClick();
void MBDown();
void MBUp();
void MBRoll(int ch);
};
//移动鼠标到绝对位置(X坐标,Y坐标)
void MOUSE::Move(int x,int y)
{
this->point.x=x;
this->point.y=y;
::SetCursorPos(x,y);
}
//移动鼠标到相对位置(X位移,Y位移)
void MOUSE::RelativeMove(int cx,int cy)
{
::GetCursorPos(&this->point);
this->point.x+=cx;
this->point.y+=cy;
::SetCursorPos(this->point.x,this->point.y);
}
//保存当前位置()
void MOUSE::SavePos()
{
::GetCursorPos(&this->point);
}
//恢复鼠标位置()
void MOUSE::RestorePos()
{
::SetCursorPos(this->point.x,this->point.y);
}
//锁定鼠标()
void MOUSE::Lock()
{
POINT pt;
RECT rt;
::GetCursorPos(&pt);
rt.left=rt.right=pt.x;
rt.top=rt.bottom=pt.y;
rt.right++;
rt.bottom++;
::ClipCursor(&rt);
}
//解锁鼠标()
void MOUSE::Unlock()
{
::ClipCursor(NULL);
}
//左键单击()
void MOUSE::LBClick()
{
this->SavePos();
::mouse_event(MOUSEEVENTF_LEFTDOWN|MOUSEEVENTF_LEFTUP,this->point.x,this->point.y,0,0);
}
//左键双击()
void MOUSE::LBDbClick()
{
this->SavePos();
::mouse_event(MOUSEEVENTF_LEFTDOWN|MOUSEEVENTF_LEFTUP,this->point.x,this->point.y,0,0);
::mouse_event(MOUSEEVENTF_LEFTDOWN|MOUSEEVENTF_LEFTUP,this->point.x,this->point.y,0,0);
}
//左键按下()
void MOUSE::LBDown()
{
this->SavePos();
::mouse_event(MOUSEEVENTF_LEFTDOWN,this->point.x,this->point.y,0,0);
}
//左键抬起()
void MOUSE::LBUp()
{
this->SavePos();
::mouse_event(MOUSEEVENTF_LEFTUP,this->point.x,this->point.y,0,0);
}
//右键单击()
void MOUSE::RBClick()
{
this->SavePos();
::mouse_event(MOUSEEVENTF_RIGHTDOWN|MOUSEEVENTF_RIGHTUP,this->point.x,this->point.y,0,0);
}
//右键双击()
void MOUSE::RBDbClick()
{
this->SavePos();
::mouse_event(MOUSEEVENTF_RIGHTDOWN|MOUSEEVENTF_RIGHTUP,this->point.x,this->point.y,0,0);
::mouse_event(MOUSEEVENTF_RIGHTDOWN|MOUSEEVENTF_RIGHTUP,this->point.x,this->point.y,0,0);
}
//右键按下()
void MOUSE::RBDown()
{
this->SavePos();
::mouse_event(MOUSEEVENTF_RIGHTDOWN,this->point.x,this->point.y,0,0);
}
//右键抬起()
void MOUSE::RBUp()
{
this->SavePos();
::mouse_event(MOUSEEVENTF_RIGHTUP,this->point.x,this->point.y,0,0);
}
//中键单击()
void MOUSE::MBClick()
{
this->SavePos();
::mouse_event(MOUSEEVENTF_MIDDLEDOWN|MOUSEEVENTF_MIDDLEUP,this->point.x,this->point.y,0,0);
}
//中键双击()
void MOUSE::MBDbClick()
{
this->SavePos();
::mouse_event(MOUSEEVENTF_MIDDLEDOWN|MOUSEEVENTF_MIDDLEUP,this->point.x,this->point.y,0,0);
::mouse_event(MOUSEEVENTF_MIDDLEDOWN|MOUSEEVENTF_MIDDLEUP,this->point.x,this->point.y,0,0);
}
//中键按下()
void MOUSE::MBDown()
{
this->SavePos();
::mouse_event(MOUSEEVENTF_MIDDLEDOWN,this->point.x,this->point.y,0,0);
}
//中键抬起()
void MOUSE::MBUp()
{
this->SavePos();
::mouse_event(MOUSEEVENTF_MIDDLEUP,this->point.x,this->point.y,0,0);
}
//中键滚动(滚动位移)
void MOUSE::MBRoll(int ch)
{
this->SavePos();
::mouse_event(MOUSEEVENTF_WHEEL,this->point.x,this->point.y,ch,0);
}
合成击键。系统可以使用这样的合成击键来生成WM_KEYUP或WM_KEYDOWN消息。键盘驱动程序的中断处理程序调用keybd_event函数。
注意:此功能已被取代。请改用SendInput。
应用程序可以模拟按下 PRINTSCRN 键以获得屏幕快照并将其保存到剪贴板。为此,调用keybd_event并将 bVk参数设置为VK_SNAPSHOT。
void keybd_event(
[in] BYTE bVk,
[in] BYTE bScan,
[in] DWORD dwFlags,
[in] ULONG_PTR dwExtraInfo
);
以下示例程序通过使用带有VK_NUMLOCK虚拟键的keybd_event来切换 NUM LOCK 灯。它采用一个布尔值,指示灯应该关闭 ( FALSE ) 还是打开 ( TRUE )。相同的技术可用于 CAPS LOCK 键 ( VK_CAPITAL ) 和 SCROLL LOCK 键 ( VK_SCROLL )。
#include
void SetNumLock( BOOL bState )
{
BYTE keyState[256];
GetKeyboardState((LPBYTE)&keyState);
if( (bState && !(keyState[VK_NUMLOCK] & 1)) ||
(!bState && (keyState[VK_NUMLOCK] & 1)) )
{
// Simulate a key press
keybd_event( VK_NUMLOCK,
0x45,
KEYEVENTF_EXTENDEDKEY | 0,
0 );
// Simulate a key release
keybd_event( VK_NUMLOCK,
0x45,
KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP,
0);
}
}
void main()
{
SetNumLock( TRUE );
}
class KEYBOARD
{
public:
void PressStr(char *str);
void PressKey(BYTE bVk);
void KeyDown(BYTE bVk);
void KeyUp(BYTE bVk);
};
//按键(虚拟键值)
void KEYBOARD::PressKey(BYTE bVk)
{
::keybd_event(bVk,0,0,0);
::keybd_event(bVk,0,KEYEVENTF_KEYUP,0);
}
//按下(虚拟键值)
void KEYBOARD::KeyDown(BYTE bVk)
{
::keybd_event(bVk,0,0,0);
}
//抬起(虚拟键值)
void KEYBOARD::KeyUp(BYTE bVk)
{
::keybd_event(bVk,0,KEYEVENTF_KEYUP,0);
}
//发送字符串(字符串)
void KEYBOARD::PressStr(char *str)
{
for (unsigned i=0;i
{
if (str[i]>0x60 && str[i]<0x7B)
this->PressKey(str[i]-0x20);
else
this->PressKey(str[i]);
}
}
合成击键、鼠标动作和按钮点击。
SendInput函数将 INPUT 结构中的事件串行插入到键盘或鼠标输入流中。这些事件不会与用户(使用键盘或鼠标)或通过调用keybd_event、mouse_event或对SendInput的其他调用插入的其他键盘或鼠标输入事件穿插。
此功能不会重置键盘的当前状态。调用该函数时已按下的任何键都可能会干扰该函数生成的事件。为避免此问题,请使用GetAsyncKeyState函数检查键盘的状态并根据需要进行更正。
UINT SendInput(
[in] UINT cInputs,
[in] LPINPUT pInputs,
[in] int cbSize
);
//**********************************************************************
//
// Sends Win + D to toggle to the desktop
//
//**********************************************************************
void ShowDesktop()
{
OutputString(L"Sending 'Win-D'\r\n");
INPUT inputs[4] = {};
ZeroMemory(inputs, sizeof(inputs));
inputs[0].type = INPUT_KEYBOARD;
inputs[0].ki.wVk = VK_LWIN;
inputs[1].type = INPUT_KEYBOARD;
inputs[1].ki.wVk = VK_D;
inputs[2].type = INPUT_KEYBOARD;
inputs[2].ki.wVk = VK_D;
inputs[2].ki.dwFlags = KEYEVENTF_KEYUP;
inputs[3].type = INPUT_KEYBOARD;
inputs[3].ki.wVk = VK_LWIN;
inputs[3].ki.dwFlags = KEYEVENTF_KEYUP;
UINT uSent = SendInput(ARRAYSIZE(inputs), inputs, sizeof(INPUT));
if (uSent != ARRAYSIZE(inputs))
{
OutputString(L"SendInput failed: 0x%x\n", HRESULT_FROM_WIN32(GetLastError()));
}
}
//鼠标移动到指定位置
void MouseMove(int x, int y)
{
double fScreenWidth = ::GetSystemMetrics(SM_CXSCREEN) - 1;//获取屏幕分辨率宽度
double fScreenHeight = ::GetSystemMetrics(SM_CYSCREEN) - 1;//获取屏幕分辨率高度
double fx = x*(65535.0f / fScreenWidth);
double fy = y*(65535.0f / fScreenHeight);
INPUT Input = { 0 };
Input.type = INPUT_MOUSE;
Input.mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;
Input.mi.dx = fx;
Input.mi.dy = fy;
SendInput(1, &Input, sizeof(INPUT));
}
//鼠标左键按下
void MouseLeftDown()
{
INPUT Input = { 0 };
Input.type = INPUT_MOUSE;
Input.mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
SendInput(1, &Input, sizeof(INPUT));
}
//鼠标左键放开
void MouseLeftUp()
{
INPUT Input = { 0 };
Input.type = INPUT_MOUSE;
Input.mi.dwFlags = MOUSEEVENTF_LEFTUP;
SendInput(1, &Input, sizeof(INPUT));
}
//鼠标右键按下
void MouseRightDown()
{
INPUT Input = { 0 };
Input.type = INPUT_MOUSE;
Input.mi.dwFlags = MOUSEEVENTF_RIGHTDOWN;
SendInput(1, &Input, sizeof(INPUT));
}
//鼠标右键放开
void MouseRightUp()
{
INPUT Input = { 0 };
Input.type = INPUT_MOUSE;
Input.mi.dwFlags = MOUSEEVENTF_RIGHTUP;
SendInput(1, &Input, sizeof(INPUT));
}
当用户在键盘上键入时,具有键盘焦点的窗口的窗口过程接收击键消息。击键消息是WM_KEYDOWN、WM_KEYUP、WM_SYSKEYDOWN和WM_SYSKEYUP。典型的窗口过程会忽略除WM_KEYDOWN之外的所有击键消息。当用户按下某个键时,系统会发布WM_KEYDOWN消息。
当窗口过程收到WM_KEYDOWN消息时,它应该检查伴随消息的虚拟键代码以确定如何处理击键。虚拟键代码在消息的wParam参数中。通常,应用程序只处理由非字符键生成的击键,包括功能键、光标移动键和特殊用途键,如 INS、DEL、HOME 和 END。
这种方法不需要窗体在前端,甚至最小化也可以使用,但是此方法并不是在所有场合有效,特别是对于不响应鼠标消息的程序更是如此。在这种情况下,可以尝试使用mouse_event函数。
将指定的消息发送到一个或多个窗口。SendMessage函数调用指定窗口的窗口过程,并且在窗口过程处理完消息后才返回。
要发送消息并立即返回,请使用SendMessageCallback或SendNotifyMessage函数。要将消息发布到线程的消息队列并立即返回,请使用PostMessage或PostThreadMessage函数。
LRESULT SendMessage(
[in] HWND hWnd,
[in] UINT Msg,
[in] WPARAM wParam,
[in] LPARAM lParam
);
在与创建指定窗口的线程关联的消息队列中放置(发布)一条消息,并在不等待线程处理消息的情况下返回。要在与线程关联的消息队列中发布消息,请使用PostThreadMessage函数。
BOOL PostMessageA(
[in, optional] HWND hWnd,
[in] UINT Msg,
[in] WPARAM wParam,
[in] LPARAM lParam
);
::SetCursorPos(x,y);
if(IsMineIn(x, y)==TRUE){
/*::mouse_event(MOUSEEVENTF_RIGHTDOWN,x,y,0,0);
::mouse_event(MOUSEEVENTF_RIGHTUP,x,y,0,0); */
pWnd->SendMessage(WM_RBUTTONDOWN,0,(y<<16)|x);
pWnd->SendMessage(WM_RBUTTONUP,0,(y<<16)|x);
}else{
/*::mouse_event(MOUSEEVENTF_LEFTDOWN,x,y,0,0);
::mouse_event(MOUSEEVENTF_LEFTUP,x,y,0,0);*/
pWnd->SendMessage(WM_LBUTTONDOWN,0,(y<<16)|x);
pWnd->SendMessage(WM_LBUTTONUP,0,(y<<16)|x);
}
https://docs.microsoft.com/zh-cn/visualstudio/debugger/introducing-spy-increment?view=vs-2022
Spy++ 有两个版本。 第一个版本,名为 Spy++ (spyxx.exe),用于显示发送到在 32 位进程中运行的窗口的消息。 例如,在 32 位进程中运行的 Visual Studio。 因此,可以使用 Spy++ 来显示发送到“解决方案资源管理器” 中的消息。 由于 Visual Studio 中大多数生成的默认配置都是在 32 位进程中运行的,因此如果已安装所需组件,则第一个版本的 Spy++ 就是在 Visual Studio 中的“工具”菜单上可用的那一个。
第二个版本,名为 Spy++(64 位)(spyxx_amd64.exe),用于显示发送到在 64 位进程中运行的窗口的消息。 例如,在 64 位操作系统上,记事本在 64 位进程中运行。 因此,可以使用 Spy++(64 位)来显示发送到记事本的消息。 Spy++ (64 位)通常位于…\Visual Studio 安装文件夹\Common7\Tools\spyxx_amd64.exe。
https://docs.microsoft.com/zh-cn/windows/win32/winauto/inspect-objects
检查 (Inspect.exe) 是基于Windows的工具,可用于选择任何 UI 元素并查看元素的辅助功能数据。 可以查看 Microsoft UI 自动化 属性和控制模式,以及 Microsoft Active Accessibility (MSAA) 属性。 通过检查,还可以测试UI 自动化树中自动化元素的导航结构,以及 Microsoft Active Accessibility 层次结构中的可访问对象。
如果您觉得该方法或代码有一点点用处,可以给作者点个赞,或打赏杯咖啡;
╮( ̄▽ ̄)╭
如果您感觉方法或代码不咋地
//(ㄒoㄒ)//,就在评论处留言,作者继续改进;
o_O???
如果您需要相关功能的代码定制化开发,可以留言私信作者;
(✿◡‿◡)
感谢各位大佬童鞋们的支持!
( ´ ▽´ )ノ ( ´ ▽´)っ!!!