Смекни!
smekni.com

Обмен данными в Windows (стр. 4 из 8)

Начало DDE–разговора - это единственный случай, когда сообщения DDE передаются с помощью SendMessage, а не PostMessage. Это необходимо для нормального начала обмена.

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

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

Считается, что при инициализации DDE–разговора, клиент должен получить ответ (или убедится в его отсутствии) немедленно. Этого можно достичь только используя передачу, а не посылку сообщений. В этом случае подтверждение приходит в то время, пока клиент ожидает завершения работы процедуры SendMessage. Для нормального продолжения DDE клиент должен запомнить хендл сервера (а сервер - хендл клиента).

Так как возможен случай, что на запрос клиента ответят два и более серверов, то надо предусмотреть либо отказ от установления более чем одного соединения (как в приведенном примере), либо организовать DDE сразу с несколькими серверами.

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

WM_DDE_TERMINATE hWnd 0L

сообщение оканчивает DDE–разговор. Параметр hWnd является хендлом пославшего окна. При этом уничтожаются вспомогательные структуры и обнуляются переменные.

Послать WM_DDE_TERMINATE может как клиент, так и сервер. Оба они должны быть готовы к приему такого сообщения от напарника.

Обмен данными между клиентом и сервером

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

В DDE различают три способа получения данных от сервера, называемых видами связи. Эти три вида связи называются холодная, теплая и горячая. Коротко поясним различия этих видов связи:

· холодная связь — cold link

обмен данными происходит только по запросу клиента. Сервер посылает в ответ данные или отрицательное подтверждение.

· горячая связь — hot link

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

· теплая связь — warm link

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

Последние два вида связи (теплая и горячая) называются иногда постоянной (permanent) связью.

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

Когда два приложения обмениваются данными друг с другом, они передают друг другу хендлы блоков данных и атомы. Для того, что бы эти данные были доступны обоим приложениям, они должны быть глобальными. Однако надо учесть, что обычно глобальные блоки данных связаны с тем приложением, которое их создало, то есть при завершении приложения эти данные автоматически уничтожаются. Так как процесс DDE–разговора является асинхронным, то необходимо обеспечивать сохранность данных независимо от существования создавшего их приложения. Для этого глобальные блоки данных должны быть разделяемыми — при их выделении надо указывать флаг GMEM_DDESHARE (или GMEM_SHARE, который является синонимом).

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

Особое внимание надо уделить вопросу освобождения ресурсов, так как атомы и передаваемые блоки данных сами не уничтожаются, даже если все участвующие в DDE приложения завершили работу. Все созданные объекты должны быть обязательно уничтожены, независимо от исхода операции. Сложности связаны с тем, что один и тот–же объект может быть уничтожен либо клиентом, либо сервером, в зависимости от протекания процесса обмена, а, кроме того, в процессе обмена могут присходить различные ошибки (например, функция PostMessage не может послать сообщение).

“Холодная” связь — cold link

При холодной связи обмен данными активируется клиентом. Для этого клиент посылает специальное сообщение, в ответ на которое сервер отправляет данные (если может), либо сообщение об ошибке:

WM_DDE_REQUEST hWnd aItem & cfFormat

это сообщение является требованием передачи данных. Параметр wParam является хендлом пославшего сообщение окна, а lParam содержит в младшем слове номер формата данных (номера те–же, что используются буфером обмена), а в старшем - атом имени данных. Платформы Win32 и Windows 3.x используют это сообщение одинаково.

В ответ на это сообщение сервер может ответить либо сообщением WM_DDE_DATA, если он имеет необходимые данные и может их послать, либо “отрицательным” сообщением WM_DDE_ACK, указывающим, что сервер не может послать требуемые данные.

WM_DDE_DATA hWnd aItem & hData (Packed Win32)

