Содержание материала

  
В этой статье мы будем разбирать связь двух приложений с помощью именных каналов (named pipes). Рассмотрим типы каналов, а также создадим клиент и
сервер.
 
1.Для чего именные каналы нужны:
Именные каналы являются средством «общения» программ. Они широко используются в UNIX-подобных системах, однако и в Windows они нашли применение. С помощью именных каналов можно передать любую информацию, так как все каналы работают через файлы. Даже для чтения\записи данных в канале используется функция WinAPI для работы с файлами ReadFile() и WriteFile(). Передаваться может переменная любого типа (Integer, Boolean, Tbitmap и так далее). Однако, надо заметить, что для передачи строки, она должна быть ограничена String[40]; Иначе при чтении канала будет появляться ошибка.
 

2. Пример отправляемой переменной:
Далее в статье я буду использовать отправляемую переменную типа TpipeData, этот тип будет такой:

Code:

type TpipeData = packed record //Имя можно изменить, а тип packed record оставьте

pEvent:byte;

ClientName: string[40]; //Заметьте, ограниченная строка

Date:TdateTime;

End;

  
 
Как я уже сказал, здесь можно передавать любые переменные


 
==========СЕРВЕРНАЯ ЧАСТЬ==========
 
3.Использование потоков (TThread):
Как такового события записи в канал нет, поэтому придётся запускать цикл чтения. Как известно, циклы подвешивают программы (а точнее зацикливают). Application.ProcessMessages при работе с функцией ReadFile не поможет, так как ReadFile будет выполнятся до тех пор, пока кто-то что-то не запишет. Поэтому надо использовать потоки (TThread). Хочу заметить, что обращение к визуальным компонентам (формам, кнопкам, лэйблам и так далее) из дополнительных потоков невозможно. Для того, чтобы полученные данные не остались в дополнительном потоке, надо его синхронизироватьс главным. Для этого есть функция TThread.Synchronize(method: TtreadMethod); Параметром этой функции надо передовать Какой-нибудь метод(нами созданный) дополнительного потока, где есть обращение к визуальным компонентам.
А теперь напишем код нашего потока:
 

Code:

type

TPipeThread = class(TThread)

private

PipeData:TPipeData; //Вот он – наша приёмная переменная, а точнее буфер.

Protected

   procedure Execute; override; //Здесь мы будем создавать канал и читать данные из него

public

   procedure ShowData; //Здесь будем передовать данные форме

   constructor Create(CreateSuspended: Boolean); reintroduce; overload; //Создание потока

end;

 
4. Работа сименными каналами:
Здесь мы рассмотрим нашу процедуру TpipeThread.Execute; но сначала рассмотрим «настройку» наших каналов. Функция CreateNamedPipe доступна только серверу. Ей должны быть переданы следующие параметры:
1. lpName имя нашего канала, а точнее его директория. Если сервер будет расположен на той-же машине, что и клиент, то первый параметр должен быть такой: '\\.\PIPE\ИмяКанала', где имя канала ваше название, только латинскими буквами
2. dwOpenMode работа канала может быть:
PIPE_ACCESS_INBOUND сервер может только читать канал
PIPE_ACCESS_OTTBOUND сервет может только записывать
PIPE_ACCESS_DUPLEX сервер может и писать и читать
3. dwPipeMode режим канала здесь говорится о синхронности\асинхронности канала, и о том, каким методом будетпроизводится обмен данных в канале. Значения:
PIPE_WAIT - синхронный канал
PIPE_NOWAIT асинхронный канал
PIPE_READMODE_BYTE метод чтения - байты
PIPE_READMODE_MESSAGE метод чтения пакеты
PIPE_TYPE_BYTE тип канала - байты
PIPE_TYPE_MESSAGE - тип канала пакеты.
В dwPipeMode надо передавать три параметра, например, в нашем примере: PIPE_WAIT or PIPE_READMODE_MESSAGE or PIPE_TYPE_MESSAGE. А теперь, давайте вспомним наш тип TpipeData он у нас packed, то есть пакет. Поэтому наш канал работает с пакетами.
4. hMaxInstances максимальное число одновременных подключений любое ваше число, однако можно указать PIPE_UNLIMITED_INSTANCES для безконечного числа клиентов.
5. nOutBufferSize разбер буфера чтения. Можно найти так SizeOf(TpipeData) Это как раз тип нашего буфера.
6. nInBufferSize размер буфера записи. Обычно такой же как и буфер чтения.
7. nDefaultTimeOut максимальное время чтения (в милисекундах)
8. lpSecurityArtributes просто пишем nil.
Также нам понядобятся фцнкции ConnectNamedPipe, ReadFile и DisconnectNamedPipe, но их параметры мы рассмотрим в процессе написания кода. И так, а теперь наша процедура:
 


