折腾这个东西就是要实现一个很简单的功能:在我的应用中控制处于后台的PPT进行翻页。
结论:无法实现。
在我测试的过程中无论是wps还是office都无法在后台响应→
的事件 用Spy++查看无论是窗口句柄还是发送的消息都是完全正确的,都无法响应。处于前台时也依旧没法用PostMessageA
或是SendMessageA
发送消息,但使用keybd_event
是可以的。
也就是说:
keybd_event
只能在应用在前台时才有效,因为他发送的是全局事件。PostMessageA
和SendMessageA
发送的是应用的事件,但是也可能无论应用在前台或是后台都无效。查看DLL中的函数 支持WIN10
链接:https://pan.baidu.com/s/19emkiUdbCdaPRKrY9ahyWw 提取码:i32n
查看应用的按键消息等
链接:https://pan.baidu.com/s/1TV5c4cTOiG7E-OdMtHA_mA 提取码:scew
模拟单个按键,如按下键A
在一般情况下可以,即使目标程序在后台运行也可以。 但正如你等下在下面看到的文章所说,在某些程序里第四个参数需要特别注意,否则发送按键将无效。
PostMessageA(hWnd,WM_KEYDOWN,'A',0);
向后台程序发送组合键ALT+按键 是可行的。记住,只可以是ALT,不能是Ctrl或Shift 操作如下:发送ALT+A
PostMessageA(hWnd,WM_SYSKEYDOWN,'A',1<<29);
我现在的做法只能是激活目标窗口使其成为前台窗口后再模拟发送组合按键,如下:
SetForegroundWindow(g_OperaWnd);
keybd_event(VK_CONTROL,0,0,0);
keybd_event(65,0,0,0);
keybd_event(65,0,KEYEVENTF_KEYUP,0);
keybd_event(VK_CONTROL,0,KEYEVENTF_KEYUP,0);
模拟鼠标的行为最好用SendMessageA(不要用PostMessageA),这样可以把消息直接发送到目的窗口的窗口处理过程,成功率会高很多。
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才是人家的原名。
static extern bool PostMessageW(int hwnd, int msg, uint wParam, uint lParam);
请看位29的说明!! 当值为1时表示ALT键被按下!这不正是我需要的吗?于是把29位设置为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
,
0001
00
,释放键则为C0
,那么16-23位的扫描码怎么得呢
MapVirtualKeyA(0x41,0)
我们先看看VB怎样写
Function MakeKeyLparam(ByVal VirtualKey As Long, ByVal flag As Long) As Long
'参数VirtualKey表示按键虚拟码,flag表示是按下键还是释放键,用WM_KEYDOWN和WM_KEYUP这两个常数表示
Dim s As String
Dim Firstbyte As String 'lparam参数的24-31位
If flag = WM_KEYDOWN Then '如果是按下键
Firstbyte = "00"
Else
Firstbyte = "C0" '如果是释放键
End If
Dim Scancode As Long
'获得键的扫描码
Scancode = MapVirtualKey(VirtualKey, 0)
Dim Secondbyte As String 'lparam参数的16-23位,即虚拟键扫描码
Secondbyte = Right("00" & Hex(Scancode), 2)
s = Firstbyte & Secondbyte & "0001" '0001为lparam参数的0-15位,即发送次数和其它扩展信息
MakeKeyLparam = Val("&H" & s)
End Function
JS写法
16进制转换
//10进制字符串
console.info(0x41.toString());
//16进制字符串
console.info(0x41.toString(16));
//16进制字符串转数字
console.info(parseInt("0x41"));
上面的方法JS的写法
// flag 0按下 1释放
function makeKeyLparam(virtualKey, flag) {
let firstbyte = "";
let scancode = user32.MapVirtualKeyA(virtualKey, 0).toString(16);
if (flag === 0) {
firstbyte = "00"
} else {
firstbyte = "C0"
}
let keyLparamStr = "0x" + firstbyte + scancode + "0001";
console.info("keyLparamStr", keyLparamStr);
let keyLparam = parseInt(keyLparamStr);
console.info("keyLparam", keyLparam);
return keyLparam
}
const ffi = require('ffi');
const ref = require('ref');
const iconv = require('iconv-lite');
const voidPtr = ref.refType(ref.types.void);
const stringPtr = ref.refType(ref.types.CString);
const user32 = ffi.Library('user32.dll', {
GetSystemMenu: ['int', ['int', 'bool']],
EnumWindows: ['bool', [voidPtr, 'int32']],
GetWindowTextA: ['long', ['long', stringPtr, 'long']],
SetForegroundWindow: ['bool', ['long']],
MapVirtualKeyA: ['long', ['long', 'long']],
PostMessageW: ['bool', ['long', 'long', 'long', ref.types.ulong]],
SendMessageA: ['bool', ['long', 'long', 'long', ref.types.ulong]],
keybd_event:['bool', ['int', 'int', 'int', 'int']]
});
// flag 0按下 1释放
function makeKeyLparam(virtualKey, flag) {
let firstbyte = "";
let scancode = user32.MapVirtualKeyA(virtualKey, 0).toString(16);
if (flag === 0) {
firstbyte = "00"
} else {
firstbyte = "C0"
}
let keyLparamStr = "0x" + firstbyte + scancode + "0001";
console.info("keyLparamStr:", keyLparamStr);
let keyLparam = parseInt(keyLparamStr);
return keyLparam
}
windowProc = ffi.Callback('bool', ['long', 'int32'], function (hwnd, lParam) {
let buf, name, ret;
buf = new Buffer(255);
ret = user32.GetWindowTextA(hwnd, buf, 255);
name = ref.readCString(buf, 0);
let text = iconv.decode(buf, 'gbk');
if (text.indexOf("ppt") !== -1) {
console.log(text);
console.info(hwnd.toString(16));
console.log("");
user32.SetForegroundWindow(hwnd);
setTimeout(function () {
user32.keybd_event(0x74,0,0,0);
}, 500);
}
return false;
});
下面的代码并不起作用 但点击F5的消息确实是发送了
let lp1 = makeKeyLparam(0x74, 0);
let lp2 = makeKeyLparam(0x74, 1);
user32.PostMessageW(hwnd, 0x0100, 0x74, lp1);
setTimeout(function () {
user32.PostMessageW(hwnd, 0x0101, 0x74, lp2);
}, 500);
Shift+F5
user32.keybd_event(0x10,0,0,0);
user32.keybd_event(0x74,0,0,0);
user32.keybd_event(0x74,0,2,0);
user32.keybd_event(0x10,0,2,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键 |