一、WindowImplBase的bug
在第8個教程
【2013 duilib入門簡明教程 -- 完整的自繪標題欄(8)】中,可以發現窗口最大化之后有兩個問題,
1、最大化按鈕的樣式還是沒變
,正確的樣式應該是這樣的


2、再次點擊最大化按鈕,不能還原到正常大小。
這個是WindowImplBase的bug,已經提交給官方有一段時間了,但是貌似沒有被合並到SVN上去,所以這里說明一下,
我們需要在WindowImplBase的OnSysCommand函數里,在if( ::IsZoomed(*this) != bZoomed )里面加上下面這段代碼:
if( ::IsZoomed(*this) != bZoomed ) { CControlUI* pbtnMax = static_cast<CControlUI*>(m_PaintManager.FindControl(_T("maxbtn"))); // 最大化按鈕 CControlUI* pbtnRestore = static_cast<CControlUI*>(m_PaintManager.FindControl(_T("restorebtn"))); // 還原按鈕 // 切換最大化按鈕和還原按鈕的狀態 if (pbtnMax && pbtnRestore) { pbtnMax->SetVisible(TRUE == bZoomed); // 此處用表達式是為了避免編譯器BOOL轉換的警告 pbtnRestore->SetVisible(FALSE == bZoomed); } }
二、CDuiString的bug (重溫了一下 Effective C++,發現這就是
條款24所指出的問題,看來讀書百遍不如寫代碼一遍啊)
在Notify處理消息時會有很多if語句,我通常喜歡把常量放在雙等號前面,變量放在后面,比如:
if( _T("click") == msg.sType )
{
}
但是卻發現並沒有進到這個if里,調試發現,將常量調到前面時,並沒有進入到CDuiString重載的 == 函數里面,所以這里必須將常量放到后面。
if( msg.sType == _T("click") )
{
}
這個bug的原因是因為將常量放在前面時,並沒有調用CDuiString重載的 == 函數,而是調用了CDuiString重載的 ()函數,然后用系統自帶的==函數做比較,而系統自己的==函數只是比較兩個指針的首地址是否相等。_T("click") 的首地址指向的是一塊臨時變量,而msg.sType 是返回了CDuiString里面那個字符串的指針,很顯然這兩個指針地址是不相等的,所以我們只能把它放在前面,或者直接調用_tcscmp
if( ! _tcscmp( _T("click"), msg.sType) )
{
}
當然,如果要解決這個bug,就要重載多個==操作符,
由於CDuiString是將==函數作為成員函數重載的,所以只有CDuiString對象在操作符左邊時,才會調用這個重載函數,如果想要CDuiString對象在右邊時也能調用重載的==函數,那么必須將重載操作符放到外部。這里我們可以看一下MFC的CString是怎么重載的:

CString 重載了5個==操作符,都是友元函數,定義在#include<cstringt.h>里面。
再看下STL的std::string :

std::string重載了3個==操作符,都是全局函數,定義在#include<string>里面。

不過需要提醒的是,我稍微看了下CDuiString的代碼,有很多漏洞,比如在清零字符串時,只是調用了 m_szBuffer[0] = '\0'; 並不是調用memset,那么就會有以下問題,我們隨意現在用下面兩種方式給CDuiString 附值,然后監視字符串數組的內容,可以發現雖然顯示是正確的,但是在零值后面的值全部是亂碼:


這樣的話,雖然_tcslen、_tcscmp等函數能用,但是還有很多函數都會出問題的。
所以這個CDuiString能不用則不用,如果嫌MFC生成的exe體積大,可以用WTL的CString,如果WTL也不想用,那就只好用STL的string了。
當然,為了保證代碼的兼容性,一些簡單的處理還是用CDuiString比較好,比如 msg.sType。
而邏輯處理等復雜的場景,最好用久經考驗的代碼。 雖然微軟的CString很強大,而用STL的string時可能不方便,但是我現在越來越喜歡STL的string啦,當然,我是定義了一個string_t,加上一個名字空間,以防和一些開源庫沖突,
一些著名的開源庫喜歡如下定義:
#ifdef _UNICODE typedef std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > string_t; #else typedef std::basic_string<char, std::char_traits<char>, std::allocator<char> > string_t; #endif
不過我喜歡更簡短的定義:
#ifdef _UNICODE
typedef std::wstring string_t;
#else
typedef std::string string_t;
#endif
下面是我常用的Unicode定義:
#include <string> #include <sstream> namespace duilib { #ifdef _UNICODE typedef wchar_t char_t; typedef std::wstring string_t; typedef std::wstringstream stringstream_t; #else typedef char char_t; typedef std::string string_t; typedef std::stringstream stringstream_t; #endif }