Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: WinAPI и системное программирование > Приём сообщения, посланного через SendMessage


Автор: Micher 6.10.2008, 16:06
День добрый. У меня такая ситуация. Есть 2 приложения, написанные НЕ на делфи, но они импортируют DLL на делфи. Приложения получают данные через интернет. Первое приложение, получив определённую информацию, должна послать данные 2-му приложению(например какую-то строку). Написал в DLL такую функцию для отправки сообщения посредством SendMessage:

Код

Function SendMes(): integer; export; stdcall;
var
  pril: hWnd;
  aCopyData: TCopyDataStruct;
 begin
   with aCopyData do
    begin
     dwData := 0;
     cbData := StrLen(PChar('Ot ulibki stanet vsem svetley!')) + 1;
     lpData := PChar('Ot ulibki stanet vsem svetley!')
    end;
   pril:= FindWindow(nil, 'ЗАГОЛОВОК ПРИЛОЖЕНИЯ, К-МУ ОТСЫЛАЕСТЯ СООБЩЕНИЕ');
   if pril <> 0 then
    begin
      SendMessage(pril, WM_COPYDATA, 0, Longint(@aCopyData));
      SendMes:=1;
    end else SendMes:=0;
 end;


Теперь, не могу понять, как принять те данные(в частности строку) вторым приложением? Нужна функция, которая бы возвращала именно эти данные. Нашёл в инете примерно такой код:

Код

type
   TForm1 = class(TForm)
     private
     procedure WMCopyData(var Msg: TWMCopyData); message WM_COPYDATA;
   public
   end;
procedure TForm1.WMCopyData(var Msg: TWMCopyData);
 var
   sText: array[0..99] of Char;
 begin
   // generate text from parameter
  StrLCopy(sText, Msg.CopyDataStruct.lpData, Msg.CopyDataStruct.cbData);
   // write received text
  ShowMessage(sText);
 end;


Помогите написать функцию для DLL, в делфи очень слабо разбираюсь, к сожалению

Автор: Romikgy 7.10.2008, 13:37
нужно имено через сендмессаге?

Автор: Poseidon 7.10.2008, 15:28
Micher, сообщения посылаются окнам! Самой программе ничего не посылается, если у нее нет окна. Поэтому, если хочешь передавать текст от одной библиотеки другой, то сообщения использовать не получится, т.к. в библиотеках нет окон (конечно, можно запихнуть, но зачем?). Пробуй другие варианты. Мьютексы или симафоры.

Автор: Rennigth 7.10.2008, 15:56
Цитата(Poseidon @  7.10.2008,  15:28 Найти цитируемый пост)
Самой программе ничего не посылается, если у нее нет окна.

 smile  ну эт ты зря smile По сути сообщения посылаються потоку... и послать сообщение можно и без оконному приложению через PostThreadMessage. Другой вопрос будет ли поток обрабатывать сообщение.

Автор: Micher 7.10.2008, 17:02
Цитата(Romikgy @ 7.10.2008,  13:37)
нужно имено через сендмессаге?

Рассмотрю другие варианты smile

Добавлено @ 17:06
Цитата(Poseidon @ 7.10.2008,  15:28)
Micher, сообщения посылаются окнам! Самой программе ничего не посылается, если у нее нет окна. Поэтому, если хочешь передавать текст от одной библиотеки другой, то сообщения использовать не получится, т.к. в библиотеках нет окон (конечно, можно запихнуть, но зачем?). Пробуй другие варианты. Мьютексы или симафоры.

Вы недопоняли. Есть 2 приложения(которые работают в отдельных окнах), оба они написаны не на делфи, но оба подключают одну и ту же DLL, написанную на делфи как раз. 1 приложение, получив данные извне, должно как то сообщить что-либо второму приложению - например, послать ту же строку, ну или набор чисел. Т.е. нужно написать в этой DLL грубо говоря 2 функции:
1) функция типа sendData() - отправить данные
2) функция типа receivedata() - получить данные

1 приложение вызывает sendData() , а второе как то должно принять, то что посылает ему первое. И обе функции будут в DLL

