Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

OC Windows & OC Linux. Лабораторные работы по курсу «Операционные системы»

.pdf
Скачиваний:
0
Добавлен:
06.09.2026
Размер:
2 Мб
Скачать
HSZ hsz1, // идентификатор строки имени
сервиса HSZ hsz2, // зарезервировано UINT afCmd); // флаги
Через параметр idInst приложение должно передать идентифи-
катор, полученный на этапе регистрации приложения в библиотеке DDEML функцией DdeInitialize.
Параметр hsz1 предназначен для передачи имени сервиса че-
рез идентификатор текстовой строки, возвращенной функцией
DdeCreateStringHandle.
Параметр hsz2 зарезервирован, для него следует использовать
нулевое значение.
При регистрации сервиса через параметр afCmd следует
дать значение DNS_REGISTER (регистрация сервиса). Сервер DDEML в процессе своей работы может динамически регистрировать и от­менять виды предоставляемого сервиса. Для отмены сервиса через параметр afCmd передается значение DNS_UNREGISTER.
Перед завершением работы сервер DDEML должен отме-
нить весь зарегистрированный им ранее сервис, вызвав функ­цию DdeInitialize с параметром afCmd, имеющим значение
DNS_UNREGISTER.
Если регистрация сервиса выполнена успешно
DdeNameService возвращает ненулевое значение, а при ошибке
нулевое.
Приведем фрагмент кода, выполняющего регистрацию сервиса
«BMPServer»:
hszService =
DdeCreateStringHandle (idInst, "BMPServer", СР_WINANSI); DdeNameService(idInst,hszService, (HSZ)NULL, DNS_REGISTER);
Одновременно с регистрацией сервиса сервер обычно создает
идентификаторы текстовых строк, содержащих имена используемых разделов и элементов данных. Для этого вызывается все та же функ­ция DdeCreateStringHandle:
hszTopic =
DdeCreateStringHandle(idInst,szTopic,CP_WINANSI);
71
пере-
, функция
hszItem =
DdeCreateStringHandle(idInst,szItem,P_WINANSI);
Отметим, что регистрацию сервиса выполняет только сервер
DDEML. Что же касается создания идентификаторов текстовых
строк функцией DdeCreateStringHandle, то эта операция выполняется как сервером, так и клиентом. Полученные идентификаторы исполь­зуются при создании канала и в процессе передачи данных.
Зная идентификатор строки, приложение может получить стро-
ку, вызвав функцию DdeQueryString:
DWORD WINAPI DdeQueryString( DWORD idInst, // идентификатор приложения HSZ hsz, // идентификатор строки LPSTR psz, // адрес буфера для записи строки DWORD cchMax, // размер буфера int iCodePage); // кодовая страница
Назначение параметров понятно из комментариев в прототипе
функции.
Если идентификатор созданной текстовой строки используется
в функции обратного вызова (которую мы рассмотрим чуть позже), за освобождение ресурсов, связанных с текстовой строкой, отвечает система DDEML. В противном случае приложение должно само стоятельно уничтожать созданные им идентификаторы, вызывая функцию DdeFreeStringHandle:
BOOL WINAPI DdeFreeStringHandle( DWORD idInst, // иден-
тификатор приложения
HSZ hsz); // иден-
тификатор уничтожаемой строки
В случае успеха функция DdeFreeStringHandle возвращает не-
нулевое значение, при ошибке – нулевое.
1.2.3. Функция обратного вызова DDEML
Когда сервер или клиент регистрирует себя в библиотеке
DDEML при помощи функции DdeInitialize, он указывает адрес пе­реходника, созданного для функции
обратного вызова. Функция об-
72
-
ратного вызова предназначена для обработки всех событий, возни­кающих в процессе создания каналов связи и передачи данных.
В простейшем случае функция обратного вызова сервера
DDEML может выглядеть следующим образом:
HDDEDATA EXPENTRY _export DDEServerCallback( WORD wType, // код транзакции WORD wFmt, // формат данных HCONV hConv, // идентификатор канала HSZ hsz1, // первый идентификатор строки HSZ hsz2, // второй идентификатор строки HDDEDATA hData, // идентификатор глобальной
области данных
DWORD dwDatal, // первое
двойное слово
DWORD dwData2) // второе дополнительное
двойное слово
{ switch(wType) { // Создание канала передачи данных case XTYP_CONNECT: {
...........
return((HDDEDATA)TRUE); }
// Запрос данных от сервера case XTYP_REQUEST:
{
...........
return(hData); } // Запрос на выполнение команды case XTYP_EXECUTE: {
...........
break; }
73
дополнительное
// Передача данных серверу case XTYP_РОКЕ: {
...........
return((HDDEDATA)DDE_FACK); } // Подтверждение создания канала case XTYP_CONNECT_CONFIRM: {
...........
break; } // Завершение работы канала case XTYP_DISCONNECT: {
...........
break; } // Ошибка case XTYP_ERROR: {
...........
break; } } return((HDDEDATA)NULL); }
Функция обратного вызова должна быть определена как экс-
портируемая, поэтому мы указали ключевое слово _export.
Через первый параметр wType передается код транзакции. Подобно функции окна, которая обрабатывает сообщения, функ-
ция обратного вызова DDEML выполняет обработку В зависимости от кода транзакции и результата обработки функция обратного вызова DDEML возвращает то или иное значение.
Второй параметр задает код формата передаваемых данных.
Для кодов формата используются те же значения, что и для форма­тов Clipboard, например СР_ТЕХТ.
транзакций.
74
Через параметр hConv передается идентификатор канала пере-
дачи данных (который мы еще не научились создавать).
Назначение остальных параметров функции обратного вызова
зависит от кода транзакции. В приведенном выше фрагменте кода используются символические имена кодов транзакций, определен­ные в файле ddеml.h и имеющие префикс имени XTYP.
Функция обратного вызова для клиента DDEML выглядит точ-
но
так же, отличаясь лишь составом обрабатываемых транзакций:
HDDEDATA EXPENTRY DDEClientCallback( WORD wType, WORD wFmt, HCONV hConv, HSZ hsz1, HSZ
hsz2, HDDEDATA hData, DWORD dwDatal, DWORD dwData2)
{ switch(wType) { case XTYP_DISCONNECT: return((HDDEDATA) NULL); case XTYP_ERROR: break; case XTYP_XACT_COMPLETE: break; } return((HDDEDATA)NULL); }
При регистрации приложения в библиотеке DDEML функцией
DdeInitialize можно указать флаги, запрещающие или разрешающие
поступление транзакций некоторых типов в функцию обратного вы­зова. Запретив вызов функции обратного вызова для необрабаты­ваемых транзакций, можно ускорить работу приложения.
1.2.4. Создание и уничтожение канала
Последнее, что нужно сделать перед началом передачи данных
по каналу DDEML, – создать канал связи (conversation).
Канал связи между клиентом и сервером создается всегда по
инициативе клиента. После регистрации в библиотеке DDEML кли­ент вызывает функцию DdeConnect, создающую канал связи:
HCONV WINAPI DdeConnect(
DWORD idInst, // идентификатор приложения HSZ hszService, // идентификатор строки
сервиса
75
HSZ hszTopic, // идентификатор строки раздела СONVCONTEXT FAR* рСС); // адрес данных контекста
Через параметр idInst приложение должно передать идентифи-
катор, полученный на этапе регистрации приложения в библиотеки DDEML функцией DdeInitialize.
Параметры hszService и hszTopic предназначены для передачи
идентификаторов строк, содержащих, соответственно, имена серви­са и раздела. Эти идентификаторы были получены нами ранее при помощи функции DdeCreateStringHandle.
Последний параметр hszTopic – указатель
CONVCONTEXT. Эта структура используется для указания инфор­мации о национальном языке и кодовой странице, соответствующей передаваемым данным. В простейшем случае для данного параметра можно указать значение NULL, при этом будет использована кодо­вая страница СР_WINANSI (что приемлемо в подавляющем боль­шинстве случаев).
Когда канал связи больше не нужен, клиент
уничтожить его, вызвав функцию DdeDisconnect:
BOOL WINAPI DdeDisconnect(HCONV hConv);
В качестве единственного параметра этой функции передается
идентификатор уничтожаемого канала.
В процессе удаления канала «партнер» приложения, высту-
пившего инициатором удаления канала, получает транзакцию XTYP_DISCONNECT. Соответствующий обработчик может при необходимости выполнить действия по освобождению ресурсов, заказанных приложением для работы с данным каналом связи.
Передача данных через канал DDEML
1.3.
Передача и прием данных может выполняться в трех режимах:
по явному запросу, через «теплый» канал или через «горячий» канал.
В первом случае клиент посылает серверу запрос, указав нуж-
ный элемент данных. Сервер, получив такой запрос, предоставляет клиенту нужные данные. «Теплый» и «горячий» каналы связи соз­даются в изменениями данных, хранящихся в памяти сервера.
тех случаях, когда клиент должен постоянно следить за
на структуру типа
или сервер может
76
В «теплом» режиме при изменении данных сервер посылает
клиенту соответствующее извещение. Получив такое извещение, клиент запрашивает у сервера новые данные.
В «горячем» режиме при изменении данных сервер самостоя-
тельно посылает клиенту данные без дополнительного запроса.
Предметом нашего рассмотрения будет самый простой режим –
по явному запросу клиента.
Процесс передачи данных заключается в Отметим, что транзакции бывают синхронные и асинхронные. Клиент, пославший синхронную транзакцию, дожидается ее за-
вершения в течение заданного интервала времени. Если по истече­нии этого интервала времени транзакция не завершилась, клиент получает код ошибки.
После посылки асинхронной транзакции клиент не ждет завер-
шения транзакции. Когда транзакция будет завершена, чит от системы DDEML транзакцию XTYP_XACT_COMPLETE.
В наших примерах мы будем работать с синхронными транзак-
циями.
1.3.1. Запрос данных от сервера
Для того чтобы получить данные от сервера, клиент должен по-
слать серверу транзакцию XTYP_REQUEST. Задача посылки серве­ру транзакции решается с помощью функции DdeClientTransaction:
НDDЕDАТА WINAPI DdeClientTransaction(
void FAR* pData, // адрес данных, передаваемых
DWORD cbData, // размер передаваемых данных HCONV hConv, // идентификатор канала HSZ hszItem, // идентификатор элемента
UINT uFmt, // формат данных UINT uType, // код транзакции DWORD dwTimeout, // продолжительность периода
DWORD FAR* pdwResult); // указатель на двойное слово,
// выполнения транзакции
77
посылке транзакций.
клиент полу-
серверу
данных
ожидания
в которое будет записан
результат
Если приложение запрашивает данные у сервера, для первых
двух параметров следует указать нулевые значения (так как клиент не передает данные серверу, а наоборот, запрашивает их).
Через параметр hConv следует передать идентификатор создан-
ного ранее канала связи.
Так как по одному каналу связи можно передавать различные
элементы данных, следует указать нужный элемент данных щью параметра hszItem.
Формат данных передается через параметр uFmt. Здесь вы мо-
жете использовать один из идентификаторов формата Clipboard, та­кой как СР_ТЕХТ или СР_BITMAP, в зависимости от того, что со­бой представляют передаваемые данные.
Через параметр uType следует передать код транзакции, посы-
лаемой серверу. Для запроса данных следует послать транзакцию
XTYP_REQUEST.
Параметр dwTimeout задает для синхронных транзакций время
ожидания завершения транзакции (в миллисекундах). Вы можете указать для этого параметра значение TIMEOUT_ASYNC, в этом случае будет запущена асинхронная транзакция.
Параметр pdwResult должен содержать указатель на двойное
слово. В это слово будет записан код результата выполнения тран­закции. Если проверка не используется, через этот параметр можно передать нулевое значение.
1.3.2. Передача данных серверу
Рассмотрим обратную процедуру – передачу данных от клиента
серверу.
Процедура передачи данных состоит из двух шагов. Вначале
надо создать блок глобальной памяти и записать в него передавае­мые данные. Для этого следует воспользоваться только что рассмот­ренной нами функцией DdeCreateDataHandle:
hData = DdeCreateDataHandle (idInst, szString, lstrlen(szString) +
1, 0L, hszItem, CF_TEXT, 0);
Затем клиент должен передать
вызвав для этого функцию DdeClientTransaction:
if(hData != NULL) hData = DdeClientTransaction((LPBYTE)hData, -1, hConv, hszItem,
CF_ТЕХТ, XTYP_РОКЕ, 1000, &dwResult);
серверу транзакцию XTYP_POKE,
78
с помо-
В качестве первого параметра функции DdeClientTransaction пе-
редается идентификатор созданного блока памяти. Обратите внима­ние, что размер блока задан во втором параметре функции как 1. Так и должно быть, если через первый параметр передается не указатель на область памяти, а идентификатор блока памяти.
1.3.3. Выполнение команды
Помимо передачи данных возможно такое взаимодействие ме
жду клиентом и сервером, когда клиент передает серверу команды в виде текстовой строки, а сервер их исполняет. Вообще говоря, этот механизм можно реализовать с помощью рассмотренных нами ранее транзакций XTYP_POKE и XTYP_REQUEST, однако существует специально предназначенная для передачи команд транзакция
XTYP_EXECUTE. Вот соответствующие параметры, переда-
ваемые функции обратного вызова:
Параметр Значение
hsz1 Идентификатор строки, содержащей имя раздела
hsz2 Идентификатор строки, содержащей имя сервиса
Процесс передачи команды очень напоминает процесс передачи
данных серверу через транзакцию XTYP_РОКЕ.
Вначале необходимо при помощи функции DdeCreateDataHandle
создать блок памяти, содержащей текстовую строку команды. Отли­чие заключается в том, что параметр hszItem должен быть указан как
NULL:
hData = DdeCreateDataHandle (idInst, szCmdString, lstrlen(szCmdString) + 1, 0L, NULL, wFmt, 0);
Затем с помощью функции DdeClientTransaction серверу посы-
лается транзакция XTYP_EXECUTE:
if(hData != NULL) hData = DdeClientTransaction((LPBYTE)hData, -1, hConv,
hszItem, wFmt, XTYP_EXECUTE, 1000, &dwResult);
79
-
Для того чтобы получить доступ к командной строке, обра-
ботчик транзакции XTYP_EXECUTE, расположенный в функции обратного вызова сервера, должен использовать функцию
DdeAccessData:
BYTE FAR* WINAPI DdeAccessData( HDDEDATA hData, // идентификатор блока памяти DWORD FAR* pcbDatasize); // указатель на переменную, в ко-
торую будет записан размер бло­ка памяти
Эта функция возвращает указатель на начало области памяти,
содержащей команду.
После успешного выполнения
команды функция обратного вы­зова сервера должна вернуть значение DDE_FACK. Если команда не поддерживается, нужно вернуть значение DDE_FNOTPROCESSED. В том случае, когда сервер может выполнить команду позже (пото­му что занят, например, выполнением другой команды), функция обратного вызова должна вернуть значение DDE_FBUSY.
Клиент может проверить результат выполнения передачи ко-
манды, если проанализирует содержимое двойного
слова, на ко-
торое указывал параметр lpdwResult перед вызовом функции
DdeClientTransaction. Например, если в этом слове установлен бит DDE_FBUSY, можно попробовать повторить посылку команды позже.
З
АДАНИЕ НА РАБОТУ
Написать приложения сервера и клиента, отвечающие следую-
щим требованиям:
- приложение сервера должно содержать реализацию функции из
первой лабораторной работы;
- после запуска и регистрации сервер находится в состоянии ожи-
дания запроса от клиента;
- клиент посылает серверу данные, необходимые для выполнения
функции;
- сервер передает клиенту результат
выполнения функции;
- клиент должен отобразить данные, полученные от сервера.
80