Search code examples
delphidelphi-7richeditinput-language

Delphi: TRichEdit Text in non-default non-Unicode system language as String (ANSI)


So here's the setup:

  1. Make a new Delphi 7 application with a TRichEdit control on it. We are talking non-Unicode applications here.
  2. Install a new Input language in Windows' Regional and Language Options, that has a different encoding from the encoding of your default Language for non-Unicode programs - for example Greek.
  3. Add a button in the application, and in its OnClick handler add Button1.Caption := RichEdit1.Text;, and set its Font.Charset to the charset of the input language you just installed (GREEK_CHARSET if we stick to this example).
  4. Run the application, switch to your new (Greek) input language, type a few letters in the RichEdit and press the button - the button's caption now has ???? symbols instead of Greek characters.

  5. Now, if you set your default Language for non-Unicode programs to Greek (Windows restart required), this problem would disappear - greek characters would appear properly. Set your default Language for non-Unicode programs back to what it was before and the problem is there again.

So I would guess that TRichEdit works with Unicode internally, as changing its Font.Charset value never changes anything - the RichEdit accepts any installed Input language properly, and if you have installed two different non-latin languages which use different character sets (Greek /GREEK_CHARSET/ and Russian /RUSSIAN_CHARSET/ for example) it would accept them both without changing its Font.Charset.

I would also guess that when you get the .Text (or .Lines[i]) value of the TRichEdit, it converts its internal Unicode text to ANSI, based on the Windws' default Language for non-Unicode programs.

Further more, assigning the .Text value to a WideString or a UnicodeString also doesn't work properly (the text is once again in ???? instead of the proper characters), it's not only when you assign it to a String (AnsiString).

So here's the question:

I want to be able to convert the text of a RichEdit to a String (ANSI) properly, based on a character set of my choosing instead of the system's default Language for non-Unicode programs. How can I do that? I would prefer a solution that doesn't involve third party components, but, of course, if not possible - anything would do.

Thanks!

P.S.: Switching to Delphi 2009 or later is not an acceptable solution.


Solution

  • Send the underlying rich edit window the EM_GETTEXTEX message. You pass a GETTEXTEX struct which specifies the code page.

    So, something like this would pull the text out into a UTF-16 encoded WideString:

    function GetRichEditText(RichEdit: TRichEdit): WideString;
    var
      GetTextLengthEx: TGetTextLengthEx;
      GetTextEx: TGetTextEx;
      Len: Integer;
    begin
      GetTextLengthEx.flags := GTL_DEFAULT;
      GetTextLengthEx.codepage := 1200;
      Len := SendMessage(RichEdit.Handle, EM_GETTEXTLENGTHEX, 
        WPARAM(@GetTextLengthEx), 0);
      if Len=E_INVALIDARG then
        raise Exception.Create('EM_GETTEXTLENGTHEX failed');
      SetLength(Result, Len);
      if Len=0 then
        exit;
      GetTextEx.cb := (Length(Result)+1)*SizeOf(WideChar);
      GetTextEx.flags := GTL_DEFAULT;
      GetTextEx.codepage := 1200;
      GetTextEx.lpDefaultChar := nil;
      GetTextEx.lpUsedDefChar := nil;
      SendMessage(RichEdit.Handle, EM_GETTEXTEX, WPARAM(@GetTextEx), 
        LPARAM(PWideChar(Result)));
    end;
    

    You can then convert that UTF-16 string to whatever code page you like. If you'd rather pull it out in a specific code page directly, then do it like this:

    function GetRichEditText(RichEdit: TRichEdit; AnsiCodePage: UINT): AnsiString;
    var
      GetTextLengthEx: TGetTextLengthEx;
      GetTextEx: TGetTextEx;
      Len: Integer;
    begin
      GetTextLengthEx.flags := GTL_DEFAULT;
      GetTextLengthEx.codepage := AnsiCodePage;
      Len := SendMessage(RichEdit.Handle, EM_GETTEXTLENGTHEX, 
        WPARAM(@GetTextLengthEx), 0);
      if Len=E_INVALIDARG then
        raise Exception.Create('EM_GETTEXTLENGTHEX failed');
      SetLength(Result, Len);
      if Len=0 then
        exit;
      GetTextEx.cb := (Length(Result)+1)*SizeOf(AnsiChar);
      GetTextEx.flags := GTL_DEFAULT;
      GetTextEx.codepage := AnsiCodePage;
      GetTextEx.lpDefaultChar := nil;
      GetTextEx.lpUsedDefChar := nil;
      SendMessage(RichEdit.Handle, EM_GETTEXTEX, WPARAM(@GetTextEx), 
        LPARAM(PWideChar(Result)));
    end;