使用 SendMessage 从闪烁控件中检索文本

Posted

技术标签:

【中文标题】使用 SendMessage 从闪烁控件中检索文本【英文标题】:Retrieve text from a scintilla control using SendMessage 【发布时间】:2009-02-21 22:01:52 【问题描述】:

我正在尝试使用 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);

【问题讨论】:

【参考方案1】:

问题是您向 Scintilla 控件发送了一条消息,该消息在 lParam 中具有您的 StringBuilder 缓冲区的地址,但 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;

这是使用 API 函数的 Ansi 版本的早期 Delphi 版本,您可能会使用 SendMessageW 和 WideChar 缓冲区,但总体思路应该很清楚。

【讨论】:

感谢您的提示!那么我可以使用 VirtualAllocEx 分配一些内存,然后在 SendMessage 中传递内存吗?我还能如何从 Notepad++ 中检索文档文本? 我搞定了。我错过了 OpenProcess 调用。我还尝试使用 .Net 框架中提供的 Marhal 类读取内存。我将不得不调查为什么该方法不起作用。再次感谢!【参考方案2】:

经过一番摆弄,我得到了它的工作。

在VirtualFreeEx中使用MEM_RELEASE时,我认为size一定是0,否则函数会返回false

【讨论】:

以上是关于使用 SendMessage 从闪烁控件中检索文本的主要内容,如果未能解决你的问题,请参考以下文章

防止控件闪烁

从.net(C#)中的Webbrowser控件中检索选定的文本

c# SendMessage发送汉字

如何从 ListControl(ListView) 中检索数据?

更新期间停止文本框闪烁

将光标定位在 RichEdit 控件中文本的末尾