Why doesn't EM_SETTEXTMODE work?
Asked Answered
U

1

5

I'm trying to use EM_SETTEXTMODE on a RichEdit control in Delphi 7.

Just create a new project, add a TRichEdit control and a TButton control and add the following code to the button's click handler:

  SendMessage(RichEdit1.Handle, WM_SETTEXT, 0, LPARAM(PChar('')));
  Button1.Caption := IntToStr(SendMessage(RichEdit1.Handle, EM_GETTEXTMODE, 0, 0));
  Button1.Caption := Button1.Caption + ' ' + IntToStr(SendMessage(RichEdit1.Handle, EM_SETTEXTMODE, TM_PLAINTEXT, 0));
  Button1.Caption := Button1.Caption + ' ' + IntToStr(SendMessage(RichEdit1.Handle, EM_GETTEXTMODE, 0, 0));

The button's caption is set to 38 0 38 after clicking on the button, meaning the text mode didn't change at all - initially it was 38 (TM_RICHTEXT or TM_SINGLELEVELUNDO or TM_MULTICODEPAGE), then SETTEXTMODE was successful (0) but even after that it is still 38.

The RichEdit's text is cleared before using EM_SETTEXTMODE as suggested by the documentation.

I've tried setting different values with EM_SETTEXTMODE and it always stays 38.

I noticed that EM_SETTEXTMODE always returns 0 (success) even if the RichEdit control contains text before calling it.

I tried using RichEdit1.Perform instead of SendMessage - no difference.

I've found several threads in various forums on this issue, and it wasn't resolved in any of them.

Any idea why isn't this working?

Unreserved answered 11/2, 2014 at 19:17 Comment(0)
D
9

Older Delphi versions load the RichEdit control which is located in RICHED32.DLL. Newer Delphi versions load RICHED20.DLL.

RICHED32.DLL exposes many problems, like yours. The fix is modifying comctrls.pas so that it loads RICHED20.DLL. That will most likely not be enough as there may be compatibility issues with this DLL and the VCL code. Look for the TCustomRichEdit.CreateParams() procedure, it contains the LoadLibrary call.

I tested your code in Delphi XE and there it works, so your best option is to upgrade to a more recent Delphi version.

UPDATE

I did some tests with Delphi 5 and it seems to be enough to change 2 functions. Copy Comctrls.Pas into your project and execute these modifications:

1) in TCustomRichEdit.CreateParams(), change

const
  RichEditModuleName = 'RICHED32.DLL';

into

const
  RichEditModuleName = 'RICHED20.DLL';

and

CreateSubClass(Params, 'RICHEDIT');

into

CreateSubClass(Params, 'RICHEDIT20A');

2) in the procedure TRichEditStrings.Insert(), change

if RichEdit.SelStart <> (Selection.cpMax + Length(Str)) then
      raise EOutOfResources.Create(sRichEditInsertError);

into

if RichEdit.SelStart <> (Selection.cpMax + Length(Str) - CountLineBreaks(Str)) then
      raise EOutOfResources.Create(sRichEditInsertError);

code for CountLineBreaks/PosEx:

function PosEx(const SubStr, S: string; Offset: Cardinal = 1): Integer;
var
 Tmp: PChar;

begin
 Result := 0;
 if (Offset > Cardinal(Length(S))) then exit;
 if Offset = 1 then
  Result := Pos(SubStr, S)
 else
  begin
   Tmp := StrPos(PChar(@S[Offset]), PChar(SubStr));
   if Tmp = nil then exit;
   Result := Cardinal(Tmp - PChar(@S[Offset])) + Offset;
  end;
end;


function CountLineBreaks(const S: string): Integer;
const
  LB = #13#10;
var
  P: Integer;
begin
  Result := 0;
  P := Pos(LB, S);
  while P <> 0 do
  begin
    Inc(Result);
    P := PosEx(LB, S, P + 2);
  end;
end;

Doing this in Delphi5, I get the correct result:

Delphi5

One thing to keep in mind is that RichEdit 2.0 replaces CRLF pairs with CR, so other issues may creep up. Don't shoot me if you run into problems down the road...

Diadem answered 11/2, 2014 at 20:34 Comment(5)
+1 Make these changes in a local copy of ComCtrls.pas that you include in your project.Orb
@Diadem Thanks for your answer, the fix works. A couple of follow-up questions: 1. What other problems does RICHED32.DLL expose? 2. What other issues like the CRLF difference might occur when using RichEdit 2.0? Can you point me to a place where I can read up more about the differences? And one "bonus" question: is there a better way to apply this fix than to copy the entire ComCtrls unit just to fix a couple of functions? I though about making interceptor classes, but TRichEditStrings doesn't appear in the interface section of ComCtrls.Unreserved
@jedivader: the best "fix" is to upgrade, really, D7 is getting really OLD now... As for your question, RICHED32.DLL dates from the WIN98 era, so I'm not surprised that there are "problems"?Diadem
@jedi Copying ComCtrls and making changes is absolutely the best fix short of upgrading. Do it the way I said, it works a treat.Orb
@Davd yes, it works just fine, I just want to optimize the solution and include less code in the project. If TRichEditStrings was declared in the interface section, using interceptor classes would have been the best way, because you would only include in your project the code that is different from what's already there in ComCtrls.pas. I was wondering if there is a similar way to do this in this case. It's not a big deal, including the entire unit works just fine, I just want to include less code if possible, that's what I'm asking.Unreserved

© 2022 - 2024 — McMap. All rights reserved.