Автор: Rennigth 7.10.2008, 17:36
Micher, по сабжу. что мешает из dll узнать хендл принимающего сообщения окна, и подменить ему с помощью SetWindowLong процедуру обработки сообщений?

п.с. завтра накидаю примерчик, сейчас нет ни возможности, ни мозгов, ни желания :(

Автор: Micher 7.10.2008, 19:25
Цитата(Rennigth @ 7.10.2008,  17:36)
Micher, по сабжу. что мешает из dll узнать хендл принимающего сообщения окна, и подменить ему с помощью SetWindowLong процедуру обработки сообщений?

п.с. завтра накидаю примерчик, сейчас нет ни возможности, ни мозгов, ни желания :(

Можно отсюда поподробнее? smile

Автор: Rennigth 8.10.2008, 16:58
Цитата(Micher @  7.10.2008,  19:25 Найти цитируемый пост)
Можно отсюда поподробнее?  

Можно. Завтра/После завтра. Болею...

Автор: Romikgy 8.10.2008, 20:50
имхо можно юзать filemapping , udp, pipe .... можно заюзать атомы ,
для твоих уелей наверное первое самое оптимальное

Автор: Micher 12.10.2008, 08:39
Цитата(Romikgy @ 8.10.2008,  20:50)
имхо можно юзать filemapping , udp, pipe .... можно заюзать атомы ,
для твоих уелей наверное первое самое оптимальное

Нашёл в инете пример по работе с FileMapping:
Код

unit Main;

interface

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

type
  TForm1 = class(TForm)
    edVariable: TEdit;
    lbEnterValues: TLabel;
    bbOK: TBitBtn;
    bbExit: TBitBtn;
    lbShowValue: TLabel;
    lbVariable: TLabel;
    Timer1: TTimer;
    procedure FormCreate(Sender: TObject);
    procedure bbExitClick(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure bbOKClick(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  //глобальные переменные
  hFileMapObj: THandle; //описатель FaleMapping
  lpBaseAddress: PChar; //"указатель" (см. выше) на начальный адрес данных
implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
  //создадим FileMapping с именем MySharedValue
  //и передадим его хэндл в глобальную переменную hFileMapObj
  hFileMapObj := CreateFileMapping(MAXDWORD, nil, PAGE_READWRITE, 0, 4,
    'MySharedValue');
  if (hFileMapObj = 0) then
    //ошибочка вышла
    ShowMessage('Не могу создать FileMapping!')
  else
    //подключим FileMapping к адресному пространству
    //и получим начальный адрес данных
    lpBaseAddress := MapViewOfFile(hFileMapObj, FILE_MAP_WRITE, 0, 0, 0);
  if lpBaseAddress = nil then
    //ошибочка вышла
    ShowMessage('Не могу подключить FileMapping!');
end;

procedure TForm1.bbExitClick(Sender: TObject);
begin
  //закроем форму
  Close;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  //кое-что надо сделать при закрытии формы:
  //отключим FileMapping от адресного пространства
  UnMapViewOfFile(lpBaseAddress);
  //освободим объект FileMapping
  CloseHandle(hFileMapObj);
  //теперь форму можно закрыть
  Action := caFree;
end;

procedure TForm1.bbOKClick(Sender: TObject);
begin
  //поместим в адресное пространство свои данные
  //переменная типа PChar имеет в конце завершающий #0, значит при считывании данных
  //система сама сможет определить, где находится конец нужных данных
  StrPCopy(lpBaseAddress, edVariable.Text);
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  //считаем нужные данные, обратившись по начальному адресу
  //данных адресного пространства FileMapping
  lbVariable.Caption := PChar(lpBaseAddress);
end;

end.


Вопросов возникло немало: а как из другого приложения читать то, что пишет первая. Нужно ли, чтобы обе программы работали одновременно ? У меня планируется, что 2 приложения будут работать в течение долгого времени - несколько часов(в бесконечных циклах). Т.е. они оба будут работать беспрерывно. Но как всё таки читать вторым приложением то что пишет первое, не понимаю. Как получить доступ к той области, куда пишет первое.... по идентификатору какому-то(заранее оговоренному?) ? Было бы здорово получить пример  smile . Заранее спасибо за любой ответ

Автор: bems 12.10.2008, 16:52
Цитата(Micher @  12.10.2008,  08:39 Найти цитируемый пост)
по идентификатору какому-то(заранее оговоренному?) ?
да, это у тебя 'MySharedValue'

создаешь файлмаппинг с одинаковым именем в обеих программах и на практике первая программа его создаст, а вторая только откроет. MapViewOfFile в разных процессах вернет (в общем случае) разное значение, но обе области гарантировано имеют одинаковое содержимое в обоих процессах. В одном пишешь в область, начинающуюся с lpBaseAddress, в другом читаешь. Только наверное нужно еще как-то передавать сигнал о том что данные записаны и пора перечитать. Для этой цели можно использовать эвент или мютекс.

Автор: Micher 12.10.2008, 20:24
Цитата(bems @ 12.10.2008,  16:52)
Только наверное нужно еще как-то передавать сигнал о том что данные записаны и пора перечитать. Для этой цели можно использовать эвент или мютекс.

Спасибо за ответ! Сигнал подавать не придётся, так как оба приложения фактически в бесконечных циклах будут читать/писать в маппинг.
А вопрос, если первое приложение(которое создало маппинг) завершит свою работу, то маппинг будет ещё доступен второму приложению или память под маппинг очистится?

Автор: bems 13.10.2008, 15:39
Цитата(Micher @  12.10.2008,  20:24 Найти цитируемый пост)
Сигнал подавать не придётся, так как оба приложения фактически в бесконечных циклах будут читать/писать в маппинг.
тогда нужно предотвратить одновремменные запись и чтение (мютексом) а то можно прочитать что-то записаное на половину


Цитата(Micher @  12.10.2008,  20:24 Найти цитируемый пост)
если первое приложение(которое создало маппинг) завершит свою работу, то маппинг будет ещё доступен второму приложению или память под маппинг очистится? 
файлмаппинг это объект ядра, а они уничтожаются когда закрывается последний хэндл. 
Цитата(Micher @  12.10.2008,  08:39 Найти цитируемый пост)
hFileMapObj := CreateFileMapping

тут ты получаеш хэндл. Если этот код вызывается из двух процессов то имеем уже два хэндла
Цитата(Micher @  12.10.2008,  08:39 Найти цитируемый пост)
lpBaseAddress := MapViewOfFile(hFileMapObj

тут создается неявный хэндл (еще один) на тот же файлмаппинг. Неявный потому что lpBaseAddress это не хэндл, а указатель. Неявный хэндл закрывается при вызове UnmapViewOfFile. Это тоже выполняется в двух процессах, итого имеем 4 хэндла на один объект. Если ты закроешь в одном процессе все его хэндлы на объект (две штуки) или тупо крашнеш программу (при этом винда сама закроет все хэндлы принадлежащие умирающему процессу) то останется еще два хэндла на объект (принадлежащие другому процессу) и объект будет благополучно существовать, пока счётчик хэндлов не станет 0 

Автор: Micher 13.10.2008, 17:52
Цитата(bems @ 13.10.2008,  15:39)
тогда нужно предотвратить одновремменные запись и чтение (мютексом) а то можно прочитать что-то записаное на половину

Стыдно спросить, но что есть мютекс? И как использовать его чтобы предотвратить одновременную запись и чтение?

Автор: bems 13.10.2008, 18:30
Цитата(Micher @  13.10.2008,  17:52 Найти цитируемый пост)
что есть мютекс? И как использовать его чтобы предотвратить одновременную запись и чтение? 
это еще один объект ядра, он служит для эксклюзивного доступа к ресурсам.
Еще до любой записи или чтения (там же где ты вызывашь CreateFileMapping) нужно сделать 
Код

hReadWriteMutex:=CreateMutex(nil,false,'UniqueMutexName');

а чтение и запись так:
Код

WaitForSingleObject(hReadWriteMutex,INFINITE);
try
//тут запись в одном процессе и чтение в другом
finally
 ReleaseMutex(hReadWriteMutex);
end;


кстати имена своих файлмаппинга и мютекса сделай какими-то сильно длинными и уникальными, чтобы случайно не пересечся с чьей-то чужой прогой

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