Поиск по этому блогу

четверг, 23 июня 2011 г.

Обмен информацией между процессами vs WM_COPYDATA

Реализация IPC vs  WM_COPYDATA

Немного теории.
Сообщения WM_COPYDATA дают возможность обмениваться информацией между процессами. Обмен происходит через ядро (kernel). В принимающем процессе выделяется память (space) и удерживается для скопированных ядром данных. Отправитель обязан передать указатель на структуру с данными (COPYDATASTRUCT).
WM_COPYDATA
wParam = (WPARAM) (HWND) hwnd;   // дескриптор передающего окна
lParam = (LPARAM) (PCOPYDATASTRUCT) pcds;   // pointer COPYDATASTRUCT  - указатель на структуру с данными
Результат true - если принимающая программа обработала сообщение, false - нет.
Реализация.


1. Создадим структуру, с помощью которой будем обмениваться данными, нам понадобятся:
dwData - этот элемент мы используем, чтобы идентифицировать наши данные, дабы приемник понимал что с ними делать  (0-текст 1-бинарные данные).
cbData - элемент будет содержать длину данных
lpData - элемент будет содержать адрес данных (указатель)
Код приложения, которое отправляет данные
размещаем на форме Edit1, Image1 и две кнопки
unit Send2Process;
interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;
type
// определяем нашу структуру (record)
  TCopyDataStruct = packed record
    dwData: DWORD;   // тип данных
    cbData: DWORD;   // длина данных
    lpData: Pointer; // адрес данных (указатель)
  end;
...
    { Public declarations }
    // эта процедура будет копировать все, что мы пожелаем
    procedure SendCopyData(hTargetWnd: HWND; ACopyDataStruct:TCopyDataStruct);
...
procedure TForm1.SendCopyData(hTargetWnd: HWND; ACopyDataStruct: TCopyDataStruct);
begin
  if hTargetWnd <> 0 then
    SendMessage(hTargetWnd, WM_COPYDATA, Longint(Handle), Longint(@ACopyDataStruct))
  else
    ShowMessage('Нет приемника');
end;

Передача текста.
Передаем текст из компонента Edit1

procedure TForm1.Button1Click(Sender: TObject);
var
  MyCopyDataStruct: TCopyDataStruct; // наша структура
  hRecieverWnd: HWND; // handle приемника
begin
// определяем структуру COPYDATASTRUCT для использования с WM_COPYDATA
  with MyCopyDataStruct do
  begin
    dwData := 0; //  0-текст   
    cbData := StrLen(PChar(Edit1.Text)) + 1;  // длина данных +1 с учетом #0 (terminating)
    lpData := PChar(Edit1.Text); 
  end;
 // ищем окно приемника по заголовку 'Message Receiver'
  hRecieverWnd := FindWindow(nil,PChar('Message Receiver'));
  // Передаем нашу структуру приемнику
  SendCopyData(hRecieverWnd, MyCopyDataStruct);
end;

Передаем бинарные данные из потока.
соответственно на форме отправителя помещаем image1 и грузим в нее какую-нибудь картинку
procedure TForm1.Button2Click(Sender: TObject);
var
  stream: TMemoryStream; // поток
  MyCopyDataStruct: TCopyDataStruct; // наша структура
  hRecieverWnd: HWND; // handle приемника
begin
    stream := TMemoryStream.Create;
  try
    image1.Picture.Bitmap.SaveToStream(stream); // пишем картинку в поток
    with MyCopyDataStruct do
    begin
      dwData := 1; // сообщаем, что передаем бинарные данные
      cbData := stream.Size; // размер данных
      lpData := stream.Memory; // pointer
    end;
    // Ищем окно по заголовку
    hRecieverWnd := FindWindow(nil,PChar('Message Receiver'));
    // Отправляем данные
    SendCopyData(hRecieverWnd,MyCopyDataStruct);

  finally
    stream.Free;
  end;
end;

Теперь пишем приложение, которое принимает данные.
размещаем на форме Edit1, Image1, Label1, заголовок формы должен быть  Message Receiver
структура данных TCopyDataStruct один в один повторяет описание структуры на сервере.


unit ReceiveProcess;
interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls;
type
// структура данных
  TCopyDataStruct = packed record
    dwData: DWORD;   // тип данных
    cbData: DWORD;   // длина
    lpData: Pointer; // адрес
  end;
  TForm2 = class(TForm)
    Label1: TLabel;
    Edit1: TEdit;
    Image1: TImage;
  private
    // достаем сообщение
    procedure WMCopyData(var Msg: TWMCopyData); message WM_COPYDATA;

  public
    { Public declarations }
  end;
var
  Form2: TForm2;
implementation
{$R *.dfm}
{ TForm2 }
procedure TForm2.WMCopyData(var streamg: TWMCopyData);
var
  sText: array[0..99] of Char;
  stream: TMemoryStream;
begin
   case streamg.CopyDataStruct.dwData of
    0: // тип данных = 0, значит передали текст
      begin
        StrLCopy(sText, streamg.CopyDataStruct.lpData, streamg.CopyDataStruct.cbData);
        label1.Caption := sText;
      end;
    1: // тип данных = 1, значит передали бинарные данные
      begin
        stream := TMemoryStream.Create;
        try
          with Msg.CopyDataStruct^ do
           stream.Write(lpdata^, cbdata); // пишем в поток буфер, который находится по адресу lpdata, длиной cbdata
           stream.Position := 0;
          image1.Picture.Bitmap.LoadFrostreamtream(stream);
        finally
          stream.Free;
        end;
      end;
  end;
end;
end.

Ссылки 
http://delphi.about.com/od/windowsshellapi/a/wm_copydata.htm - How to Send Information (String, Image, Record) Between Two Applications.

Обзорная Why/What do you Send Between Delphi Applications?  там же на delphi.about.com