Вы находитесь на странице: 1из 27

МЕЖПРОЦЕССНОЕ ВЗАИМОДЕЙСТВИЕ (IPC)

1. Общие сведения о межпроцессном взаимодействии

IPC - InterProcess Communication).

Приложения могут использовать IPC в качестве клиентов


или серверов.

Windows поддерживает следующие средства


межпроцессного взаимодействия (IPC):

- передача данных с использованием сообщений;

- общесистемный буфер обмена (Clipboard);

- динамический обмен данными (DDE);

- проекции файлов в память (File mapping);

- каналы (Pipes);

- почтовые ячейки (Mailslots);

- удаленный вызов функций (RPC);

- сокеты Windows (Windows Sockets);

- сервисы объектов COM.


2. Передача данных с помощью сообщений

WM_SETTEXT
WM_COPYDATA.

2.1. Сообщение WM_SETTEXT


позволяет передать символьную строку, содержащую
новый текст для окна, как в этом, так и в другом процессе.
Назначение параметров сообщения следующее:
- wParam не используется;
- lParam является указателем строки, содержащей текст,
передаваемый другому окну.

В процессе-отправителе для передачи строки окну другого


процесса с использованием сообщения WM_SETTEXT:
- с помощью функции FindWindow() получить дескриптор
окна процесса-получателя;
- с помощью функции SendMessage() отправить окну
процесса-получателя сообщение WM_SETTEXT, задав в
нем в wParam значение 0, в lParam – указатель на строку,
содержащую передаваемый текст.

В процессе-получателе
В оконной процедуре окна-приемника с помощью функции
DefWindowProc() производится передача полученного
текста окну, в зависимости от его типа.

Пример 1 изменения заголовка главного окна приложения


AppReceiver.exe с именем "WndRcv" путем посылки
сообщения WM_SETTEXT с новым заголовком

// Получение дескриптора окна-приемника
HWND hWndRcv = FindWindow("WndRcv", NULL);
if(!hWndRcv)
{
MessageBox(hWnd, "Окно WndRcv не найдено", "Info",
MB_OK); return 0;
}
// Изменение заголовка окна процесса-получателя
SendMessage(hWndRcv, WM_SETTEXT, (WPARAM) 0,
(LPARAM) "New title of WndRcv");

2.2. Сообщение WM_COPYDATA
позволяет передавать набор данных произвольного типа
между окнами разных процессов. Назначение параметров
сообщения:
- wParam представляет собой описатель окна,
посылающего данное сообщение;
- lParam является указателем на структуру типа
COPYDATASTRUCT, которая содержит данные,
передаваемые другому процессу.

Структура COPYDATASTRUCT (winuser.h):

typedef struct tagCOPYDATASTRUCT {


ULONG_PTR dwData;
DWORD cbData;
PVOID lpData;
} COPYDATASTRUCT, *PCOPYDATASTRUCT;

Назначение полей структуры:


- dwData может содержать дополнительную информацию,
например тип передаваемых данных;
- cbData задает размер блока передаваемых данных в
байтах;
- lpData является указателем на блок передаваемых
данных.

В процессе-отправителе для передачи данных другому


процессу c использованием сообщения WM_COPYDATA:
- с помощью функции FindWindow() получить дескриптор
окна процесса-получателя;
- описать структуру типа COPYDATASTRUCT, заполнить её
поля, указав размеры и адрес передаваемого блока
данных;
- с помощью функции SendMessage() отправить окну
процесса-получателя сообщение WM_COPYDATA, задав в
wParam дескриптор окна–отправителя, в lParam - указатель
на структуру типа COPYDATASTRUCT.

В процессе-получателе для приема данных: в


соответствующей оконной процедуре предусмотреть
обработку сообщения WM_COPYDATA: скопировать
полученные данные в локальный буфер (или в файл) для
последующей работы с ними и выйти из оконной
процедуры, задав в операторе return значение TRUE.

Пример 2 передачи с использованием сообщения


WM_COPYDATA массива из 10000 случайных целых чисел
между приложением-отправителем AppSender.exe и
приложением-получателем AppReceiver.exe с главным
окном "WndRcv".
а) фрагмент исходного кода приложения-отправителя
AppSender.exe:
Файл AppSender.cpp

