VC2005字符集设置容易出错的问题!
微軟托管環境已經出來好多年了,作為非托管環境下運行的VC,雖然它的編程效率比托管編程低多了,但是它能生存到今天,也足見它的強大!
但是VC使用比起.net Framework來確實比較難,常常令有經驗的軟件工程師也耗費大量的時間。最近由于一個小小的環境設置設置問題花了很多時間,這跟用慣了VC6突然轉到VC2005有關,寫出來讓大家參考參考!
一。字符集設置不同系統調用的函數也不同
我不知道VC.net2005默認工程默認設置是采用“Unicode字符集”(Unicode Character Set)的,以前用VC6工程的時候默認是“多字符集”(Multi-Byte Character Set)的。
我聲明了一個CString,按計劃給它賦值,就像下面:
CString s;
s.Format(“count = %d”,count);
如果是采用Multi-Byte Character Set是不會有錯誤的,因為這是我的環境采用的Unicode字符集的,而我給CString的Format函數是“多字符集”(Multi-Byte)所以編譯不通過,要知道在這種設置下使用MessageBox(“ddd”);編譯是不會通過的,因為系統調用的是MessageBoxW,即Unicode寬字符集的那個函數。
根據編譯器的提示把s.Format(“count = %d”,count);改成s.Format(_T(“count = %d”,count);就搞定了,_T代表一個宏,宏的意思就是把字符串轉成寬字符表示。同樣的,MessageBox(“ddd”);可以為MessageBox(_T(“ddd”));
但是還有個問題就是,所有窗體顯示的東西都是寬字符的,例如a在內存里就是a/0兩個字節,前面一個字節a后面是/0,當從窗體取下數據(例如用戶輸入)要跟其他平臺交互時,例如網絡傳輸到遠端機器。如果那邊使用的不是Unicode字符集,就會出問題,為了使界面和后臺傳輸一致,只好使用把寬字符轉換成多字符集表示:
CString strWideChar;
strWideChar.Format(_T(“這是寬字節哦”));
char buf[20];
???? memset(buf,0,20);
???? WideCharToMultiByte(? //轉換Unicode到Ansi
????????? CP_ACP,
????????? WC_COMPOSITECHECK | WC_DEFAULTCHAR,
????????? strWideChar,
????????? strWideChar.GetLength(),
????????? (char *)buf,? //轉換到緩沖區中
????????? 20,? //最多個字節
????????? 0,
????????? 0
???? );
同樣的,你接收到的字符串想要在界面正常顯示,還必須把它轉換成寬字節表示:
char chBytes[8];
memcpy(chBytes,”aaaaaaa/ 0”,8);
WCHAR wch[9];
n = MultiByteToWideChar( //轉換Unicode到Ansi
???? CP_ACP,
???? 0,
???? chBytes,
???? 8,
???? wch,? //轉換到緩沖區中
???? 8? //最多個字節
);
wch[n] = '/0';
這樣每次從界面取數據和把數據顯示到界面上都要先做處理,但是也可以把編譯環境設置成“多字符集”(Multi-Byte Character Set),就可以避免這樣轉換來轉換去。就是在”Project->Configuration Properties->General->Character Set,選擇“Use Unicode Character Set”就是使用Uncode字符集,選擇” Use Multi-Byte Character Set”就是多字節字符集。
二。字符集設置造成函數調用的失敗
我在CodeProject上找了一個很厚道的老外寫的一個繼承了CDialog窗體類CResizableDialog的源碼,這個類的作用是使MFC的窗體放大縮小時,窗體上的控件可以定位(Auchor),不要小看這個小小的每天都要用到的功能,用MFC實現真的很麻煩。很佩服那個老外寫了那么多代碼。
我拿了那個現成的工程,直接在我的工程里引用他的工程。Everything works perfect。直到我把項目發布成Release的,雙擊運行后沒有任何反應,Very weird!后來我用MessageBox打印消息,發現運行到DoModal函數里面就沒有出來,程序直接退出了!使用try,catch都得不到錯誤!因為我的窗體是繼承老外寫的窗體類來的,原先繼承CDialog是好好的,問題肯定在他的工程里面,可是他給的示例程序沒有任何問題啊。
我又拿一個前的測試程序,讓它從CResizableDialog繼承,也沒有任何問題。
一時間不知道哪里出現了問題,Release又不能像Debug那樣調試,打了一堆MessageBox后還是不知道問題出現在哪里。憑著經驗,可以知道程序中可能出現了內存的越界訪問什么的致命錯誤,才會導致程序“一聲不吭”地退出,但是究竟哪里出了問題呢?
就在束手無策的時候,我發現調用CResizableDialog的成員函數EnableSaveRestore會引發鏈接錯誤:“未定義的外部符號”,不引用它不會出錯,測試程序引用它沒有任何錯誤。通常這個錯誤造成是因為引用函數在.h文件里聲明了,但是在.cpp里面沒有定義,或者.cpp文件里的定義和.h上的函數簽名不一樣。但是此時不可能是這個錯誤,因為測試程序沒有錯誤啊。直覺告訴我這是解決“Release后程序直接退出的關鍵”,說不定這個函數調用的問題解決了Release的問題也解決了。
既然調用EnableSaveRestore出現了不該出現的錯誤,那么就從這個函數開始找。這個函數是這樣的:
.h文件聲明
void EnableSaveRestore(LPCTSTR pszSection, BOOL bRectOnly = FALSE);
.cpp文件定義
void CResizableDialog::EnableSaveRestore(LPCTSTR pszSection, BOOL bRectOnly/* = FALSE */)
{
?????? m_sSection = pszSection;
?????? m_bEnableSaveRestore = TRUE;
?????? m_bRectOnly = bRectOnly;
?????? // restore immediately
?????? LoadWindowRect(pszSection, bRectOnly);
}
上面的代碼沒有任何錯誤,既然沒有錯誤,就要用使用以下方法來找:
1.重新為CResizableDialog寫一個函數,它沒有參數的,調用它,發現沒有錯誤,看來參數有問題。
2.既然沒有參數的函數沒有錯誤,就把出問題的函數參數去掉吧,竟然也沒有錯誤!那問題就肯定是出在參數上。
3.去掉其中一個參數,測試發現是LPCTSTR pszSection的問題,而不是BOOL bRectOnly的問題。
4.既然這樣,那就換一種表示吧,把LPCTSTR pszSection換成WCHAR* pszSection,運行它,竟然不出錯了!翻開MFC宏定義,就會發現其實LPCTSTR和WCHAR*是一樣的!
5.但是這個函數功能還是不正常,斷點進入那個函數里面發現傳進去的字符串只有一個字符了,這種情況就是寬字符當成短字符時,第二個字節的/0當成了字符串的截止字符了,也就是說,這個函數里采用的是短字符(多字符集Multi Byte)處理的。
6.我的工程采用的是寬字符集(Unicode Char)的,檢查設置,原來那個老外是用VC6編的,默認是使用多字符集(Multi Byte)的,VC真是笨啊,兩個Project在一個Solution里面完全不同的設置竟然沒有任何提示,簡直把我弄死了!
7.把引用工程也改成使用Unicode字符集,并且把函數EnableSaveRestore WCHAR* pszSection恢復原樣,搞定!果然不出我所料,Release也沒有問題了!
看來VC排錯需要 一點技巧啊。
總結
以上是生活随笔為你收集整理的VC2005字符集设置容易出错的问题!的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SQL Server常用数据类型
- 下一篇: 移动开发之微信小程序——资料集合