title: Using Tables in RichEdit in Delphi
body: <p>I am attempting to use Tables in a TRichEdit control in Delphi XE2 Starter Edition.  (In other words, I don’t have the source code for XE2 – but I do have it for TurboDelphi).  I understand that the default RichEdit control does not use a version of MS RichEdit that supports tables, so I have subclassed it to use MS RichEdit v4.1 as described here <a href="http://fgaillard.com/2010/09/using-richedit-4-1-with-d2010" rel="nofollow noreferrer" title="http://fgaillard.com/2010/09/using-richedit-4-1-with-d2010">1</a> and here <a href="https://stackoverflow.com/questions/10498332/delphi-7-trichtextedit-text-in-a-box-not-displaying-correctly/10499850#10499850" title="https://stackoverflow.com/questions/10498332/delphi-7-trichtextedit-text-in-a-box-not-displaying-correctly/10499850#10499850">6</a>, and also modeled off the code in the JEDI TjvRichEdit.  (for the sake of brevity, I have not included the code segment that determines RichEdit version numbers for DLL's other than v4.1, that I borrowed from JEDI. A simplified version is shown here.)</p>
  
  <p>An MSDN blog <a href="http://blogs.msdn.com/b/murrays/archive/2008/09/15/richedit-s-nested-table-facility.aspx" rel="nofollow noreferrer" title="http://blogs.msdn.com/b/murrays/archive/2008/09/15/richedit-s-nested-table-facility.aspx">2</a> states that Windows messages to support RTF tables were an undocumented feature of MS RichEdit version 4.1, and that the EM_INSERTTABLE message has been available since Windows XP SP2.  For more information on which versions were available when, see here <a href="http://blogs.msdn.com/b/murrays/archive/2006/10/14/richedit-versions.aspx" rel="nofollow noreferrer">3</a>.</p>
  
  <p>A comment following this blog <a href="http://blogs.msdn.com/b/murrays/archive/2008/09/15/richedit-s-nested-table-facility.aspx" rel="nofollow noreferrer" title="http://blogs.msdn.com/b/murrays/archive/2008/09/15/richedit-s-nested-table-facility.aspx">2</a>, posted by David Kinder on 26 Sep 2008, states he was able to get the EM_INSERTTABLE message to work with RichEdit v4.1, using the same code which I have shown below (except he wasn’t using Delphi).</p>
  
  <p>For details on the EM_INSERTTABLE message and the structures that support it, see the MSDN docs <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/hh768373%28v=vs.85%29.aspx" rel="nofollow noreferrer">4</a> (which state they were introduced with Windows 8, but which clearly pre-dated that by at least two major OS versions).  Also note that the definition(s) of the structures have changed somewhat since Murray wrote his blog <a href="http://blogs.msdn.com/b/murrays/archive/2008/09/15/richedit-s-nested-table-facility.aspx" rel="nofollow noreferrer" title="http://blogs.msdn.com/b/murrays/archive/2008/09/15/richedit-s-nested-table-facility.aspx">2</a> in 2008.  I have searched to the ends of the internet and cannot find a MS richedit.h version that goes with RichEdit 4.1 and includes the “undocumented” structures TABLEROWPARMS and TABLECELLPARMS as they existed at that time, so I am limited to the MSDN docs as the exist for Win8 <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/hh768373%28v=vs.85%29.aspx" rel="nofollow noreferrer">4</a> and Murray’s blog <a href="http://blogs.msdn.com/b/murrays/archive/2008/09/15/richedit-s-nested-table-facility.aspx" rel="nofollow noreferrer" title="http://blogs.msdn.com/b/murrays/archive/2008/09/15/richedit-s-nested-table-facility.aspx">2</a> as they allegedly existed in Win XP and Win7.</p>
  
  <p>Here is my custom RichEdit:</p>
  
  <pre><code>unit MyRichEdit;
  //Customized RichEdit to use MS RichEdit v4.1
  // Some stuff borrowed from Michael Lam's REdit (ca. 1998), found on the Torry page.
  
  interface
  
  uses
    Windows, Messages, SysUtils, Classes, Graphics, Controls,
    StdCtrls, ComCtrls, Printers, RichEdit;
  
  type
  
    {TableRowParms}
    PTableRowParms = ^TTableRowParms;
  
    _tableRowParms = packed record
       cbRow         : BYTE ;     // Count of bytes in this structure
       cbCell        : BYTE ;     // Count of bytes in TABLECELLPARMS
       cCell         : BYTE ;     // Count of cells
       cRow          : BYTE ;     // Count of rows
       dxCellMargin  : LONG ;     // Cell left/right margin (\trgaph)
       dxIndent      : LONG ;     // Row left (right if fRTL indent (similar to \trleft)
       dyHeight      : LONG ;     // Row height (\trrh)
       nAlignment{:3}: DWORD;    // Row alignment (like PARAFORMAT::bAlignment, \trql, trqr, \trqc)
       fRTL{:1}      : DWORD;    // Display cells in RTL order (\rtlrow)
       fKeep{:1}     : DWORD;    // Keep row together (\trkeep}
       fKeepFollow{:1} : DWORD;    // Keep row on same page as following row (\trkeepfollow)
       fWrap{:1}       : DWORD;    // Wrap text to right/left (depending on bAlignment) (see \tdfrmtxtLeftN, \tdfrmtxtRightN)
       fIdentCells{:1} : DWORD;   // lparam points at single struct valid for all cells
       //cpStartRow    : LONG ;   // not in Murray's blog version, so commented here...
       //bTableLevel   : BYTE;    // not in Murray's blog version
       //iCell         : BYTE;    // not in Murray's blog version
    end;
  
    TABLEROWPARMS = _tableRowParms;
    TTableRowParms = TABLEROWPARMS;
  
  
    {TableCellParms}
    PTableCellParms = ^TTableCellParms;
  
    _tableCellParms = packed record
       dxWidth        : LONG    ;      // Cell width (\cellx)
       nVertAlign{:2}   : WORD    ;      // Vertical alignment (0/1/2 = top/center/bottom  \clvertalt (def), \clvertalc, \clvertalb)
       fMergeTop{:1}    : WORD    ;      // Top cell for vertical merge (\clvmgf)
       fMergePrev{:1}   : WORD    ;      // Merge with cell above (\clvmrg)
       fVertical{:1}    : WORD    ;      // Display text top to bottom, right to left (\cltxtbrlv)
       wShading       : WORD    ;      // Shading in .01% (\clshdng) e.g., 10000 flips fore/back
       dxBrdrLeft     : SHORT   ;      // Left border width (\clbrdrl\brdrwN) (in twips)
       dyBrdrTop      : SHORT   ;      // Top border width (\clbrdrt\brdrwN)
       dxBrdrRight    : SHORT   ;      // Right border width (\clbrdrr\brdrwN)
       dyBrdrBottom   : SHORT   ;      // Bottom border width (\clbrdrb\brdrwN)
       crBrdrLeft     : COLORREF;      // Left border color (\clbrdrl\brdrcf)
       crBrdrTop      : COLORREF;      // Top border color (\clbrdrt\brdrcf)
       crBrdrRight    : COLORREF;      // Right border color (\clbrdrr\brdrcf)
       crBrdrBottom   : COLORREF;      // Bottom border color (\clbrdrb\brdrcf)
       crBackPat      : COLORREF;      // Background color (\clcbpat)
       crForePat      : COLORREF;      // Foreground color (\clcfpat)
    end;
  
    TABLECELLPARMS = _tableCellParms;
    TTableCellParms = TABLECELLPARMS;
  
    TMyRichEdit = class(ComCtrls.TRichEdit)
    private
      function GetRTF: string;              // get the RTF string
      procedure SetRTF(InRTF: string);      // set the RTF string
    protected
      procedure CreateParams(var Params: TCreateParams); override;
    published
      property RTFText: string read GetRTF write SetRTF;
    end;
  
  //--------------------------------------------------------------
  // GLOBAL VARIABLES
  //--------------------------------------------------------------
  var
    RichEditVersion    : Integer;         //Version of the MS Windows RichEdit DLL
  
  const
    RichEdit10ModuleName = 'RICHED32.DLL';
    RichEdit20ModuleName = 'RICHED20.DLL';
    RichEdit41ModuleName = 'MSFTEDIT.DLL';
    MSFTEDIT_CLASS = 'RichEdit50W'; //goes with RichEdit 4.1 (beginning with Win XP SP2)
  
    EM_INSERTTABLE = WM_USER + 232;
  
  
  implementation
  
  
  function TMyRichEdit.GetRTF: string;
  var FStream : TStringStream;
  begin
      // get the RTF string
      FStream := TStringStream.Create;    // RTF stream
      FStream.Clear;
      FStream.Position := 0;
      Lines.SaveToStream(FStream);
      Result := FStream.DataString;
      FStream.Free;                   // free the RTF stream
  end; //ok
  
  procedure TMyRichEdit.SetRTF(InRTF: string);
  var FStream : TStringStream;
  begin
      // set the RTF string
      // LoadFromStream uses an EM_STREAMIN windows msg, which by default REPLACES the contents of a RichEdit.
      FStream := TStringStream.Create;    // RTF stream
      FStream.Clear;
      FStream.Position := 0;
      FStream.WriteString(InRTF);
      FStream.Position := 0;
      Lines.LoadFromStream(FStream);
      Self.Modified := false;
      FStream.Free;                   // free the RTF stream
  end; //ok
  
  
  //===========================================================================
  //Defaults: RICHEDIT_CLASS = 'RichEdit20W'; RICHEDIT_CLASS10A = 'RICHEDIT';
  //It needs to use RichEdit50W for version 4.1, which I defined in a constant above as MSFTEDIT_CLASS.
  
  procedure TMyRichEdit.CreateParams(var Params: TCreateParams);
  begin
    inherited CreateParams(Params);
  
    If RichEditVersion = 1 then CreateSubClass(Params, RICHEDIT_CLASS10A)
    else If RichEditVersion = 4 then CreateSubClass(Params, MSFTEDIT_CLASS)
    else CreateSubClass(Params, RICHEDIT_CLASS);
  end;
  
  
  //================================================================
  {Initialization Stuff}
  //================================================================
  var
    GLibHandle: THandle = 0;
  
  procedure InitRichEditDll;
  begin
    //Try to load MS RichEdit v 4.1 into memory...
    RichEditVersion := 4;
    GLibHandle := SafeLoadLibrary(RichEdit41ModuleName);
    if (GLibHandle &gt; 0) and (GLibHandle &lt; HINSTANCE_ERROR) then
       GLibHandle := 0; //this means it could not find the DLL or it didn't load right.
    if GLibHandle = 0 then begin
       RichEditVersion := 2;
       GLibHandle := SafeLoadLibrary(RichEdit20ModuleName);
       if (GLibHandle &gt; 0) and (GLibHandle &lt; HINSTANCE_ERROR) then
          GLibHandle := 0;
       if GLibHandle = 0 then begin
          RichEditVersion := 1;
          GLibHandle := SafeLoadLibrary(RichEdit10ModuleName);
          if (GLibHandle &gt; 0) and (GLibHandle &lt; HINSTANCE_ERROR) then begin
             RichEditVersion := 0;
             GLibHandle := 0;
          end;
       end;
    end;
  end;
  
  procedure FinalRichEditDll;
  begin
    if GLibHandle &gt; 0 then
    begin
      FreeLibrary(GLibHandle);
      GLibHandle := 0;
    end;
  end;
  
  
  initialization
    InitRichEditDll;
  
  finalization
    FinalRichEditDll;
  
  
  End.
  </code></pre>
  
  <p>Usage:</p>
  
  <pre><code>Uses … MyRichEdit …
  
  type
  
    TRichEdit = class(TMyRichEdit);
  
    TfrmEdit = class(TForm)
  …
      memNotes: TRichEdit;
  …
  
  end;
  
  
  procedure TfrmEdit.actTableAddExecute(Sender: TObject);
  var
     rows: TABLEROWPARMS;
     cells: TABLECELLPARMS;
     rc : LRESULT; 
  begin
     //Insert a table into the RTF.
     ZeroMemory(@rows,sizeof(rows));
     rows.cbRow := sizeof(TABLEROWPARMS);
     rows.cbCell := sizeof(TABLECELLPARMS);
     rows.cCell := 3;
     rows.cRow := 2;
     rows.dxCellMargin := 5; //50
     rows.nAlignment := 1;
     rows.dyHeight := 100;  //400
     rows.fIdentCells := 1;
     rows.fRTL := 0;
     rows.fKeep := 1;
     rows.fKeepFollow := 1;
     rows.fWrap := 1;
     //rows.cpStartRow := -1;
  
     ZeroMemory(@cells,sizeof(cells));
     cells.dxWidth := 600; //1000
     cells.dxBrdrLeft := 1;
     cells.dyBrdrTop := 1;
     cells.dxBrdrRight := 1;
     cells.dyBrdrBottom := 1;
     cells.crBackPat := RGB(255,255,255);
     cells.crForePat := RGB(0,0,0);
     cells.nVertAlign := 0;
     //cells.fMergeTop := 1;
     //cells.fMergePrev := 1;
     cells.fVertical := 1;
  
     rc := SendMessage(memNotes.Handle,EM_INSERTTABLE, WPARAM(@rows),LPARAM(@cells));
     //rc := memNotes.Perform(EM_INSERTTABLE, WPARAM(@rows),LPARAM(@cells));
  
  end;
  </code></pre>
  
  <p>Upon execution, rc contains -2147024809 (<code>E_INVALIDARG</code>).  I don’t have a good understanding of why this fails, or what the problem is with the message arguments.  As a disclaimer, I am new to working with RichEdit in Delphi, but I tried to learn as much as possible before posting for help.</p>
  
  <p>In my extensive searching, I found this website <a href="http://flocke.vssd.de/prog/code/pascal/rtflabel/" rel="nofollow noreferrer">5</a> which may help narrow the problem.  This site hosts a utility called RTFLabel.  Download the zip file and have a look at richedit2.pas, where they explain that “The definition of CHARFORMAT2A and CHARFORMAT2W in richedit.h (2005 SDK) has an error in the C part”, and that they needed to insert a new dummy field to “fix” the byte alignment of the structure in order to make it work correctly with Delphi.  I have a feeling there may be similar alignment issues with one or both of the TABLEROWPARMS and TABLECELLPARMS structures which is causing this error. </p>
  
  <p>I would like some help to figure out why the <code>SendMessage</code> is returning a E_INVALIDARG, and what I can do to fix it.  Any assistance would be greatly appreciated!<br>
  -Jeff Aylor</p>
  
  <p>Referenced websites: <br>
  <a href="http://fgaillard.com/2010/09/using-richedit-4-1-with-d2010" rel="nofollow noreferrer" title="http://fgaillard.com/2010/09/using-richedit-4-1-with-d2010">1</a> [<a href="http://fgaillard.com/2010/09/using-richedit-4-1-with-d2010/]" rel="nofollow noreferrer">http://fgaillard.com/2010/09/using-richedit-4-1-with-d2010/]</a><a href="http://fgaillard.com/2010/09/using-richedit-4-1-with-d2010" rel="nofollow noreferrer" title="http://fgaillard.com/2010/09/using-richedit-4-1-with-d2010">1</a><br>
  <a href="http://blogs.msdn.com/b/murrays/archive/2008/09/15/richedit-s-nested-table-facility.aspx" rel="nofollow noreferrer" title="http://blogs.msdn.com/b/murrays/archive/2008/09/15/richedit-s-nested-table-facility.aspx">2</a> [<a href="http://blogs.msdn.com/b/murrays/archive/2008/09/15/richedit-s-nested-table-facility.aspx]" rel="nofollow noreferrer">http://blogs.msdn.com/b/murrays/archive/2008/09/15/richedit-s-nested-table-facility.aspx]</a><a href="http://blogs.msdn.com/b/murrays/archive/2008/09/15/richedit-s-nested-table-facility.aspx" rel="nofollow noreferrer" title="http://blogs.msdn.com/b/murrays/archive/2008/09/15/richedit-s-nested-table-facility.aspx">2</a><br>
  <a href="http://blogs.msdn.com/b/murrays/archive/2006/10/14/richedit-versions.aspx" rel="nofollow noreferrer">3</a> [<a href="http://blogs.msdn.com/b/murrays/archive/2006/10/14/richedit-versions.aspx]" rel="nofollow noreferrer">http://blogs.msdn.com/b/murrays/archive/2006/10/14/richedit-versions.aspx]</a><a href="http://blogs.msdn.com/b/murrays/archive/2006/10/14/richedit-versions.aspx" rel="nofollow noreferrer">3</a><br>
  <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/hh768373%28v=vs.85%29.aspx" rel="nofollow noreferrer">4</a> [<a href="http://msdn.microsoft.com/en-us/library/windows/desktop/hh768373%28v=vs.85%29.aspx]" rel="nofollow noreferrer">http://msdn.microsoft.com/en-us/library/windows/desktop/hh768373%28v=vs.85%29.aspx]</a><a href="http://msdn.microsoft.com/en-us/library/windows/desktop/hh768373%28v=vs.85%29.aspx" rel="nofollow noreferrer">4</a><br>
  <a href="http://flocke.vssd.de/prog/code/pascal/rtflabel/" rel="nofollow noreferrer">5</a> [<a href="http://flocke.vssd.de/prog/code/pascal/rtflabel/]" rel="nofollow noreferrer">http://flocke.vssd.de/prog/code/pascal/rtflabel/]</a><a href="http://flocke.vssd.de/prog/code/pascal/rtflabel/" rel="nofollow noreferrer">5</a><br>
  <a href="https://stackoverflow.com/questions/10498332/delphi-7-trichtextedit-text-in-a-box-not-displaying-correctly/10499850#10499850" title="https://stackoverflow.com/questions/10498332/delphi-7-trichtextedit-text-in-a-box-not-displaying-correctly/10499850#10499850">6</a> [<a href="https://stackoverflow.com/questions/10498332/delphi-7-trichtextedit-text-in-a-box-not-displaying-correctly/10499850#10499850]">Delphi 7 TRichTextEdit Text in a box not displaying correctly</a><a href="https://stackoverflow.com/questions/10498332/delphi-7-trichtextedit-text-in-a-box-not-displaying-correctly/10499850#10499850" title="https://stackoverflow.com/questions/10498332/delphi-7-trichtextedit-text-in-a-box-not-displaying-correctly/10499850#10499850">6</a><br></p>