Delphi 如何从Windows';多行编辑控件?

Delphi 如何从Windows';多行编辑控件?,delphi,winapi,editcontrol,Delphi,Winapi,Editcontrol,我发现标准Windowsedit控件的工作方式有些奇怪 当启用换行时,它接受整个逻辑行,但在EM\u GETLINE请求时返回屏幕行。但是,它在调整窗口大小和重新拆分有关原始CR LFs的文本时行为正确 因此,我找到原始逻辑行的想法是使用EM_GETLINE逐个查询屏幕行,并在块中最后一个屏幕行的末尾检测CR-LF 不幸的是,EM_GETLINE请求的行根本不包含CR LFs 控件似乎在内部存储CR LFs,但没有在EM\u GETLINE上返回它们。只有在使用WM_GETTEXT请求整个控制文

我发现标准Windows
edit
控件的工作方式有些奇怪

当启用换行时,它接受整个逻辑行,但在
EM\u GETLINE
请求时返回屏幕行。但是,它在调整窗口大小和重新拆分有关原始CR LFs的文本时行为正确

因此,我找到原始逻辑行的想法是使用
EM_GETLINE
逐个查询屏幕行,并在块中最后一个屏幕行的末尾检测CR-LF

不幸的是,
EM_GETLINE
请求的行根本不包含CR LFs

控件似乎在内部存储CR LFs,但没有在
EM\u GETLINE
上返回它们。只有在使用
WM_GETTEXT
请求整个控制文本时,才能获得它们

除了获取整个文本并将其拆分之外,是否还有其他方法可以请求相邻CR LF之间的文本片段

program WindowsEditControl;

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils;

const
  IDM_EXIT = 100;
  GAP = 10;

var
  hEdit: HWND;
  hFnt: HFONT;

function GetLine(hEdit: HWND; Index: Integer): string;
var
  Text: array[0..4095] of Char;
begin
  Word((@Text)^) := Length(Text);
  SetString(Result, Text, SendMessage(hEdit, EM_GETLINE, Index, LPARAM(@Text)));
end;

function GetTxt(hEdit: HWND): string;
var
  Len: Integer;
begin
  Len := SendMessage(hEdit, WM_GETTEXTLENGTH, 0, 0);
  SetString(Result, PChar(nil), Len);
  if Len <> 0 then
  begin
    Len := Len - SendMessage(hEdit, WM_GETTEXT, Len + 1, LongWord(PChar(Result)));
    if Len > 0 then
      SetLength(Result, Length(Result) - Len);
  end;
end;

function WndFunc(h: HWND; iMessage: UINT; w: WPARAM; l: LPARAM): LRESULT; stdcall;
var
  nWidth, nHeight: NativeUInt;
  i, j, lineLength: Integer;
  s: string;
begin
  case iMessage of
    WM_CREATE: begin
      hEdit := CreateWindowEx(WS_EX_NOPARENTNOTIFY, 'edit', 'Control #1',
        WS_BORDER or WS_VISIBLE or WS_CHILD or ES_LEFT or ES_MULTILINE or ES_READONLY or WS_VSCROLL, GAP, GAP,
        810 - GAP*2 - 15, 260, h, 0, hInstance, nil);
      hFnt := CreateFont(20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'Calibri');
      SendMessage(h, WM_SETFONT, hFnt, MakeLong(1, 0));
      SendMessage(hEdit, WM_SETFONT, hFnt, MakeLong(1, 0));
      for i := 0 to 10 do begin
        for j := 0 to 20 do s := s + 'Item' + IntToStr(i) + '.' + IntToStr(j) + ' ';
        s := s + #13#10;
      end;
      SendMessage(hEdit, WM_SETTEXT, 0, LongWord(PWideChar(s)));

      //Attempts to query the edit control:
      //This gets the first SCREEN line of the edit control. CR-LFs aren't there.
      MessageBox(h, PWideChar('|'+GetLine(hEdit, 0)+'|'), 'Screen line 1', 0);
      //This gets the second SCREEN line of the edit control. CR-LFs aren't there.
      MessageBox(h, PWideChar('|'+GetLine(hEdit, 1)+'|'), 'Screen line 2', 0);
      //In a whole text are all the CR-LFs there
      MessageBox(h, PWideChar(GetTxt(hEdit)), 'Whole text', 0);
    end;
    WM_DESTROY: begin
      DeleteObject(hFnt);
      PostQuitMessage(0);
      Result := 0;
    end;
    WM_SIZE: begin
      nWidth := LOWORD(l);
      nHeight := HIWORD(l);
      SetWindowPos(hEdit, 0, GAP, GAP, nWidth - GAP*2, nHeight - GAP*2, 0);
      Result := 0;
    end;
    WM_COMMAND: if w = IDM_EXIT then PostMessage(h, WM_CLOSE, 0, 0);
    else
      Result := DefWindowProc(h, iMessage, w, l);
  end;
end;

var
  wndClass: TWndClass;
  h: HWND;
  msg: TMsg;

begin
  wndClass.style          := CS_HREDRAW or CS_VREDRAW;
  wndClass.lpfnWndProc    := @WndFunc;
  wndClass.cbClsExtra     := 0;
  wndClass.cbWndExtra     := 0;
  wndClass.hInstance      := hInstance;
  wndClass.hIcon          := 0;
  wndClass.hCursor        := LoadCursor(0, IDC_ARROW);
  wndClass.hbrBackground  := GetStockObject(WHITE_BRUSH);
  wndClass.lpszMenuName   := nil;
  wndClass.lpszClassName  := 'EditTest';

  if RegisterClass(wndClass) = 0 then Halt(0);

  h := CreateWindow(wndClass.lpszClassName, 'Edit Test', WS_OVERLAPPEDWINDOW, 35, 35, 810, 320, 0, 0, hInstance, nil);
  ShowWindow(h, SW_SHOW);

  while GetMessage(msg, 0, 0, 0) do begin
    TranslateMessage(msg);
    DispatchMessage(msg);
  end;
  Halt(msg.wParam);
