2

C# で SendMessage を使用して、Notepad++ でドキュメント テキストを取得しようとしています。以下は私の現在のコードです。SendMessage の最初の呼び出しでは、テキストの長さが正しく返されます。SendMessage の 2 回目の呼び出しでは、テキストが 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);
4

2 に答える 2

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;

これは、API 関数の Ansi バージョンを使用する以前の Delphi バージョンです。おそらく SendMessageW と WideChar バッファを使用しますが、一般的な考え方は明確です。

于 2009-02-21T22:57:46.507 に答える