CRichEditCtrl: Cannot replace selection with Unicode string

118 Views Asked by At

In MFC/C++, I have a rich edit 2.0 control, class CRichEditCtrl, which is RichEdit20W. Rich edit 2.0 controls are supposed to support Unicode. I am using it to display Unicode characters. But the ReplaceSel function doesn't accept Unicode strings. For example

CRichEditCtrl re;
wstring line;
auto x = line.c_str(); // array of const wchar_t
re.ReplaceSel(x); // ERROR

gets a compiler error because CRichEditCtrl::ReplaceSel is prototyped as

void ReplaceSel(LPCTSTR lpszNewText, BOOL bCanUndo = FALSE);

and LPCTSTR is a const char *.

How can I replace using a Unicode string?

Windows 11 Pro Visual Studio 2022

2

There are 2 best solutions below

6
Constantine Georgiou On

If LPCTSTR is const char * then your project settings are multibyte characters and not unicode. The answer is simple, you have to convert the unicode string to ansi, using your preferred conversion function, eg WidecharToMultibyte(), wcstombs() or std::codecvt. Please note that you are converting the string to a type capable of representing fewer character values, ie these functions may fail.


EDIT:
You can call the IsWindowUnicode() function to check if the control is indeed unicode. And you can experiment further with the WM_SETTEXT message for example (not the SetWindowText() function) to find out with more certainty (ie if it works with ansi or wide strings). If your project setting is multibyte chars (ie the UNICODE identifier is not defined), then CEdit::ReplaceSel() takes an ansi string (as the function uses the dual/conditional T notation, which translates to either W or A, depending on UNICODE being defined/undefined), even if the underlying control is actually Unicode. This is a discrepancy of course, arising from the fact that MFC assumes that all its controls are ansi or unicode, consistent with the project setting. In this case I would try sending the EM_REPLACESEL message directly, (of which CEdit::ReplaceSel() is a wrapper), (ie re.SendMessage(EM_REPLACESEL, bCanUndo, (LPARAM)x)), or even calling ReplaceSel() with the string type cast (ie re.ReplaceSel((LPCTSTR)x)); although this may seem wrong it's actually not, as the cast won't perform any conversion, it will just keep the compiler from complaining and will pass the wide string address to the function (and finally the message), which is correct, assuming a Unicode underlying control.

0
Woody20 On

This is how I managed to use a Unicode rich text control in an ANSI project.

First, I created a new Win32 (not MFC) project, with the character set set to Unicode. The type of project was static library. This project contained only the following code:

#include "framework.h"
#include "windows.h"
#include "RichEdit.h"

HWND CreateRichEdit(HWND hwndOwner,        // Dialog box handle.
    int x, int y,          // Location.
    int width, int height, // Dimensions.
    HINSTANCE hinst)       // Application or DLL instance.
{
    LoadLibrary(TEXT("Msftedit.dll"));

    HWND hwndEdit = CreateWindowEx(0, MSFTEDIT_CLASS, TEXT("Type here"),
        ES_MULTILINE | WS_VISIBLE | WS_CHILD | WS_BORDER | WS_TABSTOP,
        x, y, width, height,
        hwndOwner, NULL, hinst, NULL);

    return hwndEdit;
}

I then used the CreateRichEdit function in my "real", not Unicode, project, by linking my project with the static library. Using the window handle output by CreateRichEdit, I could send Unicode text to the rich edit control, using edit messages.

Here is an example function which adds a line of Unicode text to the rich edit control:

void AddLine(HWND h, wstring &line)
{
    line += L"\n";
    GETTEXTLENGTHEX g; g.flags = GTL_DEFAULT; g.codepage = 1200; // Unicode
    size_t nBegin = ::SendMessageW(h, EM_GETTEXTLENGTHEX, (WPARAM)&g, 0);
    CHARRANGE r; r.cpMin = r.cpMax = (long)nBegin;
    auto what1 = ::SendMessageW(h, EM_EXSETSEL, (WPARAM)&r, 0);
    SETTEXTEX s; s.flags = ST_DEFAULT; s.codepage = 1200;
    auto ok = ::SendMessageW(h, EM_SETTEXTEX, (WPARAM)&r, (LPARAM)line.data()); // Combines the functionality of the WM_SETTEXT and EM_REPLACESEL
    return;
}

The last trick is that you need to use line.data(), not line, &line, or line.c_str() in the EM_SETTEXTEX message.