int iSendArray[10000]; // Пересылаемый массив
// Получение дескриптора окна-приемника
HWND hWndRcv = FindWindow("WndRcv", NULL);
if(!hWndRcv)
{
MessageBox(hWnd, "Окно WndRcv не найдено", "Info",
MB_OK); return 0;
}

// Заполнение массива случайными целыми числами


for(int i=0; i<l0000; i++)
iSendArray[i] = rand();

// Передача данных в другой процесс


COPYDATASTRUCT CDS;
CDS.dwData = 0;
CDS.cbData = sizeof(iSendArray);
CDS.lpData = (LPVOID) iSendArray;
SendMessage(hWndRcv, WM_COPYDATA, (WPARAM) hWnd,
(LPARAM) &CDS);

б) фрагмент исходного кода приложения-получателя Ap-


pReceiver.exe:
Файл AppReceiver.cpp
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg,
WPARAM wParam, LPARAM lParam)
{
COPYDATASTRUCT *pCDS;
DWORD dwCount;
HANDLE hFile;
// Обработка сообщения для окна
switch (uMsg)
{
// Обработка сообщения WM_COPYDATA
case WM_COPYDATA:
// Получение адреса стр-ры COPYDATASTRUCT
pCDS = (COPYDATASTRUCT *) lParam;

// Создание файла
hFile = CreateFile("iArray.dat", GENERIC_
WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);

// Запись данных в файл


WriteFile(hFile, pCDS->lpData, pCDS->cbData,
&dwCount, NULL);
CloseHandle(hFile); // Закрытие файла

// Возврат TRUE для функции SendMessage()


return TRUE;

// Обработка других сообщений

// Уничтожение окна
case WM_DESTROY:
PostQuitMessage (0);
return 0;
}
return DefWindowProc (hWnd, uMsg, wParam, lParam);
}
3. Буфер обмена (Clipboard)
- набор функций и сообщений Windows, который позволяет
сохранять данные в памяти и передавать их между окнами
одного или разных приложений.

Основные операции с буфером обмена:


а) открытие буфера обмена;
б) передача данных в буфер обмена;
в) получение данных из буфера обмена;
г) закрытие буфера обмена.

3.1. Форматы данных для буфера обмена


а) стандартные форматы;
б) регистрируемые форматы;
в) частные форматы.

Для идентификации форматов используют их номера,


заданные целыми значениями

Стандартные форматы данных буфера обмена (winuser.h)

Наиболее часто используемые:

- CF_TEXT – группа символов из кодовой таблицы ASCII


заканчивающаяся нуль-символом, в конце каждой строки
которой имеются символы возврата каретки и перевода
строки;

- CF_BITMAP – зависящий от устройства битовый образ,


который передается в буфер обмена с помощью описателя
битового образа HBITMAP;

- CF_ENHMETAFILE – формат для расширенного


метафайла, содержащего несколько новых функций,
структур данных и новое расширение файла - EMF по
сравнению со стандартным WMF-метафайлом.
Регистрируемые форматы буфера обмена
идентификаторы в диапазоне от 0xC000 до 0xFFFF.

Для регистрации нового формата буфера обмена -


RegisterClipboardFormat().

позволяет нескольким приложениям передавать данные


через буфер обмена, регистрируя формат с одинаковым
именем и получая одинаковый его номер.

Частные форматы данных буфера обмена

В winuser.h зарезервированы 2 набора номеров частных


форматов:

- от CF_PRIVATEFIRST (0x0200) до CF_PRIVATELAST


(0x02FF).

- от CF_GDIOBJFIRST (0x0300) до CF_GDIOBJLAST


(0x03FF) для объектов типа GDI (Graphics Device Interface).

Приложения могут передавать через буфер обмена


собственные данные, используя частные форматы с
одинаковыми номерами из указанных диапазонов.

3.2. Открытие, очистка и закрытие буфера обмена


В любой момент времени только одно окно какой-либо
программы может быть владельцем буфера обмена и
держать его открытым. При этом можно передавать данные
в буфер обмена или получать их из него. После чего буфер
обмена должен быть закрыт, чтобы с ним могли работать
другие окна.

Открытие буфера обмена выполняет следующая функция:


