近期,公司的一個項目是採用華邁SDK來做一套C/S架構的監控中心。自己犯了兩個低級錯誤,寫出來看看有多少朋友平時跟我一樣沒注意,哈哈。
第一個錯誤是這樣出現的:在最初的幾個版本中比較順利。於是有點飄飄然,連續提交了好幾個版本都沒有進行全面測試。直到一個完成得差不多的版本出現後,來了一次全面的測試。這個時候發現,居然主介面顯示不正常。找了好久原因沒找到,只好回退到之前最初幾個正常的版本,然後建立了個分支專門來排除此問題。一個一個版本的合并,一個一個檔案的差異對比,最終定位到一個版本中原來把CXXX::OnPaint()中的CPaintDC dc(this);寫成了CClientDC dc(this);
下面是錯誤分析。
《Windows 程式設計》裡曾經說過,在WM_PAINT中一定要成對使用BeginPaint跟EndPaint。否則程式會不停產生WM_PAINT訊息。而MFC的CPaintDC的構造與析構中封裝了BeginPaint及EndPaint。而CClientDC則沒有,所以在OnPaint中使用CClientDC當然是不行的。慚愧啊,慚愧。一時疏忽浪費我整整一個下午找這個BUG。
第二個錯誤更加慚愧了。直接看碼:
void CDxHMContainer::OnPaint(){ CPaintDC dc(this); for (UINT i = 0; i < m_vecChilds.size();i++) { if (m_vecChilds[i]->IsSelect()) { CRect rt; CPen aNewPen,*pOldPen; int nOldRop = dc.SetROP2(R2_NOTXORPEN); m_vecChilds[i]->GetClientRect(rt); rt.InflateRect(2,2); m_vecChilds[i]->ClientToScreen(rt); this->ScreenToClient(rt); aNewPen.CreatePen(PS_SOLID,3,m_colorFrame); pOldPen = (CPen*)dc.SelectObject(aNewPen); dc.Rectangle(rt); dc.SelectObject(pOldPen);// 這裡報錯 dc.SetROP2(nOldRop); } }}
這段代碼很詭異,有時候運行個兩三個小時不會出錯,有時候三五分鐘就出錯。請先不要著急看下面的錯誤分析,你能看出這段代碼有什麼問題嗎?
當初這段代碼報錯,說實話我一時半會還真沒找到原因。最後看MSDN的關於CDC::SelectObject的相關文檔,才終於找到原因。
CDC::SelectObject重載了很多種方法。上面代碼出錯的原因是第一次調用使用的的函數原型是 HGDIOBJ CDC::SelectObject(HGDIOBJ),
而第二次使用的函數原型確是 CBitmap* CDC::SelectObject(CBitmap*)。對於上述代碼第一次調用的時候,將HGDIOBJ強行轉成CBitmap*實在有點慘不忍睹啊。。。。。。如此,便導致了悲催的崩潰。