Solved

::GetWindowText/RichEdit Inconsistencies

Posted on 2002-03-21
8
1,698 Views
Last Modified: 2012-06-27
I'm using VC++ 6.0/SP4 on Windows NT 2000/SP2. The problem I've discovered is inconsistencies in what ::GetWindowText() returns when it is passed a handle to a RICHEDIT control.

In the example below IDC_EDIT is a RICHEDIT control and there has been over 255 characters entered in the field.

-----
CString str;

int nResult = ::GetWindowText(GetDlgItem(IDC_EDIT)->m_hWnd, str.GetBuffer(256), 256);    

str.ReleaseBuffer();

CString strResult;

strResult.Format("::GetWindowText() returned: %d Value: \n%s", nResult, (LPCTSTR) str);

AfxMessageBox(strResult);
-----

On my machine (NT2000) ::GetWindowText() returns 0 and str is empty. Calling ::GetLastError() returns 122 (ERROR_INSUFFICIENT_BUFFER). A true statement, but the documentation says:

-----
int GetWindowText(
  HWND hWnd,        // handle to window or control
  LPTSTR lpString,  // text buffer
  int nMaxCount     // maximum number of characters to copy
);

Parameters

hWnd
[in] Handle to the window or control containing the text.
lpString

[out] Pointer to the buffer that will receive the text.
nMaxCount

[in] Specifies the maximum number of characters to copy to the buffer, including the NULL character. If the text exceeds this limit, it is truncated.
-----

On NT4/SP5 and on Windows 95 the function returns 255 and str is equal to the first 255 characters.

I'd have expected XP to act the same as NT 2000, and it does sort of. It returns 0, but str is set to the 1st 256 of data. This is alarming, because I first discovered this problem in the DDX_Text() processing. The AfxSetWindowText() utility function located in WINUTIL.CPP defines a buffer like so:

TCHAR szOld[256];

Then calls the API like so:

::GetWindowText(hWndCtrl, szOld, _countof(szOld))

I wonder if it returned the NULL also? If so, then the stack is messed up.

Has anyone seen this behavior before?

Thanks,
Steve
0
Comment
Question by:SteveGTR
  • 4
  • 4
8 Comments
 
LVL 49

Expert Comment

by:DanRollins
ID: 6888243
Interesting problem.  I can verify your findings for Win2K but The only documentation I can find agrees with what you said "if it is too long, it will be truncated"

However, this is kinda subtle:  It doesn't say "it will be truncated to the specified length"  just "it will be truncated."  Thus, the documentation is correct, if misleading.

You probably already know the various workarounds, but in case you don't, I'll list a couple.  The simplest is:

MFC:
CString str;
GetDlgItem(IDC_EDIT)->GetWindowText( str );

For some reason, you are mixing MFC and Win32 API, so I'll show you the pure Win32 version:

Win32 API:
int nLen= ::GetWindowTextLength( hWndOfEditCtrl );
char pszText= new char[nLen];
::GetWindowText( hWndOfEditCtrl, nLen );

In other words, do what MFC does:  Request the length, then provide a buffer that large.  You will always get the data.

-- Dan
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 6888247
Just a hunch, but I'll bet that this is a security-related issue.  Maybe there is a buffer overrun exploit possible if GetWindowText works as advertised.

-- Dan
0
 
LVL 30

Author Comment

by:SteveGTR
ID: 6888537
Maybe I didn't make it clear in my long spewing forth of information. I discovered this problem in the DDX_Text() processing. I was looking into a problem where a RICHEDIT control was not being reset to blank when the user requested a new record. The value of the control had been over 255 characters and was being set to 0 characters.

Here is the flow:

m_strMyRichEdit = _T("");
UpdateData(FALSE);

As you know in the DoDataExchange() function there will is the following:

DDX_Text(pDX, IDC_MYRICHEDIT, m_strMyRichEdit);

This function executes the DDX_Text() function in DLGDATA.CPP that calls AfxSetWindowText() in WINUTIL.CPP:

void AFXAPI AfxSetWindowText(HWND hWndCtrl, LPCTSTR lpszNew)
{
     int nNewLen = lstrlen(lpszNew);
     TCHAR szOld[256];
     // fast check to see if text really changes (reduces flash in controls)
     if (nNewLen > _countof(szOld) ||
          ::GetWindowText(hWndCtrl, szOld, _countof(szOld)) != nNewLen ||
          lstrcmp(szOld, lpszNew) != 0)
     {
          // change it
          ::SetWindowText(hWndCtrl, lpszNew);
     }
}