Code:

procedure TPipeThread.Execute;

var

hPipe: THandle; //Указатель на наш канал

bytesRead: DWORD;  //Количество прочитанных байт

begin

try

  hPipe := CreateNamedPipe('\\.\PIPE\OurPipe', //Наше имя

     PIPE_ACCESS_INBOUND, // сервер может только читать канал

     PIPE_WAIT or               // Синхронная работа

     PIPE_READMODE_MESSAGE or   // метод чтения - пакеты

     PIPE_TYPE_MESSAGE,

     PIPE_UNLIMITED_INSTANCES,        // Бесконечно много клиентов

     SizeOf(Tpipedata), //размер буфера чтения

     SizeOf(Tpipedata), // размер буфера записи

                       100,                  // Тайм-аут

     nil);                      // Артребуты безопасности.

   if hPipe = INVALID_HANDLE_VALUE then Exit; //Если не удалось создать канал, то выходим

 

   while true do //Теперь читаем, пока не надоест!

   begin

     try

       ConnectNamedPipe(hPipe, nil); //Подключаемся к каналу, второй параметр

                                     //нужен только, если вместо PIPE_WAIT вы указали PIPE_NOWAIT

 

       //Теперь читаем, параметры – указатель на канал, наш буфер,

       //кол-во прочитанных байт, и последнее опять таки только для PIPE_NOWAIT.

       if ReadFile(hPipe, PipeData, SizeOf(TpipeData), bytesRead, nil) then

       begin

       Synchronize(ShowData); //Синхронизируемся с главным потоком

       end;

     finally

       DisconnectNamedPipe(hPipe); //Закрываем канал, параметры – только указатель

     end;

    end;

finally

end;

end;

 
Ну вот, это большая часть нашего сервера. Функцию ShowData можно сделать такую:
 

Code:

procedure TPipeThread.ShowData;

begin

Case PipeData.pEvent of

1:form1.Memo1.Lines.Add('======Событие 1======');

2:form1.Memo1.Lines.Add('=========Событие 2==========');

end;

form1.Memo1.Lines.Add('Программа '+PipeData.Clientname+' открыла канал');

form1.Memo1.Lines.Add(DateTostr(PipeData.Date));

form1.Memo1.Lines.Add('');

end;

 


 
==========КЛИЕНТ==========
Теперь пришло время написать наш клиент. Так как нам нужо только подключиться и выкинуть данные серверу, а затем сразу отключиться, то весь код клиета можно поместить в одну процедуру:

Code:

procedure SendToServer; // Замечу, что должна быть переменная такого же

                       //типа TpipeData, как и у сервера. Допустим, это Data

var

hPipe: THandle;

bytesWritten: DWORD;

begin

hPipe := CreateFile('\\.\PIPE\OurPipe', //Как видите, здесь мы подключаемся

                                         //даже не к каналу, а к файлу

   GENERIC_WRITE, //Только запись

   FILE_SHARE_READ or // Обмениваемся чтенью\записью

   FILE_SHARE_WRITE,

   nil, //Артрибуты безопасности

   OPEN_EXISTING,   // Канал должен быть создан

   0, 0);

if hPipe = INVALID_HANDLE_VALUE then Exit; //Если произошла ошибка, выходим

 

if WriteFile(hPipe, Data, SizeOf(TpipeData), bytesWritten,

   nil) then DisconnectNamedPipe(hpipe); //Если удачно запиали, закрываем канал.

end;

 

 
Исходник клиента - так же можно найти в аттаче!
Всё. Теперь вы знаете, как передовать кучу переменных своим программам из других!
 
Автор: Стас Пономарёв. Хочу поблагодарить www.sources.ru www.delphimasters.ru а также Alex-Eraser

Добавить комментарий

Не использовать не нормативную лексику.

Просьба писать ваши замечания, наблюдения и все остальное,
что поможет улучшить предоставляемую информацию на этом сайте.

ВСЕ КОММЕНТАРИИ МОДЕРИРУЮТСЯ ВРУЧНУЮ, ТАК ЧТО СПАМИТЬ БЕСПОЛЕЗНО!


Защитный код
Обновить