end.
程序窗口编辑控件;
使用
Windows、Winapi.Messages、System.SysUtils;
常数
IDM_出口=100;
间隙=10;
变量
hEdit:HWND;
hFnt:HFONT;
函数GetLine(hEdit:HWND;Index:Integer):字符串;
变量
Text:字符的数组[0..4095];
开始
字((@Text)^):=长度(Text);
SetString(结果、文本、发送消息(hEdit、EM_GETLINE、索引、LPARAM(@Text));
结束;
函数getText(hEdit:HWND):字符串;
变量
Len:整数;
开始
Len:=SendMessage(hEdit,WM_GETTEXTLENGTH,0,0);
设置字符串(结果,PChar(nil),Len);
如果Len为0,则
开始
Len:=Len-SendMessage(hEdit,WM_GETTEXT,Len+1,LongWord(PChar(Result));
如果Len>0,则
设置长度(结果),长度(结果)-Len;
结束;
结束;
函数WndFunc(h:HWND;i消息:UINT;w:WPARAM;l:LPARAM):LRESULT;stdcall;
变量
第十四届,第八届:NativeUInt;
i、 j,线宽:整数;
s:字符串;
开始
案例信息
创建:开始
hEdit:=CreateWindowEx(WS_EX_NOPARENTNOTIFY,'edit','Control#1',
WS_BORDER或WS_VISIBLE或WS_CHILD或ES_LEFT或ES_MULTILINE或ES_READONLY或WS_VSCROLL、GAP、GAP、,
810-间隙*2-15260,h,0,hInstance,无);
hFnt:=CreateFont(20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'Calibri');
SendMessage(h,WM_SETFONT,hFnt,MakeLong(1,0));
SendMessage(hEdit、WM_SETFONT、hFnt、MakeLong(1,0));
对于i:=0到10,开始
对于j:=0到20 do s:=s+'Item'+IntToStr(i)+'.+IntToStr(j)+'';
s:=s+#13#10;
结束;
SendMessage(hEdit,WM_SETTEXT,0,LongWord(PWideChar));
//尝试查询编辑控件:
//这将获取编辑控件的第一个屏幕行。CR LF不在那里。
消息框(h,PWideChar(“|”+GetLine(hEdit,0)+“|”),“屏幕行1',0);
//这将获取编辑控件的第二个屏幕行。CR LF不在那里。
消息框(h,PWideChar(“|”+GetLine(hEdit,1)+“|”),“屏幕行2',0);
//在整个文本中,所有CR LF都在那里吗
MessageBox(h,PWideChar(getText(hEdit)),'wholetext',0);
结束;
WM_销毁:开始
删除对象(hFnt);
PostQuitMessage(0);
结果:=0;
结束;
WM_大小:开始
nWidth:=LOWORD(l);
nHeight:=HIWORD(l);
设置窗口位置(hEdit,0,间隙,间隙,nWidth-间隙*2,nHeight-间隙*2,0);
结果:=0;
结束;
WM_命令:如果w=IDM_EXIT,则为PostMessage(h,WM_CLOSE,0,0);
其他的
结果:=DefWindowProc(h,iMessage,w,l);
结束;
结束;
变量
wndClass:TWndClass;
h:HWND;
msg:TMsg;
开始
wndClass.style:=CS_HREDRAW或CS_VREDRAW;
wndClass.lpfnWndProc:=@WndFunc;
wndClass.cbClsExtra:=0;
wndClass.cbWndExtra:=0;
wndClass.hInstance:=hInstance;
wndClass.hIcon:=0;
wndClass.hCursor:=LoadCursor(0,IDC_箭头);
wndClass.hbrBackground:=GetStockObject(白色画笔);
wndClass.lpszMenuName:=nil;
wndClass.lpszClassName:=“EditTest”;
如果RegisterClass(wndClass)=0,则停止(0);
h:=CreateWindow(wndClass.lpszClassName,“编辑测试”,WS_重叠窗口,35,35,810,320,0,0,hInstance,nil);
展示窗口(h、SW_展示);
当GetMessage(消息,0,0,0)开始时
翻译信息;
发送消息(msg);
结束;
暂停(msg.wParam);
结束。

请注意,GetText中可能存在一个大小为一个字符的缓冲区溢出。@Sertac Akyuz:那么这就是Delphi VCL错误。我从TControl复制了代码。API复制了空终止符,但报告了非空字符计数,这是一个bug。我不想复制它。@Paul
EM_GETLINE
是从编辑控件中检索单个行的唯一方法,否则您必须使用
WM_GETTEXT
获取整个文本,然后自己解析出这些行。在换行过程中,默认情况下,编辑控件只需在文本中插入自己的“软”分隔符,并根据需要将其删除
EM_GETLINE
EM_GETLINECOUNT
像对待任何其他换行符一样对待这些“软”换行符。有关详细信息,请参阅MSDN上的。@SertacAkyuz我看不到缓冲区溢出。
字符串
用于缓冲区,而
字符串
始终具有空终止符,即使它不按字符串的
长度计算
SetLength()
将为
length+1
字符分配空间。“API复制空终止符,但报告非空字符计数,这是一个bug”-不,不是。大多数处理以null结尾的字符串的API都会这样做。这是完全正常的。