Получить текст из элемента управления Scintilla, используя SendMessage - PullRequest
2 голосов
/ 22 февраля 2009

Я пытаюсь получить текст документа в Notepad ++, используя SendMessage в C #. Ниже мой текущий код. Первый вызов 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);

Ответы [ 2 ]

5 голосов
/ 22 февраля 2009

Проблема в том, что вы отправляете сообщение в элемент управления Scintilla, у которого есть адрес буфера StringBuilder в lParam, но элемент управления Scintilla в Notepad ++ находится в другом адресном пространстве, поэтому адрес в сообщении окна, которое он получает, может не быть написано. Стандартные сообщения, такие как WM_GETTEXT и WM_SETTEXT обрабатываются таким образом, что для вас выполняется необходимое сопоставление адресов, но этого не происходит для специальных сообщений, используемых элементом управления Scintilla. Для получения дополнительной информации ищите маршаллинг.

К сожалению, поддержка WM_GETTEXTLENGTH и WM_GETTEXT прекращается из элемента управления Scintilla, и в документации рекомендуется использовать специальные сообщения SCI_XXX . Блокнот ++ уже не работает с 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;

Это более ранняя версия Delphi, использующая версию API-функций Ansi; вероятно, вы бы использовали SendMessageW и буфер WideChar, но общая идея должна быть ясной.

1 голос
/ 29 декабря 2013

Я получил его после некоторой возни.

При использовании MEM_RELEASE в VirtualFreeEx я считаю, что размер должен быть 0, иначе функция вернет false.

...