您的位置:首页 > 其它

一次windows程序消息分析的曲折经历

2018-02-28 22:25 330 查看
这是在尝试实现安卓模拟器模拟点击过程中,我使用spy++分析消息的经历。

对窗口模拟点击的方法不难,就是使用SendMessage()函数发送模拟消息。难点在于找到窗口句柄并分析出模拟点击对应的消息。而我之前对于windows消息机制了解甚少,为此还专门学习了一阵子spy++的使用方法,方法总结详见另一篇文章——《》。
下载了一个模拟器。通过spy++可以看出,这个模拟器有3个窗口。不过最下面的子窗口没有消息,有消息的是父窗口和子窗口。



为了方便起见,又安装了一个画图app。

主要测试方法是在模拟器中不停画点,如果模拟成功,应当会在画图app的画布中画出一个点。考虑到模拟器大小是720×1280,所以点击位置我折中取了(500,500).

请记住这个位置,我后来可是被这个给坑惨了

……消息列表一出来,刷新速度丧心病狂让我一脸懵逼:



这让我这个小萌新无从下手啊!折腾了好半天,最终发现:

标记“R”的是SendMessage()获得的返回值,没有必要去分析
子窗口会定时收到WM_TIMER消息,这个对分析没用,可以直接在spy++中禁用。

开启“原始消息参数”和“消息原始时间”有助于更好地进行分析。

设置如下



事后,在编写完成之后总结时,我认为消息分析应当分为以下部分:
1.    激活窗口(点开最小化的窗口或使用ALT+TAB切换到窗口),此时会发出一系列消息,不妨将其称为“消息头”。
2.    沉默窗口(最小化窗口或使用ALT+TAB切出),此时发出的一系列消息,不妨称之为“消息体”
3.    对窗口进行一种单一操作,并分析去除了“消息头”和“消息尾”的消息,即“消息体”
说起来,以上的分析都是我完成了程序之后写blog时才分析出的,一开始分析的时候根本没想到能够通过这些分析,基本上凭直觉,也难怪花了两天时间才分析出来。现在回看起来,其实由于模拟器内部自成一体,其消息基本上就是由窗口重绘或切换进来的消息+鼠标操作+最小化或切换出去时消息组成,并不难分析。但是因为缺乏经验,再加上消息日志刷新得非常快而且庞杂,走了非常多的弯路。
基本上,在选中模拟器窗口——点击——离开模拟器窗口时,主要发生了这些消息序列:
在将最小化的窗口点开或使用alt+tab切换进来之后,首先会进行擦除、获得焦点、获得对象等操作(包括一个作用不明的自定义消息ImmersiveFocusNotification),最后使用WM_PAINT进行重绘。



最小化时,有:



离开窗口同样有一系列消息,不妨称为“消息尾”
如果开始不是最小化不需要重绘(使用ALT+TAB切换出来),那么只会有:



基本上只是有一些获得焦点和对象的消息。而失去焦点时(ALT+TAB),则有:



WM_KILLFOCUS:失去焦点
WM_IME_SETCONTEXT:输入焦点转移
WM_IME_NOTIFY:
而后,将鼠标在窗体内划过(不点击),消息日志如:



可以看出鼠标移动有两个消息——WM_NCHITTEST,WM_MOUSEMOVE——组成,且WM_NCHITTEST在前(当时还因为不知道先后顺序折腾了很久)。
分析鼠标点击时,则应先用鼠标随意划过从而产生很多的WM_NCHITTEST, WM_MOUSEMOVE消息,这样方便找到对应的点击消息。消息如下:



可以看出,每点击一次,会出现如下的消息

