SoUI界面库的介绍和使用方法可以查看启程软件的博客园:https://www.cnblogs.com/setoutsoft/。这里默认大家都对SoUI有一定的了解了。
现在SoUI界面库已经发展到SoUI3版本了,但是由于本人一直在使用的是SoUI2版本,所以今天分析的也是SoUI2版本的代码。
分析流程
首先我们创建一个SoUI的测试项目,在SoUI项目的创建向导中全部选择默认即可,然后在CMainDlg::OnClose()
中打上断点,如下所示
然后运行程序,点击右上角的关闭按钮,查看VS中的调用堆栈,如下所示
从调用堆栈图我们可以大致看到函数的执行顺序,于是我们一个个地进行分析,首先从调用堆栈中的> souid.dll!SOUI::CSimpleWnd::WindowProc(HWND__ * hWnd, unsigned int uMsg, unsigned int wParam, long lParam) 行 110 C++
这个函数开始吧,这里是直接跳转到CMainDlg
的消息处理函数ProcessWindowMessage
中,如下所示
然后我们查看调用堆栈中的> SouiWizard1.exe!CMainDlg::ProcessWindowMessage(HWND__ * hWnd, unsigned int uMsg, unsigned int wParam, long lParam, long & lResult, unsigned long dwMsgMapID) 行 30 C++
和> SouiWizard1.exe!CMainDlg::_ProcessWindowMessage(HWND__ * hWnd, unsigned int uMsg, unsigned int wParam, long lParam, long & lResult, unsigned long dwMsgMapID) 行 35 C++
,发现这里只是在CMainDlg
的消息映射表中查看是否需要处理当前的点击消息,即WM_LBUTTONUP
,默认是没有处理的,所以可以去查看下一项。
接着是> souid.dll!SOUI::SHostWnd::ProcessWindowMessage(HWND__ * hWnd, unsigned int uMsg, unsigned int wParam, long lParam, long & lResult, unsigned long dwMsgMapID) 行 338 C++
和> souid.dll!SOUI::SHostWnd::_ProcessWindowMessage(HWND__ * hWnd, unsigned int uMsg, unsigned int wParam, long lParam, long & lResult, unsigned long dwMsgMapID) 行 351 C++
,在这里呢我们可以发现,代码从CMainDlg
的消息映表跳转到了SHostWnd
的消息映射表中,由于呢MESSAGE_RANGE_HANDLER_EX(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseEvent)
中处理了WM_LBUTTONUP
的消息,所以流程进入了OnMouseEvent
函数中。
> souid.dll!SOUI::SHostWnd::OnMouseEvent(unsigned int uMsg, unsigned int wParam, long lParam) 行 662 C++
中可以看到流程进入了DoFrameEvent
函数。
> souid.dll!SOUI::SwndContainerImpl::DoFrameEvent(unsigned int uMsg, unsigned int wParam, long lParam) 行 81 C++
中可以看到流程进入了OnFrameMouseEvent
函数中。
在> souid.dll!SOUI::SwndContainerImpl::OnFrameMouseEvent(unsigned int uMsg, unsigned int wParam, long lParam) 行 286 C++
中呢,我们可以看到,pCapture->SSendMessage(uMsg,wParam,lParam,&bMsgHandled);
这句代表就代表了消息交由了被点击控件处理。
于是可以继续查看下去> souid.dll!SOUI::SWindow::SSendMessage(unsigned int Msg, unsigned int wParam, long lParam, int * pbMsgHandled) 行 222 C++
、> souid.dll!SOUI::SButton::ProcessSwndMessage(unsigned int uMsg, unsigned int wParam, long lParam, long & lResult) 行 264 C++
、> souid.dll!SOUI::SButton::ProcessSwndMessage(unsigned int uMsg, unsigned int wParam, long lParam, long & lResult) 行 264 C++
中和前面的一样,先是ProcessSwndMessage(Msg, wParam, lParam, lResult);
进入消息映射表,然后现在SButton
和SWindow
的消息映射表中寻找处理WM_LBUTTONUP
的消息的宏。
然后就到了> souid.dll!SOUI::SWindow::OnLButtonUp(unsigned int nFlags, SOUI::CPoint pt) 行 1547 C++
和souid.dll!SOUI::SWindow::FireEvent(SOUI::EventArgs & evt) 行 1127 C++
,在这里我们可以看到调用了FireEvent(evtLButtonUp);
函数和FireEvent(evt)
函数。
最后呢> souid.dll!SOUI::SWindow::FireEvent(SOUI::EventArgs & evt) 行 1127 C++
中调用了GetContainer()->OnFireEvent(evt)
,> souid.dll!SOUI::SHostWnd::OnFireEvent(SOUI::EventArgs & evt) 行 709 C++
中调用了_HandleEvent(&evt);
,> SouiWizard1.exe!CMainDlg::_HandleEvent(SOUI::EventArgs * pEvt) 行 23 C++
中就到了CMainDlg
的SoUI消息宏里面去调用了OnClose
函数。
上述就是整个调用流程。
但是,在 > souid.dll!SOUI::SwndContainerImpl::OnFrameMouseEvent(unsigned int uMsg, unsigned int wParam, long lParam) 行 286 C++
中,我们要注意一句代码SWindow *pCapture=SWindowMgr::GetWindow(m_hCapture)
,这句代码是获取当前鼠标点击的控件的指针,在这里没有看到如何通过坐标之类计算出来,而是直接通过m_hCapture
获取的,这样就不合理了。
于是我们可以在void SwndContainerImpl::OnFrameMouseEvent(UINT uMsg,WPARAM wParam,LPARAM lParam)
的SWindow *pCapture=SWindowMgr::GetWindow(m_hCapture)
那里打一个断点,然后重新运行程序查看调用流程。
首先呢,该断点触发了,此时应该是WM_LBUTTONDOWN
消息(鼠标左击一次会产生两个消息,先是WM_LBUTTONDOWN
,再是WM_LBUTTONUP
),然后流程走到了m_hHover=SwndFromPoint(CPoint(GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam)),FALSE);
中,这句代码的作用是根据坐标计算出坐标位置最上面的控件,然后跟前面一样,使用该控件的SSendMessage
函数处理WM_LBUTTONDOWN
消息。
于是我们在上述的SWindow
的消息映射表中找到OnLButtonDown
响应函数,代码如下
在上述函数中打一个断点,继续执行程序。
我们让程序停留在上面图片中的SetCapture();
位置,这里是重点,进入该函数发现,这里呢调用了GetContainer()->OnSetSwndCapture(m_swnd);
、CSimpleWnd::SetCapture();
、
SwndContainerImpl::OnSetSwndCapture(swnd);
,在SwndContainerImpl::OnSetSwndCapture(swnd);
中我们可以看到程序保存了处理WM_LBUTTONDOWN
的控件的句柄,如下所