How to resolve printing problems with TRichEdit on Windows 2000

By: mykle hoban

Abstract: This article describes how to resolve the infinite-spooling problem when printing a TRichEdit under Windows 2000

Question

I'm trying to use a TPrintDialog with a TRichEdit under Windows 2000 and it's printing enormous amounts of blank pages (spooling infinitely). How do I fix this?

Answer

This is a problem with the way the EM_FORMATRANGE message changed with the advent of Windows 2000. The problem can be easily fixed by modifying ComCtrls.pas (located within $(BCB)sourcevcl), and compiling that file with (adding it to) all your projects that are affected by this problem. (NOTE: this change is unofficial and unsupported). You'll need to make the following changes in ComCtrl.pas:


//you'll need to add this function above TCustomRichEdit.Print
function GetPreciseTextLen(ARichEditCtrl : TRichedit): integer;
var
  gtlex : TGetTextLengthEx;
begin
  with gtlex do
  begin
    flags := GTL_PRECISE;
    codepage := CP_ACP;
  end;
  Result := ARichEditCtrl.Perform(EM_GETTEXTLENGTHEX,WPARAM(@gtlex), 0 );
end;
//---------end new function---------

//this is the print function.
procedure TCustomRichEdit.Print(const Caption: string);
var
  Range: TFormatRange;
  LastChar, MaxLen, LogX, LogY, OldMap: Integer;
  SaveRect: TRect;
begin
  FillChar(Range, SizeOf(TFormatRange), 0);
  with Printer, Range do
  begin
    Title := Caption;
    BeginDoc;
    hdc := Handle;
    hdcTarget := hdc;
    LogX := GetDeviceCaps(Handle, LOGPIXELSX);
    LogY := GetDeviceCaps(Handle, LOGPIXELSY);
    if IsRectEmpty(PageRect) then
    begin
      rc.right := PageWidth * 1440 div LogX;
      rc.bottom := PageHeight * 1440 div LogY;
    end
    else begin
      rc.left := PageRect.Left * 1440 div LogX;
      rc.top := PageRect.Top * 1440 div LogY;
      rc.right := PageRect.Right * 1440 div LogX;
      rc.bottom := PageRect.Bottom * 1440 div LogY;
    end;
    rcPage := rc;
    SaveRect := rc;
    LastChar := 0;
    // (commented out )MaxLen := GetTextLen; remove this line
    MaxLen := GetPreciseTextLine; //add this line
    chrg.cpMax := -1;
    // ensure printer DC is in text map mode
    OldMap := SetMapMode(hdc, MM_TEXT);
    SendMessage(Self.Handle, EM_FORMATRANGE, 0, 0);    // flush buffer
    try
      repeat
        rc := SaveRect;
        chrg.cpMin := LastChar;
        LastChar := SendMessage(Self.Handle, EM_FORMATRANGE, 1, Longint(@Range));
        if (LastChar < MaxLen) and (LastChar <> -1) then NewPage;
      until (LastChar >= MaxLen) or (LastChar = -1);
      EndDoc;
    finally
      SendMessage(Self.Handle, EM_FORMATRANGE, 0, 0);  // flush buffer
      SetMapMode(hdc, OldMap);       // restore previous map mode
    end;
  end;
end;


Server Response from: ETNASC04