BOOL OpenClipboard(HWND hWndNewOwner);
hWndNewOwner - описатель окна, которое станет новым
владельцем буфера обмена. Если этот параметр равен
NULL, то владельцем буфера обмена будет текущий
процесс, а не одно из его окон.
Окно с описателем hWndNewOwner становится владельцем
буфера обмена после того, как вызовет функцию Empty-
Clipboard(), которая производит его очистку.
Функция возвращает значение: TRUE, если буфер обмена
успешно открыт, или FALSE, когда буфером обмена
владеет другое окно и держит его открытым.
Очистка содержимого буфера обмена:
BOOL EmptyClipboard(VOID);
Она не имеет параметров и возвращает значение TRUE,
если буфер обмена успешно очищен, или FALSE, в
противном случае.
При выполнении в памяти удаляются все объекты данных,
находящиеся в буфере обмена и назначается владельцем
окно, открывшее его.

Закрытие буфера обмена выполняется, если он ранее был


успешно открыт в данной программе, с помощью функции:
BOOL CloseClipboard(VOID);

Функция возвращает значение TRUE, если буфер обмена


успешно закрыт, или FALSE, в противном случае. После
закрытия буфера обмена окно уже не может помещать в
него данные.

Пример открытия, очистки и закрытия буфера обмена


окном с описателем hWnd:
if (!OpenClipboard(hWnd))
return FALSE;
EmptyClipboard();
… // Передача данных в буфер обмена
CloseClipboard();
3.3. Передача и получение данных через буфер обмена
После открытия и очистки буфера обмена окно становится
его владельцем. Затем окно может поместить в буфер
обмена несколько объектов данных с различными
доступными для этого окна форматами, а другое окно этого
или иного приложения получить необходимые ему данные
из буфера обмена.

Передача в буфер обмена данных заданного формата:

HANDLE SetClipboardData(UINT uFormat, HANDLE hMem);

Параметры:
- uFormat задает формат данных, передаваемых в буфер
обмена.
- hMem является описателем блока памяти, где размещены
передаваемые данные в указанном формате. Если он
равен NULL, то данные будут переданы в буфер обмена
позже, по запросу окна-получателя (отложенная передача
данных буферу обмена).
Блок памяти для данных должен быть выделен в куче
функцией GlobalAlloc() c флагом GHND, комбинирующим
флаги GMEM_MOVEABLE и GMEM_ZEROINIT.

Функция SetClipboardData() возвращает описатель данных,


помещенных в буфер обмена или значение NULL в случае
невозможности передачи данных.

Получение из буфера обмена данных заданного формата:


HANDLE GetClipboardData(UINT uFormat);

Параметр uFormat задает требуемый формат данных в


буфере обмена.

Функция возвращает описатель в буфере обмена для


объекта данных в требуемом формате. Если данных в
таком формате нет, то возвращается значение NULL.

Приложению необходимо сразу же скопировать эти данные


в своё адресное пространство или отобразить их в окне.
3.4. Отложенная передача данных буферу обмена
При передаче в буфер обмена объектов данных больших
размеров может использоваться отложенное исполнение
(delayed rendering) передачи. При этом данные не
передаются буферу обмена до тех пор, пока они не
потребуются другой программе.

Потребность в передаче данных буферу обмена возникнет,


когда в приложении-получателе будет вызвана функция
GetClipboardData() для соответствующего формата.
При этом ОС посылает окну-владельцу буфера обмена 2
сообщения:

- WM_RENDERFORMAT, поле wParam указывает формат


данных, которые должны быть переданы;

- WM_RENDERALLFORMATS перед закрытием окна-


владельца буфера обмена, чтобы оно передало в буфер
обмена все отложенные данные.

Для реализации отложенной передачи данных буферу


обмена в приложении-отправителе необходимо:
1) при вызовах функции SetClipboardData() для первого
параметра uFormat указывать конкретный формат
передаваемых данных, а для второго параметра hMem
задавать значение NULL.

Пример отложенной передачи буферу обмена текстовых


данных и битового образа:
OpenClipboard(hWnd);
EmptyClipboard();
SetClipboardData(CF_TEXT, NULL);
SetClipboardData(CF_BITMAP, NULL);
CloseClipboard();

2) при обработке сообщения WM_RENDERFORMAT


получить описатель области памяти с передаваемыми
данными в формате типа wParam и, не открывая буфер
обмена, вызвать функцию SetClipboardData(), задав ей эти
формат и описатель области памяти.

