前言
折腾这个东西就是要实现一个很简单的功能:在我的应用中控制处于后台的PPT进行翻页。
结论:无法实现。
在我测试的过程中无论是wps还是office都无法在后台响应→
的事件 用Spy++查看无论是窗口句柄还是发送的消息都是完全正确的,都无法响应。处于前台时也依旧没法用PostMessageA
或是SendMessageA
发送消息,但使用keybd_event
是可以的。
也就是说:
keybd_event
只能在应用在前台时才有效,因为他发送的是全局事件。PostMessageA
和SendMessageA
发送的是应用的事件,但是也可能无论应用在前台或是后台都无效。
工具
Viewdll
查看DLL中的函数 支持WIN10
链接:https://pan.baidu.com/s/19emkiUdbCdaPRKrY9ahyWw
提取码:i32n
Microsoft Spy++
查看应用的按键消息等
链接:https://pan.baidu.com/s/1TV5c4cTOiG7E-OdMtHA_mA
提取码:scew
模拟单个按键
模拟单个按键,如按下键A
在一般情况下可以,即使目标程序在后台运行也可以。
但正如你等下在下面看到的文章所说,在某些程序里第四个参数需要特别注意,否则发送按键将无效。
1 | PostMessageA(hWnd,WM_KEYDOWN,'A',0); |
模拟ALT+A
向后台程序发送组合键ALT+按键 是可行的。记住,只可以是ALT,不能是Ctrl或Shift
操作如下:发送ALT+A
1 | PostMessageA(hWnd,WM_SYSKEYDOWN,'A',1<<29); |
模拟其他组合按键
我现在的做法只能是激活目标窗口使其成为前台窗口后再模拟发送组合按键,如下:
1 | SetForegroundWindow(g_OperaWnd); |
模拟鼠标的行为
模拟鼠标的行为最好用SendMessageA(不要用PostMessageA),这样可以把消息直接发送到目的窗口的窗口处理过程,成功率会高很多。
1 | SendMessageA(GetWnd(),WM_LBUTTONDOWN,NULL,MAKELPARAM(47,11)); |
方法介绍
PostMessage
https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-postmessagea
声明PostMessage函数的时候(其实很多很多API函数都是这样),有两种版本:A结尾的是ANSI版本,W结尾的是Unicode版本。一般用A结尾的。
可见PostMessage是我们为了方便起的名字,PostMessageA才是人家的原名。
1 | static extern bool PostMessageW(int hwnd, int msg, uint wParam, uint lParam); |
第一参数是窗口句柄
第二个参数是消息windows消息,在C#中需要定义WM_CHAR或者直接填WM_CHAR的值0x0102
第三个参数填键码
第四个参数
- 0-15位:指定当前消息的重复次数。其值就是用户按下该键后自动重复的次数,但是重复次数不累积
- 16-23位:指定其扫描码,其值依赖于OEM厂商
- 24位:指定该按键是否为扩展按键,所谓扩展按键就是Ctrl,Alt之类的,如果是扩展按键,其值为1,否则为0
- 25-28位:保留字段,暂时不可用
- 29位:指定按键时的上下文,其值为1时表示在按键时Alt键被按下,其值为0表示WM_SYSKEYDOWN消息因没有任何窗口有键盘焦点而被发送到当前活动窗口。
- 30位:指定该按键之前的状态,其值为1时表示该消息发送前,该按键是被按下的,其值为0表示该消息发送前该按键是抬起的。
- 31位:指定其转换状态,对WM_SYSKEYDOWN消息而言,其值总为0。
请看位29的说明!!
当值为1时表示ALT键被按下!这不正是我需要的吗?于是把29位设置为1,函数调用变成
1
PostMessage(hWnd,WM_SYSKEYDOWN,0x41,1<<29);
经过测试,发现这个就是Alt+A的效果!!
那么再来看看如何确定键盘消息中的wParam 和lParam 这两个参数。
wParam 参数的含义较简单,它表示你要发送的键盘事件的按键虚拟码,比如你要对目标程序模拟按下A键,那么wParam 参数的值就设为VK_A 。
lParam 这个参数就比较复杂了,因为它包含了多个信息,一般可以把它设为0,但是如果你想要你的模拟更真实一些,那么建议你还是设置一下这个参数。那么我们就详细了解一下lParam 吧。
lParam 是一个long类型的参数,它在内存中占4个字节,写成二进制就是00000000 00000000 00000000 00000000
一共是32位,我们从右向左数,假设最右边那位为第0位(注意是从0而不是从1开始计数),最左边的就是第31位,
那么该参数的的
0-15位表示键的发送次数等扩展信息,
16-23位为按键的扫描码,
24-31位表示是按下键还是释放键。
大家一般习惯写成16进制的,那么就应该是00 00 00 00
,
- 0-15位一般为
0001
- 16-23位的扫描码
- 24-31位如果是按下键为
00
,释放键则为C0
,
那么16-23位的扫描码怎么得呢
1 | MapVirtualKeyA(0x41,0) |
我们先看看VB怎样写
1 | Function MakeKeyLparam(ByVal VirtualKey As Long, ByVal flag As Long) As Long |
JS写法
16进制转换
1 | //10进制字符串 |
上面的方法JS的写法
1 | // flag 0按下 1释放 |
PPT放映
1 | const ffi = require('ffi'); |
下面的代码并不起作用 但点击F5的消息确实是发送了
1 | let lp1 = makeKeyLparam(0x74, 0); |
Shift+F5
1 | user32.keybd_event(0x10,0,0,0); |
按键表
https://docs.microsoft.com/zh-cn/windows/win32/inputdev/virtual-key-codes
https://docs.microsoft.com/zh-cn/windows/win32/inputdev/wm-syskeydown
按键方式码
常用名称 | 十六进制值 | 十进制值 | 作用 |
---|---|---|---|
WM_KEYDOWN | 0x0100 | 256 | 表示一个普通键被按下 |
WM_KEYUP | 0x0101 | 257 | 表示一个普通键被释放 |
WM_SYSKEYDOWN | 0x0104 | 260 | 表示一个系统键被按下,比如Alt键 |
WM_SYSKEYUP | 0x0105 | 261 | 表示一个系统键被释放,比如Alt键 |
WM_KEYDOWN
和WM_KEYUP
之间的区别就很容易区别了,一个是键的按下,一个是键的释放。
而WM_SYSKEYDOWN
与WM_KEYDOWN
的区别在于WM_SYSKEYDOWN
和WM_SYSKEYUP
消息经常由与Alt相组合的按键产生,这些按键启动程序菜单或者系统菜单上的选项,或者用于切换活动窗口等系统功能(Alt-Tab或者Alt-Esc),也可以用作系统菜单加速键(Alt键与一个功能键相结合,例如Alt-F4用于关闭应用程序)。程序通常忽略WM_SYSKEYUP
和WM_SYSKEYDOWN
消息,并将它们传送到DefWindowProc。由于Windows要处理所有Alt键的功能,所以您无需拦截这些消息。您的窗口消息处理程序将最后收到关于这些按键结果(如菜单选择)的其它消息。如果您想在自己的窗口消息处理程序中加上拦截系统按键的程序码,那么在处理这些消息之后再传送到DefWindowProc,Windows就仍然可以将它们用于通常的目的。当然我们完全可以在响应WM_KEYDOWN
和WM_KEYUP
消息的lParam参数时,判断第29位来判断Alt键是否按下,如果在按键的时候同时按下ALT键,那么该位为1, 否则为0;或者通过GetKeyState(VK_MENU)来判断ALT也是可以的。
我们开发时主要用
WM_KEYDOWN
和WM_KEYUP
按键码
常用名称 | 十六进制值 | 十进制值 | 对应按键 |
---|---|---|---|
VK_LBUTTON | 0x01 | 1 | 鼠标的左键 |
VK_RBUTTON | 0x02 | 2 | 鼠标的右键 |
VK-CANCEL | 0x03 | 3 | Contol-break执行 |
VK_MBUTTON | 0x04 | 4 | 鼠标的中键(三按键鼠标) |
VK_BACK | 0x08 | 8 | Backspace键 |
VK_TAB | 0x09 | 9 | Tab键 |
VK_CLEAR | 0x0C | 12 | Clear键 |
VK_RETURN | 0x0D | 13 | Enter键 |
VK_SHIFT | 0x10 | 16 | Shift键 |
VK_CONTROL | 0x11 | 17 | Ctrl键 |
VK_MENU | 0x12 | 18 | Alt键 |
VK_PAUSE | 0x13 | 19 | Pause键 |
VK_CAPITAL | 0x14 | 20 | Caps Lock键 |
VK_ESCAPE | 0x1B | 27 | Ese键 |
VK_SPACE | 0x20 | 32 | Spacebar键 |
VK_PRIOR | 0x21 | 33 | Page Up键 |
VK_NEXT | 0x22 | 34 | Page Domw键 |
VK_END | 0x23 | 35 | End键 |
VK_HOME | 0x24 | 36 | Home键 |
VK_LEFT | 0x25 | 37 | LEFT ARROW键(←) |
VK_UP | 0x26 | 38 | UP ARROW键(↑) |
VK_RIGHT | 0x27 | 39 | RIGHT ARROW键(→) |
VK_DOWN | 0x28 | 40 | DOWN ARROW键(↓) |
VK_SELECT | 0x29 | 41 | SELECT键 |
VK_EXECUTE | 0x2B | 43 | EXECUTE键 |
VK_SNAPSHOT | 0x2C | 44 | Print Screen键 |
VK_INSERT | 0x2D | 45 | Ins键 |
VK_DELETE | 0x2E | 46 | Del键 |
VK_HELP | 0x2F | 47 | Help键 |
VK_0 | 0x30 | 48 | 0键 |
VK_1 | 0x31 | 49 | 1键 |
VK_2 | 0x32 | 50 | 2键 |
VK_3 | 0x33 | 51 | 3键 |
VK_4 | 0x34 | 52 | 4键 |
VK_5 | 0x35 | 53 | 5键 |
VK_6 | 0x36 | 54 | 6键 |
VK_7 | 0x37 | 55 | 7键 |
VK_8 | 0x38 | 56 | 8键 |
VK_9 | 0x39 | 57 | 9键 |
VK_A | 0x41 | 65 | A键 |
VK_B | 0x42 | 66 | B键 |
VK_C | 0x43 | 67 | C键 |
VK_D | 0x44 | 68 | D键 |
VK_E | 0x45 | 69 | E键 |
VK_F | 0x46 | 70 | F键 |
VK_G | 0x47 | 71 | G键 |
VK_H | 0x48 | 72 | H键 |
VK_I | 0x49 | 73 | I键 |
VK_J | 0x4A | 74 | J键 |
VK_K | 0x4B | 75 | K键 |
VK_L | 0x4C | 76 | L键 |
VK_M | 0x4D | 77 | M键 |
VK_N | 0x4E | 78 | N键 |
VK_O | 0x4F | 79 | O键 |
VK_P | 0x50 | 80 | P键 |
VK_Q | 0x51 | 81 | Q键 |
VK_R | 0x52 | 82 | R键 |
VK_S | 0x53 | 83 | S键 |
VK_T | 0x54 | 84 | T键 |
VK_U | 0x55 | 85 | U键 |
VK_V | 0x56 | 86 | V键 |
VK_W | 0x57 | 87 | W键 |
VK_X | 0x58 | 88 | X键 |
VK_Y | 0x59 | 89 | Y键 |
VK_Z | 0x5A | 90 | Z键 |
VK_NUMPAD0 | 0x60 | 96 | 数字键0键 |
VK_NUMPAD1 | 0x61 | 97 | 数字键1键 |
VK_NUMPAD2 | 0x62 | 98 | 数字键2键 |
VK_NUMPAD3 | 0x63 | 99 | 数字键3键 |
VK_NUMPAD4 | 0x64 | 100 | 数字键4键 |
VK_NUMPAD5 | 0x65 | 101 | 数字键5键 |
VK_NUMPAD6 | 0x66 | 102 | 数字键6键 |
VK_NUMPAD7 | 0x67 | 103 | 数字键7键 |
VK_NUMPAD8 | 0x68 | 104 | 数字键8键 |
VK_NUMPAD9 | 0x69 | 105 | 数字键9键 |
VK_MULTIPLY | 0x6A | 106 | *键 |
VK_ADD | 0x6B | 107 | =+键 |
VK_SEPARATOR | 0x6C | 108 | Separator键 |
VK_SUBTRACT | 0x6D | 109 | =-键 |
VK_DECIMAL | 0x6E | 110 | .键 |
VK_DIVIDE | 0x6F | 111 | 键 |
VK_F1 | 0x70 | 112 | F1键 |
VK_F2 | 0x71 | 113 | F2键 |
VK_F3 | 0x72 | 114 | F3键 |
VK_F4 | 0x73 | 115 | F4键 |
VK_F5 | 0x74 | 116 | F5键 |
VK_F6 | 0x75 | 117 | F6键 |
VK_F7 | 0x76 | 118 | F7键 |
VK_F8 | 0x77 | 119 | F8键 |
VK_F9 | 0x78 | 120 | F9键 |
VK_F10 | 0x79 | 121 | F10键 |
VK_F11 | 0x7A | 122 | F11键 |
VK_F12 | 0x7B | 123 | F12键 |
VK_NUMLOCK | 0x90 | 144 | Num Lock键 |
VK_SCROLL | 0x91 | 145 | Scroll Lock键 |