<?xml version='1.0' encoding="utf-8"?>
      <rss version='2.0'>
      <channel>
      <title>Форум на Исходниках.RU</title>
      <link>https://forum.sources.ru</link>
      <description>Форум на Исходниках.RU</description>
      <generator>Форум на Исходниках.RU</generator>
  	
      <item>
        <guid isPermaLink='true'>https://forum.sources.ru/index.php?showtopic=140047&amp;view=findpost&amp;p=1079327</guid>
        <pubDate>Mon, 17 Apr 2006 00:37:55 +0000</pubDate>
        <title>Создание именованых каналов.</title>
        <link>https://forum.sources.ru/index.php?showtopic=140047&amp;view=findpost&amp;p=1079327</link>
        <description><![CDATA[Borland Delphi 7: <span class='tag-size' data-value='14' style='font-size:14pt;'><strong class='tag-b'>Создание именованых каналов</strong></span>. Автор: Стас &quot;Hexorg&quot; Пономарёв.<br>
<br>
В этой статье мы будем разбирать связь двух приложений с помощью именованых каналов (named pipes). Рассмотрим типы каналов, а также создадим клиент и сервер.<br>
<br>
<strong class='tag-b'>1.</strong>Для чего именованые каналы нужны:<br>
Именовыные каналы являются средством «общения» программ. Они широко используются в UNIX-подобных системах, однако и в Windows они нашли применение. С помощью именованых каналов можно передать любую информацию, так как все каналы работают через файлы. Даже для чтения&#092;записи данных в канале используется функция WinAPI для работы с файлами – ReadFile() и  WriteFile(). Передаваться может переменная любого типа (Integer, Boolean, Tbitmap и так далее). Однако, надо заметить, что для передачи строки, она должна быть ограничена – String[40]; Иначе при чтении канала будет появляться ошибка.<br>
<br>
<strong class='tag-b'>2.</strong>Пример отправяемой переменной:<br>
Далее в статье я буду использовать отправляемую переменную типа TpipeData, этот тип будет такой:<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">type TpipeData = packed record &nbsp;//Имя можно изменить, а тип packed record оставьте</div><div class="code_line">&nbsp;&nbsp;pEvent:byte;</div><div class="code_line">&nbsp;&nbsp;ClientName: string[40]; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //Строка фиксированной длины обязательна</div><div class="code_line">&nbsp;&nbsp;Date:TdateTime;</div><div class="code_line">End;</div></ol></div></div></div></div><script>preloadCodeButtons('1');</script><br>
<br>
Как я уже сказал, здесь можно передавать любые переменные<br>
 <br>
<strong class='tag-b'>==========СЕРВЕРНАЯ ЧАСТЬ==========</strong><br>
<br>
<strong class='tag-b'>3.</strong>Использование потоков (TThread):<br>
Как такового события записи в канал нет, поэтому придётся запускать цикл чтения. Как известно, циклы подвешивают программы (а точнее зацикливают). Application.ProcessMessages при работе с функцией ReadFile не поможет, так как ReadFile будет выполнятся до тех пор, пока кто-то что-то не запишет. Поэтому надо использовать потоки (TThread). Хочу заметить, что обращение к визуальным компонентам (формам, кнопкам, лэйблам и так далее) из дополнительных потоков невозможно. Для того, чтобы полученные данные не остались в дополнительном потоке, надо его синхронизироватьс главным. Для этого есть функция TThread.Synchronize(method: TtreadMethod); Параметром этой функции надо передовать Какой-нибудь метод(нами созданный) дополнительного потока, где есть обращение к визуальным компонентам. <br>
А теперь напишем код нашего потока:<br>
<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">type</div><div class="code_line">&nbsp;&nbsp;TPipeThread = class(TThread)</div><div class="code_line">&nbsp;&nbsp;private</div><div class="code_line">&nbsp;&nbsp;PipeData:TPipeData; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;//Переменная, описывающая сдруктуру данных канала</div><div class="code_line">&nbsp;&nbsp;Protected</div><div class="code_line">&nbsp;&nbsp; &nbsp;procedure Execute; override; //Здесь мы будем создавать канал и читать данные из него</div><div class="code_line">&nbsp;&nbsp;public</div><div class="code_line">&nbsp;&nbsp; &nbsp;procedure ShowData; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;//Здесь будем передовать данные форме</div><div class="code_line">&nbsp;&nbsp; &nbsp;constructor Create(CreateSuspended: Boolean); reintroduce; overload; //Создание потока</div><div class="code_line">&nbsp;&nbsp;end;</div></ol></div></div></div></div><br>
<br>
<br>
<strong class='tag-b'>4.</strong>Работа с именоваными каналами:<br>
Напишем нашу процедуру TpipeThread.Execute; но сначала рассмотрим «настройку» наших каналов. Функция CreateNamedPipe доступна только серверу. Ей должны быть переданы следующие параметры:<br>
<span class='tag-u'><em class='tag-i'>1.</em></span>	lpName – имя нашего канала, а точнее его директория. Если сервер будет расположен на той-же машине, что и клиент, то первый параметр должен быть такой: <span class="tag-color tag-color-named" data-value="blue" style="color: blue">&#39;&#092;&#092;.&#092;PIPE&#092;ИмяКанала&#39;</span>, где имя канала – ваше название, только латинскими буквами<br>
<br>
<span class='tag-u'><em class='tag-i'>2.</em></span>	dwOpenMode – работа канала может быть:<br>
	PIPE_ACCESS_INBOUND – сервер может только читать канал<br>
	PIPE_ACCESS_OTTBOUND – сервет может только записывать<br>
	PIPE_ACCESS_DUPLEX – сервер может и писать и читать<br>
<br>
<span class='tag-u'><em class='tag-i'>3.</em></span>	dwPipeMode – режим канала здесь говорится о синхронности&#092;асинхронности канала, и о том, каким методом будетпроизводится обмен данных в канале. Значения:<br>
	PIPE_WAIT  - синхронный канал<br>
	PIPE_NOWAIT – асинхронный канал<br>
	PIPE_READMODE_BYTE – метод чтения - байты<br>
	PIPE_READMODE_MESSAGE – метод чтения пакеты<br>
	PIPE_TYPE_BYTE – тип канала - байты<br>
	PIPE_TYPE_MESSAGE  - тип канала пакеты.<br>
<br>
В dwPipeMode надо передавать три параметра, например, в нашем примере: PIPE_WAIT <strong class='tag-b'>or</strong> PIPE_READMODE_MESSAGE <strong class='tag-b'>or</strong> PIPE_TYPE_MESSAGE. А теперь, давайте вспомним наш тип TpipeData – он у нас <strong class='tag-b'>packed</strong>, то есть – запакованный - наш канал работает с пакетами.<br>
<br>
<span class='tag-u'><em class='tag-i'>4.</em></span>	hMaxInstances – максимальное число одновременных подключений – любое ваше число, однако можно указать PIPE_UNLIMITED_INSTANCES для безконечного числа клиентов.<br>
<br>
<span class='tag-u'><em class='tag-i'>5.</em></span>	nOutBufferSize – разбер буфера чтения. Можно найти так – SizeOf(TpipeData) Это как раз тип нашего буфера.<br>
<br>
<span class='tag-u'><em class='tag-i'>6.</em></span>	nInBufferSize – размер буфера записи. Обычно такой же как и буфер чтения.<br>
<br>
<span class='tag-u'><em class='tag-i'>7.</em></span>	nDefaultTimeOut – максимальное время чтения (в милисекундах)<br>
<br>
<span class='tag-u'><em class='tag-i'>8.</em></span>	lpSecurityArtributes – просто пишем nil.<br>
<br>
Также нам понядобятся фцнкции ConnectNamedPipe, ReadFile и DisconnectNamedPipe, но их параметры мы рассмотрим в процессе написания кода. И так, а теперь наша процедура:<br>
<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">procedure TPipeThread.Execute;</div><div class="code_line">var</div><div class="code_line">&nbsp;&nbsp;hPipe: THandle; //Указатель на наш канал</div><div class="code_line">&nbsp;&nbsp;bytesRead: DWORD; &nbsp;//Количество прочитанных байт</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp; hPipe := CreateNamedPipe(&#39;\.\PIPE\OurPipe&#39;, //Наше имя</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp;PIPE_ACCESS_INBOUND, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// сервер может только читать канал</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp;PIPE_WAIT or &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// Синхронная работа</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp;PIPE_READMODE_MESSAGE or &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// метод чтения - пакеты</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp;PIPE_TYPE_MESSAGE,</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp;PIPE_UNLIMITED_INSTANCES, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Бесконечно много клиентов</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp;SizeOf(Tpipedata), &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;//размер буфера чтения</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp;SizeOf(Tpipedata), &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// размер буфера записи</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp;100, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// Тайм-аут</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp;nil); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Артребуты безопасности.</div><div class="code_line">&nbsp;&nbsp; &nbsp;if hPipe = INVALID_HANDLE_VALUE then Exit; &nbsp;//Если не удалось создать канал, то выходим</div><div class="code_line">&nbsp;</div><div class="code_line">&nbsp;&nbsp; &nbsp;while true do //Теперь читаем, пока не надоест!</div><div class="code_line">&nbsp;&nbsp; &nbsp;begin</div><div class="code_line">&nbsp;&nbsp; &nbsp; try</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;ConnectNamedPipe(hPipe, nil); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //Подключаемся к каналу, второй параметр нужен только, если вместо PIPE_WAIT вы указали PIPE_NOWAIT</div><div class="code_line">&nbsp;</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;//Далее читаем, параметры – указатель на канал, наш буфер, кол-во прочитанных байт, и последнее опять таки только для PIPE_NOWAIT.</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;if ReadFile(hPipe, PipeData, SizeOf(TpipeData), bytesRead, nil) then</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Synchronize(ShowData); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //Синхронизируемся с главным потоком</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp;finally</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;DisconnectNamedPipe(hPipe); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;//Закрываем канал, параметры – только указатель</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp;end;</div><div class="code_line">&nbsp;&nbsp; &nbsp; end;</div><div class="code_line">end;</div></ol></div></div></div></div><br>
<strong class='tag-b'>5.</strong>Ну вот, это большая часть нашего сервера. Функцию ShowData можно сделать такую:<br>
<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">procedure TPipeThread.ShowData;</div><div class="code_line">begin</div><div class="code_line">Case PipeData.pEvent of</div><div class="code_line">1:form1.Memo1.Lines.Add(&#39;======Событие 1======&#39;);</div><div class="code_line">2:form1.Memo1.Lines.Add(&#39;=========Событие 2==========&#39;);</div><div class="code_line">end;</div><div class="code_line">form1.Memo1.Lines.Add(&#39;Программа &#39;+PipeData.Clientname+&#39; открыла канал&#39;);</div><div class="code_line">form1.Memo1.Lines.Add(DateTostr(PipeData.Date));</div><div class="code_line">form1.Memo1.Lines.Add(&#39;&#39;);</div><div class="code_line">end;</div></ol></div></div></div></div><br>
Полный исходник сервера вы найдёте в аттаче&#33;<br>
<br>
<strong class='tag-b'>==========КЛИЕНТ==========</strong><br>
Теперь пришло время написать наш клиент. Так как нам нужо только подключиться и выкинуть данные серверу, а затем сразу отключиться, то весь код клиета можно поместить в одну процедуру:<br>
<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">procedure SendToServer; // Замечу, что должна быть переменная такого же типа TpipeData, как и у сервера. Допустим, это Data</div><div class="code_line">var</div><div class="code_line">&nbsp;&nbsp;Data:TpipeData;</div><div class="code_line">&nbsp;&nbsp;hPipe: THandle;</div><div class="code_line">&nbsp;&nbsp;bytesWritten: DWORD;</div><div class="code_line">begin</div><div class="code_line">hPipe := CreateFile(&#39;\.\PIPE\OurPipe&#39;, &nbsp; &nbsp;//Как видите, здесь мы подключаемся даже не к каналу, а к файлу</div><div class="code_line">&nbsp;&nbsp; &nbsp;GENERIC_WRITE, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //Только запись</div><div class="code_line">&nbsp;&nbsp; &nbsp;FILE_SHARE_READ or &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Обмениваемся чтенью\записью</div><div class="code_line">&nbsp;&nbsp; &nbsp;FILE_SHARE_WRITE,</div><div class="code_line">&nbsp;&nbsp; &nbsp;nil, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //Артрибуты безопасности</div><div class="code_line">&nbsp;&nbsp; &nbsp;OPEN_EXISTING, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Канал должен быть создан</div><div class="code_line">&nbsp;&nbsp; &nbsp;0, 0);</div><div class="code_line">if hPipe = INVALID_HANDLE_VALUE then Exit; //Если произошла ошибка, выходим</div><div class="code_line">&nbsp;</div><div class="code_line">Data.pEvent:=0;</div><div class="code_line">Data.ClientName:=&#39;PipeClient&#39;;</div><div class="code_line">Data.Date:=Time; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //Функция Time записана в модуле SysUtils и возвращает текущее время</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </div><div class="code_line">if WriteFile(hPipe, Data, SizeOf(TpipeData), bytesWritten,</div><div class="code_line">&nbsp;&nbsp; &nbsp;nil) then &nbsp;DisconnectNamedPipe(hpipe); //Если удачно запиали, закрываем канал.</div><div class="code_line">end;</div></ol></div></div></div></div><br>
Исходник клиента - так же можно найти в аттаче&#33;<br>
Всё. Теперь вы знаете, как передовать кучу переменных своим программам из других&#33;<br>
<br>
Автор: Стас &quot;Hexorg&quot; Пономарёв. Хочу поблагодарить www.sources.ru www.delphimasters.ru а также Alex-Eraser]]></description>
        <author>Borland Delphi 7</author>
        <category>Системные функции, WinAPI, работа с железом</category>
      </item>
	
      </channel>
      </rss>
	