3) при обработке сообщения WM_RENDERALLFORMATS


открыть буфер обмена, очистить его, поместить данные в
соответствующие области памяти и вызвать для каждого
формата функцию SetClipboardData() с заданием ей
описателя области памяти, а затем закрыть буфер обмена.
Для уменьшения объема программного кода и устранения
дублирования его частей можно совместить обработку
сообщений WM_RENDERALLFORMATS и
WM_RENDERFORMAT:

case WM_RENDERALLFORMATS:
OpenClipboard(hWnd);
EmptyClipboard();
case WM_RENDERFORMAT:
if((wParam == CF_TEXT) || (uMsg == WM_RENDERALL-
FORMATS))
{
… // Копирование текста в область памяти
// с описателем hGMemText
SetClipboardData (CF_TEXT, hGMemText);
}
if((wParam == CF_BITMAP) || (uMsg == WM_RENDERALL-
FORMATS))
{
… // Загрузка в память битового образа и
// получение его описателя hBitmap
SetClipboardData (CF_BITMAP, hBitmap);
}
if(uMsg == WM_RENDERALLFORMATS)
CloseClipboard();
return 0;
3.5. Уведомление об изменениях буфера обмена

ОС Windows обеспечивает уведомление одного или


нескольких окон об изменении содержимого буфера обмена
путем посылки сообщений их оконным процедурам. Такие
окна называются окнами-наблюдателями за буфером
обмена (Clipboard Viewer).

Одновременно может быть несколько окон-наблюдателей


за буфером обмена, из них только одно окно, которое
зарегистрировалось последним, называется текущим
наблюдателем за буфером обмена (Current Clipboard
Viewer). Этому окну ОС посылает сообщение при
изменении содержимого буфера обмена.

Несколько окон, которым необходимо получать


уведомления от буфера обмена, образуют цепочку окон-
наблюдателей за буфером обмена (Clipboard Viewer Chain).
При этом все окна цепочки по очереди получают
сообщения, которые Windows отправляет текущему окну-
наблюдателю.

При закрытии окна или при ненадобности окно должно


удалить себя из цепочки окон просмотра буфера обмена.

Для организации наблюдения за буфером обмена в


приложении необходимо:
- добавить своё окно в цепочку наблюдателей за буфером
обмена;

- обрабатывать сообщение WM_DRAWCLIPBOARD при


изменении содержимого буфера обмена;

- удалить окно из цепочки наблюдателей за буфером


обмена до уничтожения этого окна;

- обрабатывать сообщение WM_CHANGECBCHAIN при


удалении другого окна из цепочки наблюдателей за
буфером обмена.

3.5.1.Добавление окна в цепочку наблюдателей за


буфером обмена
HWND SetClipboardViewer(HWND hWndNewViewer);

Параметр hWndNewViewer является описателем окна,


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

3.5.2. Обработка сообщения WM_DRAWCLIPBOARD:

- с помощью функции SendMessage() передать это


сообщение следующему окну в цепочке наблюдателей за
буфером обмена. Если это последнее окно цепочки, для
которого значение hWndNextViewer =NULL, то пересылка
сообщения не производится;

- определить наличие в буфере обмена данных в требуемом


формате, извлечь их и далее либо отобразить в окне, либо
обработать.

3.5.3. Удаление окна из цепочки наблюдателей за буфером


обмена:

BOOL ChangeClipboardChain(HWND hWndRemove,


HWND hWndNewNext);

hWndRemove - описатель окна, удаляемого из цепочки


hWndNewNext - описатель следующего окна просмотра
буфера обмена.
Функция возвращает значение FALSE, когда окно успешно
удалено из цепочки наблюдателей за буфером обмена, и
значение TRUE, если в цепочке присутствует только одно
окно и оно удалено.

Когда программа вызывает функцию


ChangeClipboardChain(), Windows посылает сообщение
WM_CHANGECBCHAIN текущему окну просмотра буфера
обмена.
wParam этого сообщения - описатель окна, удаляемого из
цепочки, а lParam – описатель следующего в цепочке окна
просмотра буфера обмена.

Удалить окно из цепочки наблюдателей за буфером обмена


можно в любом месте программы, но чаще всего это
выполняется при обработке сообщения WM_DESTROY.

