文章目录
- 1、简介
- 2、GetAsyncKeyState
- 3. GetKeyState
- 4、mouse_event
- 5、keybd_event
- 6、SendInput
- 7、SendMessage/PostMessage
- 8、辅助工具
- 结语
1、简介
通过C++可以模拟鼠标点击和键盘输入的操作,进而可以实现一些比较有趣的功能。
2、GetAsyncKeyState
GetAsyncKeyState函数适用于鼠标按钮。但是,它会检查物理鼠标按钮的状态,而不是物理按钮映射到的逻辑鼠标按钮。例如,调用GetAsyncKeyState (VK_LBUTTON) 始终返回物理鼠标左键的状态,无论它是映射到逻辑鼠标左键还是逻辑鼠标右键。
- 确定在调用函数时某个键是向上还是向下,以及在上一次调用GetAsyncKeyState之后是否按下了该键。
- 您可以使用虚拟键代码常量VK_SHIFT、VK_CONTROL和VK_MENU作为 vKey参数的值。这给出了 SHIFT、CTRL 或 ALT 键的状态,而不区分左右。
2.1 函数声明
- 函数声明如下:
SHORT GetAsyncKeyState(
[in] int vKey
);
2.2 宏定义
#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");
}
2.3 代码示例
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;
}
}
3. GetKeyState
检索指定虚拟键的状态。状态指定按键是向上、向下还是切换(开、关——每次按下键时交替)。
-
一个虚拟钥匙。如果所需的虚拟键是字母或数字(A 到 Z、a 到 z 或 0 到 9), 则 nVirtKey必须设置为该字符的 ASCII 值。对于其他键,它必须是虚拟键码。
-
返回值指定指定虚拟键的状态,如下:
如果高位为1,则key为down;否则,它就起来了。
如果低位为 1,则键被切换。某个键(例如 CAPS LOCK 键)在打开时会被切换。如果低位为 0,则该键处于关闭状态且未切换。切换键时键盘上的切换键指示灯(如果有)将亮起,当键未切换时将熄灭。 -
应用程序调用GetKeyState以响应键盘输入消息。此函数在生成输入消息时检索键的状态。要检索所有虚拟键的状态信息,请使用GetKeyboardState函数。
要检索单个键的状态信息,请使用GetKeyState函数。无论是否已从消息队列中检索到相应的键盘消息,要检索单个键的当前状态,请使用GetAsyncKeyState函数。
注意:::GetKeyState()只能在键盘消息处理程序中使用,因为它只有在线程从消息队列中读取键盘消息时才会报告被查询键的状态,如果需要在键盘消息处理程序以外查询按键状态,则需要使用::GetAsyncKeyState()来代替。
3.1 函数声明
SHORT GetKeyState(
[in] int nVirtKey
);
3.2 宏定义
#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)
4、mouse_event
mouse_event函数合成鼠标运动和按钮点击。
::GetCursorPos,获取当前鼠标的位置;
::SetCursorPos,移动鼠标到某一位置;
mouse_event,鼠标动作,单击等。
mouse_even只能够发送前台消息,即仅对当前激活的窗体有效。它最好配合SetCursorPos(x,y)函数一起使用。
4.1 函数声明
void mouse_event(
[in] DWORD dwFlags,
[in] DWORD dx,
[in] DWORD dy,
[in] DWORD dwData,
[in] ULONG_PTR dwExtraInfo
);
4.2 代码示例
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);
}
5、keybd_event
合成击键。系统可以使用这样的合成击键来生成WM_KEYUP或WM_KEYDOWN消息。键盘驱动程序的中断处理程序调用keybd_event函数。
注意:此功能已被取代。请改用SendInput。
应用程序可以模拟按下 PRINTSCRN 键以获得屏幕快照并将其保存到剪贴板。为此,调用keybd_event并将 bVk参数设置为VK_SNAPSHOT。
5.1 函数声明
void keybd_event(
[in] BYTE bVk,
[in] BYTE bScan,
[in] DWORD dwFlags,
[in] ULONG_PTR dwExtraInfo
);
5.2 代码示例
以下示例程序通过使用带有VK_NUMLOCK虚拟键的keybd_event来切换 NUM LOCK 灯。它采用一个布尔值,指示灯应该关闭 ( FALSE ) 还是打开 ( TRUE )。相同的技术可用于 CAPS LOCK 键 ( VK_CAPITAL ) 和 SCROLL LOCK 键 ( VK_SCROLL )。
#include <windows.h>
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]);
}
}
6、SendInput
6.1 函数声明
合成击键、鼠标动作和按钮点击。
SendInput函数将 INPUT 结构中的事件串行插入到键盘或鼠标输入流中。这些事件不会与用户(使用键盘或鼠标)或通过调用keybd_event、mouse_event或对SendInput的其他调用插入的其他键盘或鼠标输入事件穿插。
此功能不会重置键盘的当前状态。调用该函数时已按下的任何键都可能会干扰该函数生成的事件。为避免此问题,请使用GetAsyncKeyState函数检查键盘的状态并根据需要进行更正。
UINT SendInput(
[in] UINT cInputs,
[in] LPINPUT pInputs,
[in] int cbSize
);
6.2 代码示例
//**********************************************************************
//
// 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));
}
7、SendMessage/PostMessage
当用户在键盘上键入时,具有键盘焦点的窗口的窗口过程接收击键消息。击键消息是WM_KEYDOWN、WM_KEYUP、WM_SYSKEYDOWN和WM_SYSKEYUP。典型的窗口过程会忽略除WM_KEYDOWN之外的所有击键消息。当用户按下某个键时,系统会发布WM_KEYDOWN消息。
当窗口过程收到WM_KEYDOWN消息时,它应该检查伴随消息的虚拟键代码以确定如何处理击键。虚拟键代码在消息的wParam参数中。通常,应用程序只处理由非字符键生成的击键,包括功能键、光标移动键和特殊用途键,如 INS、DEL、HOME 和 END。
这种方法不需要窗体在前端,甚至最小化也可以使用,但是此方法并不是在所有场合有效,特别是对于不响应鼠标消息的程序更是如此。在这种情况下,可以尝试使用mouse_event函数。
7.1 函数声明
将指定的消息发送到一个或多个窗口。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
);
7.2 代码示例
::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);
}
8、辅助工具
8.1 spy++.exe
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。
8.2 Inspect.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 层次结构中的可访问对象。
8.3 键盘键值列表
/* Virtual Keys, Standard Set*/
VK_LBUTTON 0x01
VK_RBUTTON 0x02
VK_CANCEL 0x03
VK_MBUTTON 0x04
#define VK_LBUTTON 0x01 //鼠标左键
#define VK_RBUTTON 0x02 //鼠标右键
#define VK_CANCEL 0x03 //Ctrl + Break
#define VK_MBUTTON 0x04 //鼠标中键/* NOT contiguous with L & RBUTTON */
#define VK_BACK 0x08 //Backspace 键
#define VK_TAB 0x09 //Tab 键
#define VK_CLEAR 0x0C
#define VK_RETURN 0x0D //回车键
#define VK_SHIFT 0x10
#define VK_CONTROL 0x11
#define VK_MENU 0x12 //Alt 键
#define VK_PAUSE 0x13
#define VK_CAPITAL 0x14 //Caps Lock 键
#define VK_KANA 0x15
#define VK_HANGEUL 0x15 /* old name - should be here for compatibility */
#define VK_HANGUL 0x15
#define VK_JUNJA 0x17
#define VK_FINAL 0x18
#define VK_HANJA 0x19
#define VK_KANJI 0x19
#define VK_ESCAPE 0x1B //Esc 键
#define VK_CONVERT 0x1C
#define VK_NONCONVERT 0x1D
#define VK_ACCEPT 0x1E
#define VK_MODECHANGE 0x1F
#define VK_SPACE 0x20 //空格
#define VK_PRIOR 0x21 //Page Up 键
#define VK_NEXT 0x22 //Page Down 键
#define VK_END 0x23 //End 键
#define VK_HOME 0x24 //Home 键
#define VK_LEFT 0x25 /*方向键*/
#define VK_UP 0x26
#define VK_RIGHT 0x27
#define VK_DOWN 0x28
#define VK_SELECT 0x29
#define VK_PRINT 0x2A
#define VK_EXECUTE 0x2B
#define VK_SNAPSHOT 0x2C //Print Screen 键
#define VK_INSERT 0x2D //Insert键
#define VK_DELETE 0x2E //Delete键
#define VK_HELP 0x2F
VK_0 thru VK_9 are the same as ASCII '0' thru '9' (0x30 - 0x39)
VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' (0x41 - 0x5A)
#define VK_LWIN 0x5B //左WinKey(104键盘才有)
#define VK_RWIN 0x5C //右WinKey(104键盘才有)
#define VK_APPS 0x5D //AppsKey(104键盘才有)
#define VK_NUMPAD0 0x60 //小键盘0-9
#define VK_NUMPAD1 0x61
#define VK_NUMPAD2 0x62
#define VK_NUMPAD3 0x63
#define VK_NUMPAD4 0x64
#define VK_NUMPAD5 0x65
#define VK_NUMPAD6 0x66
#define VK_NUMPAD7 0x67
#define VK_NUMPAD8 0x68
#define VK_NUMPAD9 0x69
#define VK_MULTIPLY 0x6A //乘
#define VK_ADD 0x6B //加
#define VK_SEPARATOR 0x6C //除
#define VK_SUBTRACT 0x6D //减
#define VK_DECIMAL 0x6E //小数点
#define VK_DIVIDE 0x6F
#define VK_F1 0x70 //功能键F1-F24
#define VK_F2 0x71
#define VK_F3 0x72
#define VK_F4 0x73
#define VK_F5 0x74
#define VK_F6 0x75
#define VK_F7 0x76
#define VK_F8 0x77
#define VK_F9 0x78
#define VK_F10 0x79
#define VK_F11 0x7A
#define VK_F12 0x7B
#define VK_F13 0x7C
#define VK_F14 0x7D
#define VK_F15 0x7E
#define VK_F16 0x7F
#define VK_F17 0x80
#define VK_F18 0x81
#define VK_F19 0x82
#define VK_F20 0x83
#define VK_F21 0x84
#define VK_F22 0x85
#define VK_F23 0x86
#define VK_F24 0x87
#define VK_NUMLOCK 0x90 //Num Lock 键
#define VK_SCROLL 0x91 //Scroll Lock 键
VK_L* & VK_R* - left and right Alt, Ctrl and Shift virtual keys.
Used only as parameters to GetAsyncKeyState() and GetKeyState().
No other API or message will distinguish left and right keys in this way.
#define VK_LSHIFT 0xA0
#define VK_RSHIFT 0xA1
#define VK_LCONTROL 0xA2
#define VK_RCONTROL 0xA3
#define VK_LMENU 0xA4
#define VK_RMENU 0xA5
#if(WINVER >= 0x0400)
#define VK_PROCESSKEY 0xE5
#endif /* WINVER >= 0x0400 */
#define VK_ATTN 0xF6
#define VK_CRSEL 0xF7
#define VK_EXSEL 0xF8
#define VK_EREOF 0xF9
#define VK_PLAY 0xFA
#define VK_ZOOM 0xFB
#define VK_NONAME 0xFC
#define VK_PA1 0xFD
#define VK_OEM_CLEAR 0xFE
如果您需要相关功能的代码定制化开发,可以微信或者私信作者;