当前位置:  开发笔记 > 编程语言 > 正文

使用SendMessage从scintilla控件中检索文本

如何解决《使用SendMessage从scintilla控件中检索文本》经验,为你挑选了1个好方法。

我正在尝试使用C#中的SendMessage在Notepad ++中检索文档文本.以下是我目前的代码.第一次调用SendMessage会正确返回文本的长度.对SendMessage的第二次调用不会将文本插入StringBuilder变量文本中.为什么不?

    [DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Auto)]
    static extern int SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);

    [DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Auto)]
    static extern int SendMessage(IntPtr hWnd, int msg, int wParam, StringBuilder lParam);


    var length = SendMessage(hWnd, 2183, 0,0);
    var text = new StringBuilder(length +1);
    SendMessage(hWnd, 2182, length + 1, text);

mghie.. 6

问题是您向lParam中具有StringBuilder缓冲区地址的Scintilla控件发送消息,但Notepad ++中的Scintilla控件位于不同的地址空间中,因此它接收的窗口消息中的地址无法写入至.WM_GETTEXTWM_SETTEXT等标准消息的处理方式是为您执行必要的地址映射,但Scintilla控件使用的特殊消息不会发生这种情况.有关查找编组的更多信息.

不幸的是,对WM_GETTEXTLENGTHWM_GETTEXT的支持正逐步退出Scintilla控件,文档建议使用特殊的SCI_XXX消息.Notepad ++已经无法使用WM_GETTEXT,因此您需要使用SCI_GETTEXTLENGTH(2183)和SCI_GETTEXT(2182),并自行进行编组.

警告:在没有特殊处理缓冲区地址的情况下从其他应用程序发送SCI_GETTEXT消息实际上很危险 - Notepad ++会将数据复制到缓冲区,但由于该地址在其自己的地址空间中无效,因此可能立即导致访问冲突,或者(更糟糕的是)它可以无声地覆盖内部数据.


您可以使用VirtualAllocEx()和ReadProcessMemory()来使用具有Notepad ++可用地址的缓冲区.我已经整理了一个适合我的快速Delphi程序,重要的代码如下:

procedure TForm1.Button1Click(Sender: TObject);
const
  VMFLAGS = PROCESS_VM_OPERATION or PROCESS_VM_READ or PROCESS_VM_WRITE;
var
  Wnd: HWND;
  Len: integer;
  ProcessId, BytesRead: Cardinal;
  ProcessHandle: THandle;
  MemPtr: PChar;
  s: string;
begin
  Wnd := $30488;
  Len := SendMessage(Wnd, 2183, 0, 0);
  if Len > 0 then begin
    GetWindowThreadProcessId(Wnd, @ProcessId);
    ProcessHandle := OpenProcess(VMFLAGS, FALSE, ProcessId);
    MemPtr := VirtualAllocEx(ProcessHandle, nil, Len + 1,
      MEM_RESERVE or MEM_COMMIT, PAGE_READWRITE);
    if MemPtr <> nil then try
      SendMessage(Wnd, 2182, Len + 1, integer(MemPtr));
      SetLength(s, Len + 1);
      ReadProcessMemory(ProcessHandle, MemPtr, @s[1], Len + 1, BytesRead);
      SetLength(s, BytesRead);
      Memo1.Lines.Text := s;
    finally
      VirtualFreeEx(ProcessId, MemPtr, Len + 1, MEM_RELEASE);
    end;
  end;
end;

这是使用Ansi版API函数的早期Delphi版本,您可能会使用SendMessageW和WideChar缓冲区,但一般的想法应该是清楚的.



1> mghie..:

问题是您向lParam中具有StringBuilder缓冲区地址的Scintilla控件发送消息,但Notepad ++中的Scintilla控件位于不同的地址空间中,因此它接收的窗口消息中的地址无法写入至.WM_GETTEXTWM_SETTEXT等标准消息的处理方式是为您执行必要的地址映射,但Scintilla控件使用的特殊消息不会发生这种情况.有关查找编组的更多信息.

不幸的是,对WM_GETTEXTLENGTHWM_GETTEXT的支持正逐步退出Scintilla控件,文档建议使用特殊的SCI_XXX消息.Notepad ++已经无法使用WM_GETTEXT,因此您需要使用SCI_GETTEXTLENGTH(2183)和SCI_GETTEXT(2182),并自行进行编组.

警告:在没有特殊处理缓冲区地址的情况下从其他应用程序发送SCI_GETTEXT消息实际上很危险 - Notepad ++会将数据复制到缓冲区,但由于该地址在其自己的地址空间中无效,因此可能立即导致访问冲突,或者(更糟糕的是)它可以无声地覆盖内部数据.


您可以使用VirtualAllocEx()和ReadProcessMemory()来使用具有Notepad ++可用地址的缓冲区.我已经整理了一个适合我的快速Delphi程序,重要的代码如下:

procedure TForm1.Button1Click(Sender: TObject);
const
  VMFLAGS = PROCESS_VM_OPERATION or PROCESS_VM_READ or PROCESS_VM_WRITE;
var
  Wnd: HWND;
  Len: integer;
  ProcessId, BytesRead: Cardinal;
  ProcessHandle: THandle;
  MemPtr: PChar;
  s: string;
begin
  Wnd := $30488;
  Len := SendMessage(Wnd, 2183, 0, 0);
  if Len > 0 then begin
    GetWindowThreadProcessId(Wnd, @ProcessId);
    ProcessHandle := OpenProcess(VMFLAGS, FALSE, ProcessId);
    MemPtr := VirtualAllocEx(ProcessHandle, nil, Len + 1,
      MEM_RESERVE or MEM_COMMIT, PAGE_READWRITE);
    if MemPtr <> nil then try
      SendMessage(Wnd, 2182, Len + 1, integer(MemPtr));
      SetLength(s, Len + 1);
      ReadProcessMemory(ProcessHandle, MemPtr, @s[1], Len + 1, BytesRead);
      SetLength(s, BytesRead);
      Memo1.Lines.Text := s;
    finally
      VirtualFreeEx(ProcessId, MemPtr, Len + 1, MEM_RELEASE);
    end;
  end;
end;

这是使用Ansi版API函数的早期Delphi版本,您可能会使用SendMessageW和WideChar缓冲区,但一般的想法应该是清楚的.

推荐阅读
和谐啄木鸟
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有