Thursday, 27 September 2012

GetWindowText() and IsEmpty() doesn't work on Windows 7 ?!?!

Normally, when someone comes to you and blames some bugs on compiler, or even worse, on the computer, you'd only smile mildly and give him some of the age-old programming wisdom. I said normally. Because today I tried to debug some Windows code ported from VisualStudio 2005 to VisualStudio 2010, and stumbled upon two impossible-to-think-of bugs.  And as googling didn't bring ther right results immediatelly, I will document it here for myself an others. Let's pray Google indexes it* for the sake of all the poor programmers forced to work on Windows!

As a matter of fact, I've seen a similiar problem when initially porting the code, but it was only one reptitive  bug, so I forgot about it. Here a code snippet as illustration:

  // bug VisStudio 2010: "LPCSTR comparison not working in 64 bit OS"
  // - workaround: use CString
  //int iSelSysPath = LBCBFindStringExact(*this, IDC_CB_SYSTEMPATH, 0, TRUE, m_ActSubrange.cSystemPath);
  CString sysPath(m_ActSubrange.cSystemPath);
  int iSelSysPath = LBCBFindStringExact(*this, IDC_CB_SYSTEMPATH, 0, TRUE, sysPath);
Ok, that may happen, I'm not particuralry picky on such errors. But today, there were two more bugs I didn't catch initially. One ratehr simple, so I only cite it here for reference reasons:

  // bug Windows 7!!!
  // ARRAY_SIZE (m_pBMMSett->Station[m_iStation].Subrange.cSystemPath));
  //m_strPath = m_pBMMSett->Station[m_iStation].Subrange.cSystemPath; 
  CString tmp;
  ::LB_CB_GetLBText(m_cbSysPath.m_hWnd, LB_CB_GetCurSel(m_cbSysPath.m_hWnd, true), tmp.GetBuffer(), true);
  m_strPath = tmp;
As you can see, you cannot read the contents of a combo box! Its filled, but you continue to get an empty striong over and over again! Fortunately, like the bug above, this is a known issue, and my collague told me that that GetWindowsText() fails when the addres of the target isn't even-valued. Well, somehow creepy, but if that's a known issue, then what the fuss...

The next bug however wasn't that simple to fix. In fact we stared some 15 minutes at a piece of code where the IsEmpty() method of the MFC's (notorious) CString class reported true for an obviously non empty string! How can things get that wrong? The reason was found after some googling (though some deeper and harder debgging would solve it too), here's the relevant post:
I wonder if your getting a referencing problem.  CString attempts to not create new values if it thinks it can use the same one for multiple occurrences.  I've been caught by this before.  I usually force the issue by using the LPCTSTR operator like:

CString cs = (LPCTSTR) csValue;
Might be worth a try anyway.
And he (i.e. Tom) was right, because the following change forced a copy and in result fixed the problem:
  //bool empty = m_aStrSettPath.IsEmpty(); <-- not working! internal CString cache issues
  CString p = (LPCTSTR)m_aStrSettPath[i];  
  bool empty = p.IsEmpty()  
But now hold on! The basic operation on strings is broken? Can it get any worse? If you can -stay away from MFC, if you cannot - wel,l I hope you'll be abble to google this post.

* an that's the reason for the grammatical error in the title of this post


Антон Капралов said...

Could you show declarations of LBCBFindStringExact and m_aStrSettPath?

Marek Krj said...

@Антон Капралов
Sorry, I cannot, it's confidential code. But LBCBFindStringExact() looks for a string in all entries of a combo box and m_aStrSettPath is a CString value.

Anonymous said...

Thank you for description CString problem!)

Anonymous said...

Thank you, sir, you fixed my (ridiculous) problem!