在看DirectUI的代码,尝试着写一些理解,可能有误,同时也不知道能写多少。写到哪算哪吧。
先谈谈窗口的子类化。
什么是DirectUI呢?
DirectUI界面库取名自微软的一个窗口类名“DirectUIHWND”,意为Paint on parent dc directly。即子窗口不以窗口句柄的形式创建,只是逻辑上的窗口,绘制在父窗口之上。这个父窗口就是使用windows sdk API CreateWindowEx 创建出来的窗口。这个窗口可以说是最原始的窗口了,当然,它拥有hWnd。我们之后的所有渲染工作(加载图片,营造各种绚丽效果)都是基于这个进行的。
我们还知道,Windows软件一个典型的特点是基于消息机制的。如果我们什么也不做,那么,当我们在界面上进行点击按钮,最大最小化窗口等操作的时候,它依然会“我行我素”。于是,为了使得软件按照我们设想的那样来处理消息,我们必须指定窗口过程,这个就是子类化的思想。窗口子类化技术最大的特点就是能够截取 Windows 的消息。一旦我们定义了窗口函数截取传向原窗口函数的消息,就可以对被截取的消息进行如下处理了。
怎么子类化呢?答案就是使用SubclassWindow和SetWindowLongPtr(可以用于64位系统)。SubclassWindow简单来说就是“动态地继承”窗口,一旦调用,就可以在原有基础上扩展自定义功能。(MFC的空间集成就是基于这个思想,只不过一切都预先封装好了)。比如:
CListCtrl m_List = GetListCtrl();
CMyListCtrl m_myList;
m_myList.SubclassWindow(m_List);
m_myList就具备了CListCtrl的所有特性了。
CMyListCtrl m_myList;
m_myList.SubclassWindow(m_List);
m_myList就具备了CListCtrl的所有特性了。
具体到DirectUI,我们要想使用我们自定义的窗口过程,同样需要子类化:
HWND CWindowWnd::Subclass(HWND hWnd)
{
ASSERT(::IsWindow(hWnd));
ASSERT(m_hWnd==NULL);
m_OldWndProc = SubclassWindow(hWnd, __WndProc);
if( m_OldWndProc == NULL ) return NULL;
m_bSubclassed = true;
m_hWnd = hWnd;
return m_hWnd;
}
__WndProc就是自定义窗口过程,我们再来看看它的定义:
LRESULT CALLBACK CWindowWnd::__WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CWindowWnd* pThis = NULL;
if( uMsg == WM_NCCREATE ) {
LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);
pThis = static_cast<CWindowWnd*>(lpcs->lpCreateParams);
pThis->m_hWnd = hWnd;
::SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LPARAM>(pThis));
}
else {
pThis = reinterpret_cast<CWindowWnd*>(::GetWindowLongPtr(hWnd, GWLP_USERDATA));
if( uMsg == WM_NCDESTROY && pThis != NULL ) {
LRESULT lRes = ::CallWindowProc(pThis->m_OldWndProc, hWnd, uMsg, wParam, lParam);
::SetWindowLongPtr(pThis->m_hWnd, GWLP_USERDATA, 0L);
if( pThis->m_bSubclassed ) pThis->Unsubclass();
pThis->m_hWnd = NULL;
pThis->OnFinalMessage(hWnd);
return lRes;
}
}
if( pThis != NULL ) {
return pThis->HandleMessage(uMsg, wParam, lParam);
}
else {
return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
}
}
WM_NCCREATE消息是窗口收到的第一消息,在这个消息的处理里加入存在窗口对象的代码,那么,再收到后续的消息的时候,就可以使用GetWindowLongPtr来获取这个窗口对象,从而一步一步的下发消息到具体的控件(每个控件使用HandleMessage关注自身的消息,其它消息由系统处理)。