问题是您向 Scintilla 控件发送消息,该控件在 lParam 中具有 StringBuilder 缓冲区的地址,但 Notepad++ 中的 Scintilla 控件位于不同的地址空间中,因此无法写入它接收到的窗口消息中的地址到。WM_GETTEXT和WM_SETTEXT等标准消息的处理方式是为您执行必要的地址映射,但对于 Scintilla 控件使用的特殊消息则不会发生这种情况。有关更多信息查找编组。
不幸的是,对WM_GETTEXTLENGTH和WM_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 缓冲区,但总体思路应该很清楚。