3.5.4. Обработка сообщения WM_CHANGECBCHAIN


Окно просмотра буфера обмена получает это сообщение,
когда другое окно удаляет себя из цепочки наблюдателей
за буфером обмена путем вызова функции
ChangeClipboardChain().

Если удаляемое окно – следующее в цепочке, то окно,


получающее сообщение должно удалить его из цепочки и
запомнить описатель окна, следующего за удаленным
окном. Иначе, сообщение должно быть послано
следующему окну в цепочке.

Пример
4. Обмен данными между процессами через проекцию
страничного файла

Для создания и использования проекции страничного


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

1. С помощью функции CreateFileMapping() создать в


памяти именованный объект "проекция файла". При этом
параметры функции должны получить следующие
значения:
- hFile - значение INVALID_HANDLE_VALUE (равное -1).
Это является признаком того, что будет создана проекция
страничного файла, а не открытого файла на диске;

- flProtect - атрибут защиты страниц физической памяти


(PAGE_READONLY, PAGE_READWRITE или
PAGE_WRITECOPY);

- dwMaximumSizeHigh и dwMaximumSizeLow - объем


выделяемой для проекции памяти, обязательно больший
нуля;

- lpName - имя проекции в виде строки. Это имя будет


использоваться другими процессами для открытия данной
проекции.

Функция возвращает дескриптор объекта "проекция файла"


hMapPageFile, который используется в том же процессе для
взаимодействия с объектом.

2. Отобразить проекцию файла на своё адресное


пространство с помощью функции MapViewOfFile(), которая
возвращает указатель lpMapData на созданный регион
памяти с отображением проекции файла, и явно
преобразовывать его в требуемый тип.

3. Работать с общими данными, находящимися в проекции


страничного файла через разыменование указателя
*lpMapDatalpMapData.

4. По окончании работы с этими данными процесс должен,


используя функцию UnmapViewOfFile(), сделать
недействительным указатель lpMapData и освободить
соответствующий регион памяти в своем адресном
пространстве. Затем с помощью функции CloseHandle()
освободить дескриптор проекции hMapPageFile.

В других процессах, которые хотят получить доступ к уже


созданной проекции страничного файла можно открыть
имеющуюся проекцию с помощью функции
OpenFileMapping(), задав ей имя этой проекции.
Функция возвращает дескриптор объекта "проекция файла"
hMapPageFile для этого процесса.

По окончании работы с проекцией, когда во всех


использующих её процессах с помощью функции
CloseHandle() будут закрыты описатели этого объекта,
система освободит память, переданную из страничного
файла.
При этом данные, которые там размещались, не
сохраняются на диске.

Кроме того, когда хотя бы одним процессом производится


запись данных в проекцию, то обязательно потребуется
использование какого-либо именованного объекта
синхронизации.

Пример
5. Использование каналов для передачи данных
между процессами
Канал, труба (Pipe) - это участок разделяемой памяти,
который используется для коммуникации между
приложениями.

Процесс, который создает канал – это сервер канала, а


процесс, который присоединяется к каналу – это клиент.

Имеется два вида каналов для межпроцессной связи:


- анонимные каналы
- именованные каналы.

5.1. Анонимные каналы (Anonymous pipes)

Обычно, анонимный канал используется для


перенаправления стандартного ввода или вывода
дочернего процесса таким образом, что он может
обмениваться данными со своим родительским процессом.

Для обмена данными в обоих направлениях (дуплексная


передача) необходимо создать два анонимных канала.

Анонимные каналы не могут использоваться в сети, также


не могут они использоваться и между не связанными
процессами.

5.1.1. Функции для работы с анонимными каналами

Создает анонимный канал родительский процесс с


помощью функции:

BOOL CreatePipe(PHANDLE phReadPipe, PHANDLE ph-


WritePipe, LPSECURITY_ATTRIBUTES lpPi-
peAttributes, DWORD nSize);
Параметры функции:
- phReadPipe, phWritePipe указывают на переменные,
которые получают значения дескрипторов чтения и записи
канала;
- lpPipeAttributes указывает на структуру типа SECU-
RITY_ATTRIBUTES, которая определяет права доступа к
каналу и наследуемость его дескрипторов. Член bIn-
heritHandle этой структуры должен быть равным TRUE,
тогда дескрипторы будут наследуемы;
- nSize указывает размер буфера для канала, в байтах.
Если он равен 0, то используется размер буфера по
умолчанию.