There is the reference to ::GetWindowText(). On NT 2000, 0 is returned and no data is placed in szOld. So the "if" statement resolves to FALSE. The result is the field does not get updated and remains equal to the previous value.

So all this is stock MFC processing.

Thanks for looking at this Dan. I really appreciate it.
0
 
LVL 49

Accepted Solution

by:
DanRollins earned 200 total points
ID: 6889514
Wow!  I think you have uncovered a bonefide bug in MFC!  You should report this to Microsoft.

Here is how to solve it.  Write this function:

void AFXAPI DDX_RichText(CDataExchange* pDX, int nIDC, CString& value)
{
     HWND hWndCtrl = pDX->PrepareEditCtrl(nIDC);
     if (pDX->m_bSaveAndValidate) {
          int nLen = ::GetWindowTextLength(hWndCtrl);
          ::GetWindowText(hWndCtrl, value.GetBufferSetLength(nLen), nLen+1);
          value.ReleaseBuffer();
     }
     else {
          ::SetWindowText(hWndCtrl, value);
     }
}


Now modify your program as shown:

void CD03Dlg::DoDataExchange(CDataExchange* pDX)
{
     CDialog::DoDataExchange(pDX);
     //{{AFX_DATA_MAP(CD03Dlg)
     DDX_RichText(pDX, IDC_RICHEDIT1, m_sRichEd); // <---- MODIFIED!
     //}}AFX_DATA_MAP

-==-=--==--=-=-==-=-=-=-=-=-
There is no there way (other than forsaking the DDX mechanism and using SetWindowText manually rather than UpdateData).  

The undocumented AfxSetWindowText definately malfunctions under the circumstances you specify when running under Windows 2000.  Here is a full description, in case you want to inform Microsoft:

"
If the current contents of the Edit control -OR- RichEdit control are longer than 256, then it is possible that the the DDX_Text function will malfunction.  It might not update the control with the new text in the associated CString variable.

The reason: The MFC private function AfxSetWindowText might not set the text because it calls
 ::GetWindowText( hWnd, buf, 256 );
which can return 0 under Windows 2000 in the situation described.  

Furthermore, the AfxSetWindowText is quite dangerous because it can (and does) call lstrcmp against an UNINITIALIZED LOCAL BUFFER.
"

-- Dan
0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 30

Author Comment

by:SteveGTR
ID: 6889755
Thanks for the verification Dan. I've submitted this as a bug to Microsoft.
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 6889806
In the thousands of Q's I've read here, I've seen dozens claiming the problem is a bug in MFC.  This is the first one that I could verify.  And this bug exists only because of an unexpected change to a Win32 API function that one would think was chiseled in granite.

The most significant thing is that the same error occurs for a regular old Edit control.  It might also exist for other controls, such as ListBoxes and ComboBoxes, and Popup Window titles etc. (though these will rarely be affected because >256 chars there would be unusual...)

If you ever hear back from MS, I'd be interested in what they have to say.  I have the latest MFC source code from DevStudio.NET at home.  I'll check it to see if this bug was quietly corrected 'inline'

-- Dan
0
 
LVL 30

Author Comment

by:SteveGTR
ID: 6889873
Dan,

I believe that this is a bug in NT2000 and XP handling of RICHEDIT controls. It happens to surface in MFC code. The way XP handles the function really scares me.

Also, this problem does occur when dealing with EDIT controls. The ::GetWindowText() function will return 255 and truncate to the 1st 255 characters for EDIT controls. Having said that, I've only tested that on NT2000. It's Friday and I'm just too lazy to walk to the other end of the office to test it on XP :)

Have a great weekend.
0
 
LVL 30

Author Comment

by:SteveGTR
ID: 6889878
The Also sentence should have a "not" after does. As in, "Also, this problem does not occur when dealing with EDIT controls".

That's what I get for not proofing my messages...
0

Featured Post

Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

Join & Write a Comment

In days of old, returning something by value from a function in C++ was necessarily avoided because it would, invariably, involve one or even two copies of the object being created and potentially costly calls to a copy-constructor and destructor. A…
Many modern programming languages support the concept of a property -- a class member that combines characteristics of both a data member and a method.  These are sometimes called "smart fields" because you can add logic that is applied automaticall‚Ķ
The viewer will learn how to user default arguments when defining functions. This method of defining functions will be contrasted with the non-default-argument of defining functions.
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.

757 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

21 Experts available now in Live!

Get 1:1 Help Now