我试图在Delphi XE2 Starter Edition中的TRichEdit控件中使用Tables.(换句话说,我没有XE2的源代码 - 但我确实有TurboDelphi的源代码).我知道默认的RichEdit控件不使用支持表的MS RichEdit版本,因此我将其子类化为使用MS RichEdit v4.1,如此处1和此处所述6,并且还模拟了JEDI TjvRichEdit中的代码.(为简便起见,我没有包括代码段,用于确定DLL的其他比4.1版的RichEdit版本号,即我从JEDI借来的.的简化版本在此示出).
MSDN博客2指出,支持RTF表的Windows消息是MS RichEdit 4.1版的未记录功能,并且自Windows XP SP2起EM_INSERTTABLE消息可用.有关可用版本的更多信息,请参见此处3.
注释下面这个博客2,发布者:大卫金德于2008年09月26日,国家他能得到EM_INSERTTABLE消息的RichEdit 4.1版配合使用,这是我下面所示相同的代码(除了他没有用Delphi ).
有关EM_INSERTTABLE消息的详细信息,并支持它的结构,请参阅MSDN文档4(哪个国家,他们与Windows 8进行了介绍,但它清楚地预先日,通过至少两个主要的OS版本).还要注意的是结构的定义(一个或多个)已经有所改变,因为穆雷说他的博客2在2008年我已搜索到互联网的两端,并不能找到一个MS richedit.h版本与RichEdit中4.1去,并包含"没有文档的"结构TABLEROWPARMS和TABLECELLPARMS,因为它们当时存在,因此我仅限于MSDN文档,因为它们存在于Win8 4和Murray的博客2中,因为它们据称存在于Win XP和Win7中.
这是我的自定义RichEdit:
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 > 0) and (GLibHandle < 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 > 0) and (GLibHandle < HINSTANCE_ERROR) then
GLibHandle := 0;
if GLibHandle = 0 then begin
RichEditVersion := 1;
GLibHandle := SafeLoadLibrary(RichEdit10ModuleName);
if (GLibHandle > 0) and (GLibHandle < HINSTANCE_ERROR) then begin
RichEditVersion := 0;
GLibHandle := 0;
end;
end;
end;
end;
procedure FinalRichEditDll;
begin
if GLibHandle > 0 then
begin
FreeLibrary(GLibHandle);
GLibHandle := 0;
end;
end;
initialization
InitRichEditDll;
finalization
FinalRichEditDll;
End.
Run Code Online (Sandbox Code Playgroud)
用法:
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;
Run Code Online (Sandbox Code Playgroud)
执行时,rc包含-2147024809(E_INVALIDARG).我不太清楚为什么会失败,或者消息参数的问题是什么.作为免责声明,我是Delphi中使用RichEdit的新手,但我在发布求助之前尽可能多地学习.
在我的广泛搜索中,我发现这个网站5可能有助于缩小问题范围.该站点托管一个名为RTFLabel的实用程序.下载zip文件并查看richedit2.pas,其中他们解释说"richedit.h中的CHARFORMAT2A和CHARFORMAT2W的定义(2005 SDK)在C部分中有错误",并且他们需要插入一个新的假人字段"修复"结构的字节对齐,以使其与Delphi一起正常工作.我感觉TABLEROWPARMS和TABLECELLPARMS结构中的一个或两个可能存在类似的对齐问题,导致此错误.
我想帮助弄清楚为什么SendMessage返回E_INVALIDARG,以及我可以做些什么来修复它.任何帮助将不胜感激!
-Jeff Aylor
引用的网站:
1 [ http://fgaillard.com/2010/09/using-richedit-4-1-with-d2010/] 1
2 [ http://blogs.msdn.com/b/murrays/archive/2008 /09/15/richedit-s-nested-table-facility.aspx] 2
3 [ http://blogs.msdn.com/b/murrays/archive/2006/10/14/richedit-versions.aspx] 3
4 [ http://msdn.microsoft.com/en-us/library/windows/desktop/hh768373%28v=vs.85%29.aspx] 4
5 [ http://flocke.vssd.de/prog/code/ pascal/rtflabel /] 5
6 [ Delphi 7 TRichTextEdit文本框未正确显示6
长话短说:博士:
不要将其用于广告/记录之外的任何其他用途(即低于 W8),即使那样我也会保持警惕。一团糟。
请注意,除了 的附加三个字段之外,您从中获取记录的博客文章和文档之间还存在其他差异TABLEROWPARMS。这是在TABLECELLPARMS. 这是并排比较,左边是博客文章的记录,右边是文档的记录。
typedef struct _tableCellParms { typedef struct _tableCellParms {
LONG dxWidth; LONG dxWidth;
WORD nVertAlign:2; WORD nVertAlign:2;
WORD fMergeTop:1; WORD fMergeTop:1;
WORD fMergePrev:1; WORD fMergePrev:1;
WORD fVertical:1; WORD fVertical:1;
WORD fMergeStart:1;
WORD fMergeCont:1;
WORD wShading; WORD wShading;
SHORT dxBrdrLeft; SHORT dxBrdrLeft;
SHORT dyBrdrTop; SHORT dyBrdrTop;
SHORT dxBrdrRight; SHORT dxBrdrRight;
SHORT dyBrdrBottom; SHORT dyBrdrBottom;
COLORREF crBrdrLeft; COLORREF crBrdrLeft;
COLORREF crBrdrTop; COLORREF crBrdrTop;
COLORREF crBrdrRight; COLORREF crBrdrRight;
COLORREF crBrdrBottom; COLORREF crBrdrBottom;
COLORREF crBackPat; COLORREF crBackPat;
COLORREF crForePat; COLORREF crForePat;
} TABLECELLPARMS; } TABLECELLPARMS;
Run Code Online (Sandbox Code Playgroud)
在这里,您直观地看到记录中间插入了两个附加字段。或者,其中之一是错误的,也许两者都是错误的......但我们从博客文章中知道,有人能够使其与左边的一起工作。也许有针对特定 dll 的特定版本。
不管怎样,考虑到原因很可能是E_INVALIDARG结构的大小,因为即使进行最小的测试也无法工作,这导致我使用强力测试来确定正确的记录大小。
var
rows: TABLEROWPARMS;
cells: TABLECELLPARMS;
begin
ZeroMemory(@rows,sizeof(rows));
rows.cbRow := 1;
rows.cbCell := 1;
rows.cCell := 1;
rows.cRow := 1;
ZeroMemory(@cells,sizeof(cells));
while SendMessage(RichEdit1.Handle, EM_INSERTTABLE,
WPARAM(@rows), LPARAM(@cells)) <> S_OK do begin
if rows.cbCell < 120 then // arbitrary upper limit
Inc(rows.cbCell)
else begin
Inc(rows.cbRow);
rows.cbCell := 1;
end;
if rows.cbRow = 120 then
raise Exception.Create('no match');
end;
end;
Run Code Online (Sandbox Code Playgroud)
将断点置于过程末尾,rows.cbRow出现“28”并rows.cbCell出现“40”。它们比两个参考文献都要小得多。我还测试了是否有可能比第一个命中更大的匹配,但没有。我的测试是针对“msftedit.dll”版本 5.41.21.2510,驻留在 W7 盒子的“\syswow64”中,使用 XE2。
那么我们应该从哪里开始切割结构呢?正如我们从上面的参考文献中看到的那样,可能不是。我没有看到任何从这里前进的合理方法,但我会发布我的最佳尝试,以防您有类似的环境并想要继续(我不推荐 - 请注意,我必须将字段的顺序更改为走到这一步了)。这绝对是不正确的,因为它无法插入具有多个列的表。
_tableRowParms = packed record
cbRow : BYTE ;
cbCell : BYTE ;
cCell : BYTE ;
cRow : BYTE ;
dxCellMarginOrVertAlign : LONG ; // when there's more than one cell
dxIndent : LONG ;
dyHeight : LONG ;
nAlignment : DWORD;
fRTL : DWORD;
fKeep : DWORD;
end;
_tableCellParms = packed record
dxWidth : LONG ;
nVertAlign : WORD ;
fVertical : WORD ;
dxBrdrLeft : SHORT ;
dyBrdrTop : SHORT ;
dxBrdrRight : SHORT ;
dyBrdrBottom : SHORT ;
crBrdrLeft : COLORREF;
crBrdrTop : COLORREF;
crBrdrRight : COLORREF;
crBrdrBottom : COLORREF;
crBackPat : COLORREF;
crForePat : COLORREF;
end;
..
var
rows: TABLEROWPARMS;
cells: TABLECELLPARMS;
rc : LRESULT;
begin
ZeroMemory(@rows,sizeof(rows));
rows.cbRow := sizeof(TABLEROWPARMS);
rows.cbCell := sizeof(TABLECELLPARMS);
rows.cCell := 1; // ??
rows.cRow := 3;
rows.dxCellMarginOrVertAlign := 120;
rows.dxIndent := 200;
rows.dyHeight := 400;
rows.nAlignment := 1; // don't leave at 0
rows.fRTL := 0; // ???
rows.fKeep := 0; // ???
ZeroMemory(@cells,sizeof(cells));
cells.dxWidth := 1000;
cells.nVertAlign := 1;
cells.fVertical := 1;
cells.dxBrdrLeft := 50;
cells.dyBrdrTop := 10;
cells.dxBrdrRight := 50;
cells.dyBrdrBottom := 20;
cells.crBrdrLeft := RGB(255,0,0);
cells.crBrdrTop := RGB(0, 255, 0);
cells.crBrdrRight := RGB(0, 0, 255);
cells.crBrdrBottom := RGB(255, 255, 0);
cells.crBackPat := RGB(255, 255, 255);
cells.crForePat := RGB(128, 64, 64); // ?
rc := SendMessage(RichEdit1.Handle,EM_INSERTTABLE, WPARAM(@rows),LPARAM(@cells));
end;
Run Code Online (Sandbox Code Playgroud)