![4f7036730557b134eb9425ca5bf5b8c0.png](https://i-blog.csdnimg.cn/blog_migrate/5a47bbb511c56ec6ed62cb7c2380acbd.jpeg)
本文是i春秋论坛作家「flag0」表哥原创的一篇技术文章,关于定位MFC中SDK创建窗口API位置的相关内容,感兴趣的小伙伴快来学习吧。
SDK中用于创建窗口的API
在SDK中创建窗口的过程,我们可以将其称为为创建窗口6要素。其分别为:
1、设计窗口类;
2、创建窗口实例;
3、显示窗口;
4、更新窗口;
5、建立消息循环;
6、实现窗口过程函数。
其中对应的API为:
1、RegisterClass;
2、CreateWindow;
3、ShowWindow;
4、UpdateWindow;
5、GetMessage、TranslateMessage、DispatchMessage;
6、WindowProc、DefWindowProc。
在MFC中对其进行了封装,接下来我们通过一系列的操作来定位到MFC中对应的SDK窗口的创建过程。
MFC单文件工程
一、定位消息循环
创建完项目后,配置项目属性。
属性页->配置属性->高级->在静态库中使用MFC
将其默认的在共享 DLL 中使用 MFC改成在静态库中使用MFC,就可以进行MFC框架的调试了。
![3698bc3cfd07b2e2297ff1f1d6fdaaff.png](https://i-blog.csdnimg.cn/blog_migrate/c60a163f234acb78a85ed88ea0784069.jpeg)
消息循环,使用F10无法步过,会陷入到函数里面,所以可以采用一路F10,然后依次跟进循环里面的方法来进行。
按F10运行断到Main函数的位置。
![f11419f6cb645a5984fa22d4220dc331.png](https://i-blog.csdnimg.cn/blog_migrate/4aaadff73869f291283c0fa8f0bceaf5.jpeg)
按F11跟进AfxWinMain函数中(此处如果不进行配置在静态库中使用MFC会进入到别的函数)。
![77cfe019fafbe2bb0cae407ec2eb9004.png](https://i-blog.csdnimg.cn/blog_migrate/68c14d96a84c12118e3c923454089936.jpeg)
接下来一路F10,可以看到程序陷入了函数中。
![2cf305a6ee94b9df11e7658c95a7bc26.png](https://i-blog.csdnimg.cn/blog_migrate/f2111e9eca6826f976142e095ffa0250.jpeg)
下断点,重新运行,F11进入该函数。
![8e50a9ebd83c2e77fff627529c7f520f.png](https://i-blog.csdnimg.cn/blog_migrate/cb753bc71d53ce8496ad8e29d46ee62c.jpeg)
接着F11进入CWinThread::Run( )
![7f909eeb1ad30b4938b22903075a7954.png](https://i-blog.csdnimg.cn/blog_migrate/1acc3c5f812f67d0297b5fe6b9ee007c.jpeg)
可以看到for循环是一个死循环,接着一路F10,可以发现程序在do while语句之间一直循环。
![ac2bff935cd68d7149dcd9bbecb09e97.png](https://i-blog.csdnimg.cn/blog_migrate/6809b73a9685f6ca43466411b2d24b96.jpeg)
do while循环中有三个函数,我们进入PumpMessage( )函数进行查看:
![afb8d663311fe6143b2e984a14dce5ef.png](https://i-blog.csdnimg.cn/blog_migrate/5a88b30bdc5a0b874a72c5789a2a6333.jpeg)
再紧接着进入AfxInternalPumpMessage( )函数中
![eca571921573edbcf1ce342870e9f18d.png](https://i-blog.csdnimg.cn/blog_migrate/2446c80543907ff3bf471a2113ee474e.jpeg)
可以看到该函数中存在处理消息的APIGetMessage、TranslateMessage、DispatchMessage,据此可以判定,其为消息循环部分。
二、定位窗口过程函数
窗口过程函数是用来处理消息的,可以在发送消息处,下断点,通过栈回溯,找到窗口回调函数。
在控件操作处下断点,然后运行,触发该操作。
这里我们选择的是关于窗口的确定按钮控件。
![29ef339ba7ec622d26311f9321cd4026.png](https://i-blog.csdnimg.cn/blog_migrate/5b47ddaebcd5d6dc4a0ec3379463bae1.jpeg)
点击关于窗口的确定按钮
![910222698faa13bdbc0e31deafa14cf3.png](https://i-blog.csdnimg.cn/blog_migrate/cc81682acae8911a0ddd9fe2f8f9390e.jpeg)
可以看到断在了下断点的位置,打开调用堆栈窗口从上往下依次分析。
![c1de32e8ee9107c2a86e49a33ce422af.png](https://i-blog.csdnimg.cn/blog_migrate/d9bcf437d7776eea6936e64f3aa4bb39.jpeg)
窗口过程函数的参数为:
- HWND hWnd
- UINT Msg
- WPARAM wParam
- LPARAM lParam
从上到下依次进行分析,哪个调用函数符合。
最终定位到CALLBACK,其参数列表完全符合,可以确定其就是窗口过程函数。
![c0e6663d7fcc95376f4e11227fba95fd.png](https://i-blog.csdnimg.cn/blog_migrate/33ce48ce5805793f09f451cda6300b90.jpeg)
3、UpdateWindow && ShowWindow
MSDN对该API描述如下:如果窗口的更新区域不为空,则通过向窗口发送WM_PAINT消息来更新指定窗口的客户端区域。该函数直接向指定窗口的窗口过程发送WM_PAINT消息,绕过应用程序队列。如果更新区域为空,则不发送消息。
我们可以判断,其会发送WM_PAINT消息,所以我们寻找MFC中的绘制函数在绘制时会发送WM_PAINT消息,最终确定了OnDraw函数。
![7bb47cd9b4c5e04da03506e9d8822967.png](https://i-blog.csdnimg.cn/blog_migrate/642483e9d6af1753f154a377fd48d496.jpeg)
在OnDraw函数内部下断点
![70a460da2ad866ab4a395f40ba7e895c.png](https://i-blog.csdnimg.cn/blog_migrate/e212ec6bf789a9643de5832d678a30f0.jpeg)
运行后查看调用堆栈
![54f192a831eae94ae1581337f66d0d0e.png](https://i-blog.csdnimg.cn/blog_migrate/f25bb2f325bb4fe8fd20a0a63bf8b066.jpeg)
双击进入该函数即可看到UpdateWindow API
![ac8ede31d9b7c8d5d6aa99ee272a7325.png](https://i-blog.csdnimg.cn/blog_migrate/1e604df3237c9090977e98b23f7edd37.jpeg)
再往上回溯一层
![9c912fff41b3e1850117a295a651df0f.png](https://i-blog.csdnimg.cn/blog_migrate/21d3330253334ddfefb307c9e311dd21.jpeg)
进入InitInstance( )可以看到ShowWindowAPI
![5c1980bb49f7d74213c14d621ec77b85.png](https://i-blog.csdnimg.cn/blog_migrate/f70b24618e703de4bda374a393c4c4e4.jpeg)
UpdateWindow当有效区存在时会调用窗口过程函数,发送WM_PAINT消息。
关系链如下:
UpdateWindow->(发送WM_PAINT消息)->WndProc->(处理WM_PAINT消息)->OnDraw
因此可以通过栈回溯定位到UpdateWindow
4、CreateWindow
在返回之前,Create Window向窗口过程发送WM_CREATE消息。
因此在处理WM_CREATE处下断点,通过栈回溯可以定位到CreateWindow。
在MainFrm.cpp中的OnCreate函数处下断点。
![ae39c9be24aab8aef4d557dc373ac1e5.png](https://i-blog.csdnimg.cn/blog_migrate/35bc72423e79ec4d7b994e6d7c6fe4a1.jpeg)
查看调用堆栈,进行分析。
![c3c79fbb75c25075ae85f6f032c86baf.png](https://i-blog.csdnimg.cn/blog_migrate/261a56604dc26aed9660c54bc4d42d57.jpeg)
可以看到其参数与CreateWindowAPI的参数列表完全吻合
![7f3d4950cd5b2df7cf988770df153fd9.png](https://i-blog.csdnimg.cn/blog_migrate/6f10610929db773f592f8b88753089b1.jpeg)
可以判定其就是CreateWindowAPI
![93898aaa4a7a24e28cb63ae41cd229b0.png](https://i-blog.csdnimg.cn/blog_migrate/da6258e24263e395a3a22a3274a0f969.jpeg)
5、定位RegisterClass
通过CreateWindow的类名进行回溯RegisterClass
![e5c1d70fdee49bbe7ef0f0fc48517fb9.png](https://i-blog.csdnimg.cn/blog_migrate/91b254f6d8c7c01f0bd549cd2565cf64.jpeg)
查看调用堆栈中上一层的函数的调用。
![162c9a5af910c57f8679280551c1db25.png](https://i-blog.csdnimg.cn/blog_migrate/f0795fbc4db779def543eb4582ed9817.jpeg)
可以看到其也来源于上层函数调用,再往上回溯一层。
![621dadc50c473bff268fd254e1684017.png](https://i-blog.csdnimg.cn/blog_migrate/0ea306c79b5dad762f8cf6cd26892e36.jpeg)
此时可以看到类名来源于GetIconWndClass函数,按F9下断点,F5继续运行到当前断点后F11跟进去。
![c12349063929dbc094ac98e54e399a93.png](https://i-blog.csdnimg.cn/blog_migrate/e9a53ef205e2913736103c8deb5940d2.jpeg)
我们关注该函数的返回值,可以看到其有两个返回,排除返回为NULL的那个,在另一个返回处下断点,跟进。
![f49141a1221303e3ce9fb9a985a1dc80.png](https://i-blog.csdnimg.cn/blog_migrate/59cf0a3a3882ef918dc4bbc0aaf15936.jpeg)
我们同样关注该函数的返回,其有两个返回,从上到下依次进行分析,首先在GetClassInfo处下断点跟进。
![3ae702378150150ada82211f7de1149c.png](https://i-blog.csdnimg.cn/blog_migrate/46b1facfae773e0b2338d8be45ba36fb.jpeg)
按F11发现其并没有进入函数内部,继续关注另一个函数AfxRegisterClass,下断点跟进。
![08b74027c2c5b23542e65e4e9bf0114b.png](https://i-blog.csdnimg.cn/blog_migrate/58741637697caaf34369b3eff83fed9c.jpeg)
在该函数内部可以发现RegisterClass的宏定义,跟进后确定其RegisterClassAPI的位置。
![3310b3a9b675ce37cebba581428f6059.png](https://i-blog.csdnimg.cn/blog_migrate/a06eb8898130b8401f6c191030d98d19.jpeg)
以上我们依次找到了创建窗口的六要素的位置。
总结
1、RegisterClass通过CreateWindow回溯ClassName。
2、CreateWindow找到处理WM_CREATE消息处OnCreate,进行栈回溯。
3、UpdateWindow找到处理WM_PAINT消息处OnDraw处下断点进行栈回溯。
4、ShowWindow找到处理WM_PAINT消息处OnDraw处下断点进行栈回溯。
5、消息循环F10,然后依次跟进循环内部进行分析。
6、窗口过程函数在发送消息处,下断点,通过栈回溯,找到窗口回调函数。
以上就完成了MFC单文件工程中,SDK创建窗口API的定位。
以上是今天要分享的内容,大家看懂了吗?