Это сообщение передает данные клиенту. Младший компонент lParam содержит хендл глобального разделяемого блока данных hData. Этот параметр может быть равен 0, что реально встречается только в случае “теплой” связи (см. ниже). Передаваемый блок данных должен начинаться со структуры DDEDATA, флаг fAckReq которой указывает на необходимость отправки подтверждения о получении данных. Передача: сервер не может сбрасывать одновременно оба бита fRelease и fAckReq в 0. То есть корректными являются только три возможных комбинации fRelease и fAckReq, при которых можно определить, кто должен освобождать блок данных — клиент или сервер. Освобождение ресурсов: атом aItem должен удаляться, если только он не посылается с ответным сообщением WM_DDE_ACK (если бит fAckReq заголовка данных равен 1, то есть требуется ответ). Освобождение блока данных зависит от установки битов fRelease и fAckReq заголовка данных:

fRelease fAckReq
0 1 блок данных всегда освобождается сервером при получении ответа от клиента (считается, что к моменту получения сервером подтверждения клиент уже прочитал требуемые данные и они ему больше не нужны).
1 0 блок данных всегда освобождается клиентом, сервер ответа не получает (так клиент может некоторое время сохранять полученные данные независимо от течения DDE–разговора).
1 1 при успешном чтении блок данных освобождается клиентом, а в случае ошибки (то есть при получении сервером отрицательного подтверждения) блок освобождается сервером. Этот вариант близок к предыдущему, за исключением того, что за ошибочно полученный блок клиент ответственности не несет.

Структура DDEDATA содержит следующие данные:

typedef struct tagDDEDATA { WORD unused:12, fResponse:1, // 1 в ответ на WM_DDE_REQUEST, 0 — WM_DDE_ADVISE fRelease:1, // данные должны быть удалены после получения reserved:1, fAckReq:1; // 1 — необходимо послать подтверждение о приеме short cfFormat; // формат данных BYTE Value[1]; // сами данные } DDEDATA;

Внимание! в документации по Windows 3.x SDK и Win32 SDK содержалась ошибка — там были перепутаны пояснения к полям fResponse и fAckReq, так что описание структуры DDEDATA противоречило описанию сообщения WM_DDE_DATA. В документации, сопровождающей компилятор Microsoft Visual C++ v4.0 эта ошибка была исправлена.

Заметьте, что сервер может потребовать подтверждения о приеме посланных данных. Для этого он устанавливает бит fAckReq в заголовке переданных данных равным 1. В таком случае необходимо послать ответное сообщение WM_DDE_ACK (положительное или отрицательное, если возникла какая-либо ошибка).

Если сервер не имеет нужных данных, то вместо WM_DDE_DATA клиент получит WM_DDE_ACK (отрицательное)

WM_DDE_ACK hWnd aItem & wStatus (Packed Win32)

Младший компонент lParam содержит информацию о посылаемом подтверждении. Младший байт этого слова содержит код, возвращаемый приложением, старший байт содержит два значащих бита 0x80 и 0x40. Часто этот параметр представляется в виде структуры DDEACK. Старшее слово lParam представляет собой атом, идентифицирующий данные. Согласно документации этот атом обязательно должен быть удален при обработке этого сообщения. В случае платформы Win32 параметр lParam используется для передачи “упакованного” значения; при этом надо помнить, что если WM_DDE_ACK приходит в ответ на WM_DDE_INITIATE, то параметр lParam содержит обычные, не упакованные данные.

Структура DDEACK содержит следующие данные:

typedef struct tagDDEACK { WORD bAppReturnCode:8, // код, определяемый приложением reserved:6, fBusy:1, // 1 — сервер занят и не может обработать запрос fAck:1; // 1 — сообщение было получено приложением } DDEACK;

В зависимости от значений битов fBusy и fAck различают следующие возможные виды ответов:

· fAck = 1 fBusy = 0 — положительное подтверждение

· fAck = 0 fBusy = 0 — отрицательное подтверждение

· fAck = 0 fBusy = 1 — сервер занят, либо не может обработать запрос в разумные сроки (что есть “разумные” сроки — не поясняется)

· Согласно документации установка обеих флагов fAck и fBusy одновременно должна быть исключена.

Вместо структуры DDEACK часто используют просто битовые шаблоны — 0x8000 соответствует положительному подтверждению, 0 — отрицательному и 0x4000 — сервер занят (вообще говоря, это тоже отрицательное подтверждение, хотя запрос можно попробовать повторить).