Получение дескриптора устройства стандартного ввода,


вывода и вывода сообщений об ошибках выполняется с
помощью функции:

HANDLE GetStdHandle(DWORD nStdHandle);

nStdHandle задает вид стандартного устройства для


получения дескриптора и может принимать следующие
значения:
- STD_INPUT_HANDLE для получения дескриптора
устройства стандартного ввода;
- STD_OUTPUT_HANDLE для получения дескриптора
устройства стандартного вывода;
- STD_ERROR_HANDLE для получения дескриптора
стандартного устройства для вывода сообщений об
ошибках.

При успешном завершении функция GetStdHandle()


возвращает дескриптор указанного устройства, иначе
возвращается значение INVALID_HANDLE_VALUE.

Выполнение операций чтения и записи данных с каналом.

Для получения данных из канала используется дескриптор


чтения канала при вызове функции ReadFile().

Вызов ReadFile() завершается успешно, когда другой


процесс записал информацию в канал. Если дескриптор
записи с другого конца канала закрыт, то функция
ReadFile() возвращает значение FALSE.

Для передачи данных в канал используется дескриптор


записи канала при вызове функции WriteFile().
Вызов WriteFile() не завершается, пока не будет записано
указанное число байтов в канал.
Функция WriteFile() возвращает значение FALSE, когда
производится попытка записи в канал при закрытом
дескрипторе чтения из канала с другой его стороны.
Закрытие дескриптора канала сервером или клиентом
производится с помощью функции CloseHandle().
5.1.2. Однонаправленная передача данных между
процессами с использованием анонимного канала

После создания анонимного канала родительский процесс


должен сообщить дочернему процессу один из
наследуемых дескрипторов канала для чтения или записи.
Это производится путем перенаправления стандартного
ввода или вывода дочернего процесса на канал с
использованием структуры STARTUPINFO.

Родительский процесс должен выполнить следующие


действия:
- создать с помощью функции CreatePipe() анонимный
канал, получив при этом наследуемые дескрипторы для
чтения и записи данных с разных концов канала;

- перенаправить стандартный ввод или вывод дочернего


процесса на канал, для чего необходимо очистить структуру
STARTUPINFO и заполнить:
hStdInput, hStdOutput, hStdError и dwFlags.

Одному из членов этой структуры (hStdInput или hStdOutput)


присваивается дескриптор канала для чтения или записи.

Остальные члены структуры STARTUPINFO получают


стандартные дескрипторы с помощью функции
GetStdHandle(),
а dwFlags – значение STARTF_USESTDHANDLES;

- создать дочерний процесс с помощью функции


CreateProcess(), передав ей адрес структуры STAR-
TUPINFO и задать bInheritHandles значение TRUE;

- закрыть с помощью функции CloseHandle() дескриптор


канала, переданный дочернему процессу;

- выполнять операции записи или чтения данных с каналом


вызывая функции WriteFile() или ReadFile();

- по окончании работы с каналом закрыть его дескриптор с


помощью функции CloseHandle().
Дочерний процесс должен выполнить следующие действия:
- с помощью функции GetStdHandle() получить дескриптор
чтения или записи канала, переданный родительским
процессом;

- выполнять операции чтения или записи данных с каналом


вызывая функции ReadFile() или WriteFile() с
использованием полученного ранее дескриптора канала;

- по окончании работы с каналом закрыть его дескриптор с


помощью функции CloseHandle().

Пример
5.2. Именованные каналы (Named pipes)
используются для двухсторонней передачи данных между
процессами, которые не связаны, а также между
процессами на различных компьютерах.

Имена таких каналов UNC-имена (Universal Naming Conven-


tion) назначают по схеме:
\\имя_машины\ріре\имя_канала

Для доступа к каналу на локальном компьютере вместо


имени машины используется символ '.':

\\.\ріре\имя_канала.

5.2.1. Функции для работы с именованными каналами


Создание именованного канала производится на локальном
компьютере с помощью функции:

HANDLE CreateNamedPipe(LPCTSTR lpName,


DWORD dwOpenMode, DWORD dwPipeMode,
DWORD nMaxInstances, DWORD nOutBufferSize,
DWORD nInBufferSize, DWORD nDefaultTimeOut,
LPSECURITY_ATTRIBUTES lpSecurityAttributes);

Параметры функции:
- lpName обозначает имя канала в форме: \\.\ріре\
имя_канала. Создавать именованные каналы на
удаленных машинах нельзя;

- dwOpenMode задает режим открытия канала:


PIPE_ACCESS_DUPLEX – двухсторонняя (дуплексная)
передача данных,
PIPE_ACCESS_INBOUND – передача от клиента к серверу,
PIPE_ACCESS_OUTBOUND – передача от сервера к
клиенту и др.;

- dwPipeMode задает режим обмена данными с каналом:


PIPE_TYPE_BYTE, PIPE_READMODE_BYTE – побайтная
запись и чтение данных,
PIPE_WAIT – ожидание освобождения канала или
подключения клиента и др. Если он равен 0, то эти режимы
будут по умолчанию;
- nMaxInstances задает максимальное количество
экземпляров канала (клиентских соединений).
Неограниченное количество:
PIPE_UNLIMITED_INSTANCES;

- nOutBufferSize, nInBufferSize задают размеры в байтах


буферов ввода и вывода для канала. Если указать нули,
будут использоваться значения по умолчанию

- nDefaultTimeOut определяет время ожидания клиентом


доступа к каналу:
по умолчанию –NMPWAIT_USE_DEFAULT_WAIT,
без ограничений – NMPWAIT_WAIT_FOREVER;

- lpSecurityAttributes указывает на структуру типа SECU-


RITY_ATTRIBUTES.

Функция CreateNamedPipe() при успешном выполнении


возвращает дескриптор для сервера канала, иначе –
значение INVALID_HANDLE_VALUE.
Для ожидания сервером подключения клиента к
именованному каналу используется функция:

BOOL ConnectNamedPipe(HANDLE hNamedPipe,


LPOVERLAPPED lpOverlapped);

Параметр hNamedPipe является дескриптором сервера


канала, полученный при вызове функции CreateNamed-
Pipe().

Параметр lpOverlapped указывает на структуру типа


OVERLAPPED, которая содержит информацию для
асинхронного ввода-вывода. Используется, если при
создании канала был разрешен режим асинхронного ввода-
вывода, иначе он получает значение NULL.

После подключения клиента к каналу функция завершает


ожидание и возвращает TRUE. Если клиент подключается
до вызова функции, то она возвращает FALSE.

Отключение сервером экземпляра канала от клиента


производится с помощью функции:

BOOL DisconnectNamedPipe(HANDLE hNamedPipe);


Сервер канала вызывает эту функцию, чтобы
освободить дескриптор (экземпляр канала) hNamedPipe
для соединения с другим клиентом.
При успешном отключении функция возвращает TRUE,
иначе - FALSE.

Ожидание клиентом освобождения экземпляра канала на


сервере производится с помощью функции:

BOOL WaitNamedPipe(LPCTSTR lpNamedPipeName,


DWORD nTimeOut);

lpNamedPipeName указывает на строку, содержащую имя


канала в виде: \\имя_сервера\ріре\имя_канала.

nTimeOut задает время ожидания в мсек.


Неограниченное время ожидания задается значением
NMPWAIT_WAIT_FOREVER.

Если канал стал доступен клиенту до истечения времени


ожидания, функция возвращает TRUE, иначе - FALSE.

Установление соединения клиента с каналом производится


с помощью функции:

HANDLE CreateFile(LPCTSTR lpNamedPipeName, …);

В первом параметре указывается имя канала в виде: \\


имя_сервера\ріре\имя_канала.

Остальные параметры задаются, как и для дисковых


файлов.

Функция возвращает дескриптор канала для клиента. В


случае неудачной попытки соединения возвращается
значение INVALID_HANDLE_VALUE.
Операции чтения и записи данных с каналом выполняются
сервером и клиентом с помощью своих дескрипторов
канала путем вызова функций ReadFile() и WriteFile().

Закрытие дескрипторов канала на стороне сервера и


клиента производится с помощью функции CloseHandle().
5.2.2. Однонаправленная передача данных между
процессами с использованием именованного канала.

5.2.3. Двухсторонняя передача данных между процессами с