<000050> 00771004 S WM_NCHITTEST xPos:2064 yPos:1121 [wParam:00000000 lParam:04610810]
<000052> 00771004 S WM_MOUSEACTIVATE hwndTopLevel:006F12AE nHittest:HTCLIENT uMsg:WM_LBUTTONDOWN [wParam:006F12AE lParam:02010001]
<000054> 00771004 S WM_SETCURSOR hwnd:00771004 nHittest:HTCLIENT wMouseMsg:WM_LBUTTONDOWN [wParam:00771004 lParam:02010001]
<000056> 00771004 P WM_LBUTTONDOWN fwKeys:MK_LBUTTON xPos:117 yPos:932 [wParam:00000001 lParam:03A40075 time:88:48:05.908]
<000057> 00771004 P WM_LBUTTONUP fwKeys:0000 xPos:117 yPos:932 [wParam:00000000 lParam:03A40075 time:88:48:05.941]
<000058> 00771004 S WM_CAPTURECHANGED hwndNewCapture:00000000 [wParam:00000000 lParam:00000000]
<000060> 00771004 S WM_NCHITTEST xPos:2064 yPos:1121 [wParam:00000000 lParam:04610810]
<000062> 00771004 S WM_SETCURSOR hwnd:00771004 nHittest:HTCLIENT wMouseMsg:WM_MOUSEMOVE [wParam:00771004 lParam:02000001]
<000064> 00771004 P WM_MOUSEMOVE fwKeys:0000 xPos:117 yPos:932 [wParam:00000000 lParam:03A40075 time:88:48:06.171]
无法分出哪一个是真正有效的(虽然理论上应当是WM_LBUTTONDOWN和WM_LBUTTONUP,但是只有这两个完全无效)。所以将它们全部给写进了程序。
结果很奇怪。程序运行后无法画出点来。而如果将鼠标放入其中,当鼠标运动起来后,会每隔一段时间在鼠标位置画一个点。



我很难想出这个问题的原因,只能不断检查输入的参数,并扩大消息的范围。调了大半天还是无果。
这个时候,无意中打开了父窗口的消息监听窗口,发现父窗口中也出现了大量消息,其中还有向子窗口发送的消息。
那么,是不是只需要对父窗口发送消息,随后父窗口就会使用消息链的方式创建消息传递给子窗口呢?用类似的方法,可以看出对于父窗口来说,主要起作用的是{1,WM_SETCURSOR,(int)_hWnd,(WM_MOUSEMOVE<<16)|HTCLIENT},
{ 1, WM_PARENTNOTIFY, WM_LBUTTONDOWN, ( 374 << 16 ) | 190 },
{ 1, WM_MOUSEACTIVATE, (int)_hParentWnd, ( WM_LBUTTONDOWN << 16 ) | HTCLIENT },
同样输入进去,但仍然毫无反应。
那么,是不是需要同时对二者进行消息发送呢?同步调试,这个难度有点大啊。我只好又照着消息日志敲命令,而且一个参数都没改。cout << SendMessage( _hParentWnd, WM_SETCURSOR, (int)_hWnd, ( WM_MOUSEMOVE << 16 ) | HTCLIENT );
cout << SendMessage( _hWnd, WM_NCHITTEST, 0, 0/*( 527 << 16) | 1888 */);
cout << SendMessage( _hWnd, WM_MOUSEMOVE, 0, ( 338 << 16 ) | 189 );
cout << SendMessage( _hParentWnd, WM_PARENTNOTIFY, WM_LBUTTONDOWN, 0/*( 374 << 16 ) | 190*/ );
cout << SendMessage( _hParentWnd, WM_MOUSEACTIVATE, (int)_hParentWnd, ( WM_LBUTTONDOWN << 16 ) | HTCLIENT );
cout << SendMessage( _hWnd, WM_MOUSEACTIVATE, (int)_hParentWnd, ( WM_LBUTTONDOWN << 16 ) | HTCLIENT );
cout << SendMessage( _hWnd, WM_LBUTTONDOWN, MK_LBUTTON, ( 374<< 16 ) | 198);
cout << SendMessage( _hWnd, WM_LBUTTONUP, 0, ( 374<< 16 ) | 198);
喜极而泣的结果出来了,终于能够显示了!
但是这样的结果实在太复杂,在熊熊燃烧的优化之魂的加持下,我一个个地注释掉了其中的语句。
一开始都没问题,我相当有成就感。但是越注释越感觉不对劲,最后发现消息变成了这样:

uint32_t loc = ( p.y << 16 ) | p.x;
cout << SendMessage( _hWnd, WM_LBUTTONDOWN, MK_LBUTTON, loc );
cout << SendMessage( _hWnd, WM_LBUTTONUP, 0, loc );

卧槽!这不是就只有WM_LBUTTONDOWN和WM_LBUTTONUP吗?可是为什么之前就不行呢?我用截图工具查看了窗口大小为720×1280,再用程序从上到下画了一条线,发现当程序中的y坐标到了大约500的时候就到了窗口外了。
              1280/500=2.56
等等,难道……
……
因为windows的DPI缩放(我的是225%)吗?
 
What the fuck!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: