Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: WinAPI и системное программирование > Получить RTF-текст из RichEdit'а


Автор: Keeper89 28.12.2011, 03:49
Доброго времен суток.

Облазил весь интернете поисках примеров - все около. Подскажите, как сделать такую вещь.  smile 

Заранее спасибо.

Автор: RomanEEP 28.12.2011, 07:57
SaveToStream уже не катит?

Автор: RomanEEP 28.12.2011, 08:58
Курить вот это:
Код

procedure TRichEditStrings.SaveToStream(Stream: TStream; Encoding: TEncoding);
var
  EditStream: TEditStream;
  TextType: Longint;
  StreamInfo: TRichEditStreamInfo;
  Converter: TConversion;
  Preamble: TBytes;
{$IF DEFINED(CLR)}
  Handle: GCHandle;
{$IFEND}
begin
  if FConverter <> nil then
    Converter := FConverter
  else
    Converter := RichEdit.DefaultConverter.Create;
  StreamInfo.Stream := Stream;
  StreamInfo.Converter := Converter;
  StreamInfo.PlainText := PlainText;
  StreamInfo.Encoding := Encoding;
  try
{$IF DEFINED(CLR)}
    Handle := GCHandle.Alloc(TObject(StreamInfo));
{$IFEND}
    try
      with EditStream do
      begin
{$IF DEFINED(CLR)}
        dwCookie := DWORD_PTR(IntPtr(Handle));
{$ELSE}
        dwCookie := DWORD_PTR(@StreamInfo);
{$IFEND}
        pfnCallBack := StreamSave;
        dwError := 0;
      end;
      if PlainText then
      begin
        TextType := SF_TEXT or SF_UNICODE;
        if Encoding <> nil then
        begin
          Preamble := Encoding.GetPreamble;
          if Length(Preamble) > 0 then
{$IF DEFINED(CLR)}
            Stream.WriteBuffer(Preamble, Length(Preamble));
{$ELSE}
            Stream.WriteBuffer(Preamble[0], Length(Preamble));
{$IFEND}
        end;
      end
      else
        TextType := SF_RTF;
      SendGetStructMessage(RichEdit.Handle, EM_STREAMOUT, TextType, EditStream, True);
      if EditStream.dwError <> 0 then
        raise EOutOfResources.Create(sRichEditSaveFail);
    finally
{$IF DEFINED(CLR)}
      Handle.Free;
{$IFEND}
    end;
  finally
    if FConverter = nil then Converter.Free;
  end;
end;

Автор: Keeper89 28.12.2011, 12:07
RomanEEP, где сказано про собственное приложение?
Вот хороший пример:
Код

unit MainUnit;

interface

uses
 Windows, Messages, Graphics, Controls, Forms, ComCtrls,
 StdCtrls, Classes, SysUtils, Dialogs;

type
 TForm1 = class(TForm)
   Edit1: TEdit;
   Button1: TButton;
   Memo1: TMemo;
   Edit2: TEdit;
   procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
     Shift: TShiftState; X, Y: Integer);
   procedure Button1Click(Sender: TObject);
 end;

var
 Form1: TForm1;

implementation

uses RichEdit;

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
{$DEFINE DEBUG_FIND}
var
 MainWindowHandle, RichEditWHandle: THandle;
 ProcessID : Cardinal;
 ProcessHandle : THandle;
 StructTextEx: TGetTextEx;
 StructTextLength: TGetTextLengthEx;
 PStructTextLength, PStructTextEx, PTextBuffer: Pointer;
 BytesWriten, RichEditTextLength: DWORD;
 TextBuffer: array of Char;
begin

 {$IFDEF DEBUG_FIND}
   // Заполняем данные для отладки
   // В качестве примера работаем с WordPad-ом
   Edit1.Text := "Document - WordPad";
   Edit2.Text := "RICHEDIT50W";
 {$ENDIF}

 // Ищем окно
 MainWindowHandle := FindWindow(nil , PChar(Edit1.Text));
 if MainWindowHandle = 0 then RaiseLastOSError;
 RichEditWHandle := FindWindowEx(MainWindowHandle, 0, PChar(Edit2.Text), nil);
 if RichEditWHandle = 0 then RaiseLastOSError;

 // Узнаем ID процесса
 GetWindowThreadProcessId(MainWindowHandle, @ProcessID);
 if ProcessID = 0 then RaiseLastOSError;

 // Открываем процесс
 ProcessHandle := OpenProcess(PROCESS_ALL_ACCESS, True, ProcessID);
 if ProcessHandle = 0 then RaiseLastOSError;
 try

   // Выделяем в нем память под структуру TGetTextLengthEx
   PStructTextLength := VirtualAllocEx(ProcessHandle, nil,
     SizeOf(TGetTextLengthEx), MEM_COMMIT or MEM_TOP_DOWN, PAGE_READWRITE);
   if PStructTextLength = nil then RaiseLastOSError;
   try

     // Подготавливаем структуру...
     StructTextLength.flags := GTL_NUMBYTES or GTL_USECRLF;
     StructTextLength.codepage := CP_ACP;

     // ...и пишем ее в память процесса
     if not WriteProcessMemory(ProcessHandle, PStructTextLength,
       @StructTextLength, SizeOf(TGetTextLengthEx),
       BytesWriten) then RaiseLastOSError;
     if BytesWriten <> SizeOf(TGetTextLengthEx) then RaiseLastOSError;

     // Отправляем сообщение удаленному RichEdit,
     // чтобы узнать необходимый размер буффера
     RichEditTextLength := SendMessage(RichEditWHandle,
       EM_GETTEXTLENGTHEX , Integer(PStructTextLength), 0);

   finally
     // Освобождаем выделенную под TGetTextLengthEx память
     VirtualFreeEx(ProcessHandle, PStructTextLength, 0, MEM_RELEASE);
   end;

   // Выделяем память под структуру TGetTextEx
   PStructTextEx := VirtualAllocEx(ProcessHandle, nil,
     SizeOf(TGetTextEx), MEM_COMMIT or MEM_TOP_DOWN, PAGE_READWRITE);
   if PStructTextEx = nil then RaiseLastOSError;
   try

     // Подготавливаем структуру...
     ZeroMemory(@StructTextEx, SizeOf(TGetTextEx));
     StructTextEx.flags := GT_USECRLF;
     StructTextEx.cb := RichEditTextLength;

     // ...и пишем ее в память процесса
     if not WriteProcessMemory(ProcessHandle, PStructTextEx,
       @StructTextEx, SizeOf(TGetTextEx),
       BytesWriten) then RaiseLastOSError;
     if BytesWriten <> SizeOf(TGetTextEx) then RaiseLastOSError;

     // Выделяем память под текстовый буффер
     PTextBuffer := VirtualAllocEx(ProcessHandle, nil,
     RichEditTextLength, MEM_COMMIT or MEM_TOP_DOWN, PAGE_READWRITE);
     if PTextBuffer = nil then RaiseLastOSError;
     try

       // отправляем сообщение
       SendMessage(RichEditWHandle, EM_GETTEXTEX, Integer(PStructTextEx),
         Integer(PTextBuffer));

       // Читаем, то, что у нас скопировалось
       SetLength(TextBuffer, RichEditTextLength);
       if not ReadProcessMemory(ProcessHandle, PTextBuffer, @TextBuffer[0],
         RichEditTextLength, BytesWriten) then RaiseLastOSError;

       Memo1.Text := String(TextBuffer);

     finally
       // Освобождаем выделенную под тестовый буффер память
       VirtualFreeEx(ProcessHandle, PTextBuffer, 0, MEM_RELEASE);
     end;

   finally
     // Освобождаем выделенную под TGetTextEx память
     VirtualFreeEx(ProcessHandle, PStructTextEx, 0, MEM_RELEASE);
   end;

 finally
   // Закрываем процесс
   CloseHandle(ProcessHandle);
 end;
end;

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
 Shift: TShiftState; X, Y: Integer);
begin
 ReleaseCapture;
 SendMessage(Handle, WM_SYSCOMMAND, SC_MOVE or HTCAPTION, 0);
end;

end.

http://forum.sources.ru/index.php?showtopic=153001&view=findpost&p=1245162

Но здесь, просто текст, не EM_STREAMOUT. Пробовал его переделать - безрезультатно.

Автор: RomanEEP 29.12.2011, 10:58
так тем пособом что я писал выдирается из любого РичЭдита)) просто хандле нужный тебе подставляешь и всё. Всю логику работы смотри в ComCtrls.pas, в VCL всё написано до нас и искать ничего не надо

Автор: Keeper89 29.12.2011, 11:15
Это же другой процесс. И в этом процессе другая адресация и никто не будет знать о колбэке в нашем адресом пространстве.
Или я не прав?  

Автор: RomanEEP 29.12.2011, 11:42
мы не работаем с адресами в другом процессе. мы отправляем сообщение, каллбэк которого всегда будет в адресном пространстве того процесса из которого оно было отправлено

Автор: RomanEEP 29.12.2011, 12:46
прошу прощения, был не прав... каллбэк в другом процессе не прокатит
вот тут предлагается выполнить этот код через подгружаемую к процессу дллку 
http://www.progtown.com/topic176317-how-to-obtain-the-data-from-richedit-in-another-the-program.html
и еще есть вариант просто использовать буфер обмена

Автор: Keeper89 29.12.2011, 13:40
Библиотека - не желаемый вариант. А с буфером как?

Добавлено через 1 минуту и 36 секунд
Возможен ли первоначальный вариант без библиотеки вообще или нет? Чтобы сразу определиться куда идти smile

Автор: RomanEEP 29.12.2011, 14:50
Если нет возможности изменить приложение в котором находится ричэдит, то без бибиотеки обойтись сложно, но можно и довольно геморно - записав каллбэк функции в память процесса через WriteProcessmemory и считать полученные данные через ReadProcessMemory. библиотека делает фактически тоже самое но без лишнего геморроя который автоматом делается виндой в LoadLibrary()

Через буфер - просто
Код

  SendMessage(ExternalHandle, EM_SETSEL, 0, -1);
  SendMessage(ExternalHandle, WM_COPY, 0, 0);
  RichEdit.PasteFromClipboard;

Автор: Keeper89 29.12.2011, 14:59
Насколько я помню WM_COPY копирует только текст. Или нет?

Вот с первым вариантом пример и хотелось бы. 

Автор: Keeper89 29.12.2011, 22:31
В общем, пока решил следующим образом:
Код

class function TRichEditGrabber.TryGetRTFText(RichEditHandle:
    THandle; out QuestionRTF: string): Boolean;
var
  RichEdit: TRichEdit;
  ss: TStringStream;
begin
  Result := False;

  SendMessage(RichEditHandle, EM_SETSEL, 0, -1);
  SendMessage(RichEditHandle, WM_COPY, 0, 0);
  SendMessage(RichEditHandle, EM_SETSEL, 0, 0);

  RichEdit := TRichEdit.CreateParented(HWND_MESSAGE);
  ss := TStringStream.Create;
  try
    RichEdit.PasteFromClipboard;
    RichEdit.Lines.SaveToStream(ss);
    QuestionRTF := ss.DataString;
    Result := True;
  finally
    FreeAndNil(ss);
    FreeAndNil(RichEdit);
  end;
end;

Но буду ждать примеров первого вариант.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)