использованием именованного канала.
6. Использование почтовых ячеек для передачи данных
между процессами

Почтовые ячейки (Mailslots) имеют имена и обеспечивают


одностороннюю (симплексную) связь.

Процесс, который создает почтовую ячейку, называется


mailslot-сервером.

Другие процессы, называемые mailslot-клиентами,


отправляют сообщения (данные) mailslot-серверу, в его
почтовую ячейку.

Поступающие сообщения сохраняются в почтовой ячейке,


пока mailslot-сервер не прочитает их.

Клиент почтовой ячейки может отправить mailslot-


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

Сообщения (данные), передаваемые на все почтовые


ячейки в сети, могут быть не больше, чем 400 байтов, тогда
как сообщения, отправляемые отдельной почтовой ячейке,
ограничены только максимальным размером сообщения,
указанным mailslot-сервером при создании этой ячейки.

6.1. Функции для работы с почтовыми ячейками


Создание почтовой ячейки.
На отдельной машине может быть только одна почтовая
ячейка с данным именем, однако несколько компьютеров в
сети могут использовать одно и то же имя почтовой ячейки,
что позволяет иметь нескольких получателей сообщений.

HANDLE CreateMailslot(LPCTSTR lpName,


DWORD nMaxMessageSize, DWORD lReadTimeout,
LPSECURITY_ATTRIBUTES lpSecurityAttributes);

Параметры функции:
- lpName указывает на строку, содержащую имя почтовой
ячейки, создаваемой на локальном компьютере в виде: \\.\
mailslot\имя_ячейки;
- nMaxMessageSize задает максимальный размер (в байтах)
сообщений, которые может отправлять клиент. При значении
0 ограничения на размер сообщений нет;

- lReadTimeout определяет длительность (в миллисекундах)


ожидания операции чтения. При значении 0 произойдет
немедленный возврат, a при MAILSLOT_WAIT_FOREVER —
бесконечное ожидание;

- lpSecurityAttributes указывает на структуру типа SECU-


RITY_ATTRIBUTES. Обычно этому параметру задают
значение NULL.

Функция CreateMailslot() при успешном выполнении


возвращает дескриптор для сервера почтовой ячейки,
иначе – значение INVALID_HANDLE_VALUE.

Чтение сервером сообщения из почтовой ячейки


производится с помощью функции:

BOOL ReadFile(HANDLE hMailslot, …);

Параметр hMailslot указывает дескриптор почтовой ячейки,


полученный функцией CreateMailslot().
Остальные параметры задаются, как и при чтении из
дисковых файлов.

Функция ReadFile() при успешном чтении сообщения


возвращает TRUE и FALSE – когда выполнена попытка
чтения из почтовой ячейки, буфер которой недостаточен
для размещения сообщения.

Открытие клиентом почтовой ячейки производится с


помощью функции:

HANDLE CreateFile(LPCTSTR lpMailslotName, …);

В первом параметре можно использовать следующие


формы задания имени почтовой ячейки:
а) \\.\mailslot\имя_ячейки, когда сервер и клиент
расположены на одном компьютере;
б) \\имя_сервера\mailslot\имя_ячейки, когда сервер и
клиент расположены на разных компьютерах в сети;
в) \\*\mailslot\имя_ячейки, когда необходимо посылать по
сети сообщения всем серверам почтовых ячеек, имеющих
данное имя. В этом случае максимальная длина
сообщения составляет 400 байт.

Остальные параметры функции CreateFile() задаются, как и


для дисковых файлов.

Функция возвращает дескриптор почтовой ячейки для


клиента. В случае неудачной попытки открытия
возвращается значение INVALID_HANDLE_VALUE.

Запись клиентом сообщения в почтовую ячейку


производится с помощью функции:

BOOL WriteFile(HANDLE hMailslot, …);

Параметр hMailslot указывает дескриптор почтовой ячейки,


полученный функцией CreateFile().
Остальные параметры задаются, как и при записи на
дисковые файлы.

Функция WriteFile() при успешной записи сообщения


возвращает TRUE и FALSE – в противном случае.

Закрытие дескрипторов почтовой ячейки на стороне


сервера и клиента производится с помощью функции
CloseHandle().

6.2. Однонаправленная передача данных между


процессами с использованием почтовой ячейки.