I tried using CRichEditCtrl::GetLine()
to retrieve the text of a given line of a rich-edit control in an MFC application built with VS2015 in Unicode mode, and running on Windows 10.
I wrote this helper function:
CString GetLine(CRichEditCtrl& richEdit, const int lineNum)
{
int lineLength = richEdit.LineLength(richEdit.LineIndex(lineNum));
if (lineLength == 0)
{
// Empty line
return CString();
}
const int kMinBufferLength = sizeof(int) / sizeof(wchar_t);
const int bufferLength = max(kMinBufferLength, lineLength);
CString line;
wchar_t* buffer = line.GetBuffer(bufferLength);
lineLength = richEdit.GetLine(lineNum, buffer, bufferLength);
line.ReleaseBuffer(lineLength);
return line;
}
This code works fine, except for lines containing only one character. In this case, CRichEditCtrl::GetLine()
returns 2 (instead of the expected 1), and the output buffer contains the correct character, followed by a \r
.
Why is that? Why is the \r
added only for single-character lines and not for lines containing more characters?
I was able to fix that adding a special case if
like this:
// Code inserted after the richEdit.GetLine() call, before the line.ReleaseBuffer() call:
// *** Special Case ***
// It seems that when there's only one character (e.g. 'C') in the line,
// CRichEditCtrl::GetLine() returns 2, and appends a '\r' after
// the read character in the output buffer.
if ((lineLength == 2) && (buffer[1] == L'\r'))
{
// Chop off the spurious '\r'
lineLength = 1;
}
However, it's not clear to me the reason for this special-case behavior.
P.S: The CRichEditCtrl::GetLine()
MFC code that is invoked is:
int CRichEditCtrl::GetLine(_In_ int nIndex, _Out_writes_to_(nMaxLength, return) LPTSTR lpszBuffer, _In_ int nMaxLength) const
{
ASSERT(::IsWindow(m_hWnd));
ENSURE(sizeof(nMaxLength)<=nMaxLength*sizeof(TCHAR)&&nMaxLength>0);
*(LPINT)lpszBuffer = nMaxLength;
return (int)::SendMessage(m_hWnd, EM_GETLINE, nIndex, (LPARAM)lpszBuffer);
}
So this seems just a tiny wrapper around the EM_GETLINE
message.
The MSDN doc for EM_GETLINE
states that "the return value is the number of TCHAR
s copied" (in my case, the wchar_t
s). For one-character lines the return value is two, instead of the expected one. So, sounds like the rich-edit control is actually returning the single character followed by a spurious \r
in this special case.
For lines containing more than one characters, the returned value is the actual number of characters, as expected (I tried with simple English/ASCII characters, to avoid complications of Unicode surrogate pairs and other stuff).
I got it to work without special-casing by using the other overload of
CRichEditCtrl::GetLine()
:The reference for
EM_GETLINE
says that you have to write the size of the buffer into the buffer, while this actually is the number of characters you request.The reference for the macro
Edit_GetLine()
which sendsEM_GETLINE
has it correct:The macro writes the
cchMax
parameter to the buffer before callingSendMessage()
which is exactly the same as my code above.I also think that the condition in the 3-parameter overload of
CRichEditCtrl::GetLine()
which causes an exception if you request less than 2 characters, is incorrect.The return value is zero (0) if the line is not valid.
If the line is empty it makes sense to return 1 and '\r' in the buffer. That would mean that '\r' is always returned when the line number is valid.
The function reference says that the buffer should be at least 4 bytes long, because a WORD is written to the buffer before being passed to SendMessage.
sizeof(nMaxLength) in the ENSURE function is the size of an int or WORD.
CRichEditCtrl::GetLine
CRichEditCtrl::GetLineCount has some code.