ри начале DDE-разговора посылается сообщение WM_DDE_INITIATE hWnd aTopic & aService это сообщение используется для начала DDE-разговора. Параметр wParam содержит хендл пославшего окна, а lParam в младшем слове -- атом aService, а в старшем -- атом aTopic, задающие, соответственно, сервис и тему DDE-разговора. Нулевые значения атомов указывают соответственно на любую поддерживаемую тему и любой сервис. Передача: Когда клиент вызывает функцию SendMessage для установления DDE-разговора, он создает необходимые атомы, а после возврата из функции SendMessage он обязан эти атомы удалить. Получение: сервер, получивший это сообщение, и поддерживающий указанные сервис и тему отвечает сообщением WM_DDE_ACK. При этом он обязан создать заново требуемые атомы -- использовать атомы, полученные с сообщением запрещено. Значения атомов aTopic и aService равные 0 указывают, соответственно, на любую поддерживаемую сервером тему и сервис. В этом случае сервер должен ответить столько раз, сколько подходящих тем и сервисов он поддерживает. Это сообщение передается всем приложениям (в качестве хендла окна-получателя сообщения используется HWND_BROADCAST или -1). Тот сервер, который поддерживает данную тему и сервис должен ответить на этот запрос передачей сообщения WM_DDE_ACK hWnd aTopic & aService сообщение, подтверждающее принятое прежде сообщение. Параметр wParam содержит хендл пославшего окна, а lParam используется разными способами, в зависимости от того, какое сообщение вызвало это подтверждение. В зависимости от значений параметров сообщения рассматривают положительные и отрицательные подтверждения. При ответе на WM_DDE_INITIATE младшее слово lParam содержит атом сервиса, а старшее -- атом темы, при этом WM_DDE_ACK не посылается, а передается с помощью функции SendMessage, причем оно рассматривается только как положительное, так как в качестве отрицательного ответа используется отсутствие этого сообщения. Если WM_DDE_ACK получено в ответ на WM_DDE_INITIATE, то для обоих платформ -- Windows 3.x и Win32 оно используется одинаково; а если оно получено в ответ на какое-либо иное сообщение, то параметр lParam будет использован для передачи “упакованного” значения. Получение: приложение-клиент, получившее сообщение WM_DDE_ACK обязано удалить все сопровождающие его атомы. Передача: при ответе на WM_DDE_INITIATE запрещено использовать полученные атомы, сервер обязан создать необходимые для ответа атомы и послать их (так как на одно WM_DDE_INITIATE может ответить несколько серверов, а при обработке WM_DDE_ACK клиент обязан удалять все атомы). Если WM_DDE_ACK передается в ответ на сообщение WM_DDE_INITIATE, то в lParam содержатся такие-же данные, что и у сообщения WM_DDE_INITIATE. Это считается положительным подтверждением. Если ответ отрицательный, то подтверждение при инициализации просто не выдается. В большинстве случаев сервер, отвечающий на WM_DDE_INITIATE, создает специальное окно для каждого DDE-разговора. Это связано с тем, что при дальнейшем обмене данными сервис и тема указываться не будут, а сам DDE-разговор определяется фактически окном-клиентом и окном-сервером. Одна такая пара окон обменивается данными только в рамках указанных при установлении DDE-разговора темы и сервиса. Только самые простые DDE-серверы могут обходиться одним окном, но при этом они в данный момент времени могут работать только с одним клиентом. Внимание! При установлении связи с каким-либо сервером Вы можете получить несколько сообщений WM_DDE_ACK от разных серверов, поддерживающих указанные сервис и тему. Если в сообщении WM_DDE_INITIATE вы указали любую поддерживаемую тему и/или любой возможный сервис, то практически всегда отвечают сразу несколько серверов. В этом случае Вы должны создавать список всех тем и сервисов, с которыми устанавливается связь данной операцией (не забудьте в конце закрыть все начатые DDE-разговоры), а если вы собираетесь работать только с одним из ответивших серверов, то со всеми остальными вы должны завершить DDE-разговор посылкой WM_DDE_TERMINATE. Замечание. Согласно документации сервер, получивший WM_DDE_INITIATE с указанием любой поддерживаемой темы или сервиса, обязан отправить в ответ столько сообщений WM_DDE_ACK, сколько тем и сервисов он поддерживает. На самом деле многие DDE серверы этого не делают, как, например, Microsoft Internet Explorer. Такие серверы отвечают только на точно указанные сервис и тему. Это может быть оправдано, если сервер поддерживает значительное число тем или сервисов. При ответе на WM_DDE_INITIATE уже устанавливаетя DDE-разговор, для которого сервер создает отдельное окно и, соответственно, расходует ресурсы. Если сервер отвечает на целый список тем и/или сервисов, то создается много окон, что, скорее всего, излишне. Строго говоря, указывать любую тему или сервис надо только в случае реальной необходимости, так как одним таким запросом могут быть установлены несколько десятков DDE-разговоров сразу (так, например, один только Netscape Navigator отвечает примерно 3 десятками поддерживаемых им тем). Начало 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;
Страницы: 1, 2, 3, 4, 5
|