Уже существующие приложения, использующие протокол DDE, основанный на сообщениях полностью совместимы с теми, которые используют библиотеку DDEML. Вот почему приложение, использующее DDE-протокол могут установить диалог и выполнять транзакции с приложениями, использующими библиотеку DDEML.
Взаимосвязь между клиентом и сервером.
DDE возникает всегда между клиентским приложением и серверным. Клиентское приложение инициализирует обмен данными путем установления диалога с сервером и передачи транзакции. Транзакция необходима для данных и обслуживания. Сервер отвечает на транзакцию и обеспечивает клиента данными. Сервер может иметь сразу несколько клиентов в одно и тоже время, в свою очередь, клиент может получать данные сразу от нескольких серверов. Некоторое приложение одновременно может быть и клиентом и сервером. В добавок к вышесказанному, клиент и сервер могут оборвать диалог в любое удобное для них время.
Транзакции, функция обратного вызова DDE
( CallBack function )
DDEML информирует приложение об активности DDE путем передачи транзакции в функцию обратного вызова данного приложения. DDE транзакция схожа с обыкновенным сообщением - это именованная константа, сопровождаемая другими параметрами, которые содержат дополнительную информацию о текущей транзакции.
DDEML передает транзакцию в функцию обратного вызова приложения, которая выполняет некоторое действие согласно типу и виду транзакции. Например, когда клиентское приложение пытается установить дилог с сервером, клиент вызывает функцию DdeConnect. Это означает, что DDEML должна послать транзакцию XTYP_CONNECT в функцию обратного вызова сервера. Функция обратного вызова может позволять или не позволять установку диалога, возвращая TRUE или FALSE DDEML.
Вспомогательные имена и другие названия
DDE сервер использует три зарезервированных типа имен, расположенных иерархично: service, topic item - уникально идентифицируют некоторое множество данных, которое сервер может передать клиенту в процессе диалога.
Service имя - это строка, которую генерирует сервер в те промежутки времени, в которые клиент может установить диалог с сервером.
Topic имя - это строка, которая идентифицирует логический контекст данных. Для сервера, который манипулирует файлами, topic имена это просто названия
файлов; для других серверов - это специфические имена
конкретного приложения. Клиент обязательно должен
указывать topic имя вместе с service именем, когда он
хочет установить диалог с сервером.
Item имя - это строка, которая идентифицирует некоторое множество данных, которое сервер может передать клиенту в процессе транзакции. Например, item имя может идентифицировать ЦЕЛОЕ ( int, integer ), СТРОКУ ( string, char * ), несколько параграфов текста, или BITMAP образ.
Все вышеуказанные имена позволяют клиенту
установить диалог с сервером и получить от него данные.
Системный режим
Системный режим работы обеспечивает клиента всей необходимой информцией о сервере.
Для того, чтобы определить, какие серверы доступны в данный момент времени, а также какой информацией они могут обеспечить клиента, последний, находясь в начальном режиме работы, должен установить имя устройства, равное NULL. Такой шаблон диалога максимально увеличивает эффективность работы, а также работу с сервером в системном режиме. Сервер, в свою очередь, должен поддерживать нижеописанные item имена, а также другие, часто используемые клиентом:
SZDDESYS ITEM TOPICS - список item имен, с которыми может работать сервер в данный момент времени. Этот список может изменяться время от времени.
SZDDESYS ITEM SYSITEMS - список item имен, с которыми может работать сервер в системном режиме.
SZDDDESYS ITEM STATUS - запросить текущий статус сервера. Обычно, данный запрос поддерживается только в формате CF_TEXT и содержит строку типа Готов/Занят.
SZDDE ITEM ITEMLIST - список item имен,
поддерживаемых сервером в несистемном режиме работы. Этот
список может меняться время от времени.
SZDDESYS ITEM FORMATS - списокстрок,
представляющий собой список всех форматов почтового ящика,
поддерживаемых сервером в данном диалоге. Например,
CF_TEXT формат представлен строкой TEXT.
Инициализация
Перед вызовом любой функции DDEML, приложение должно вызвать функцию DdeInitialize. Эта функция получает идентификатор копии данного приложения, регистрирует функцию обратного вызова приложения посредством DDEML и указывает флаг фильтра транзакции для функции обратного вызова.
Каждое приложение или DLL должно содержать
идентификатор своей копии, например, в параметре idInst.
Он необходим любой функции DDEML. Это очень легко поддается объяснению: назначение DDEML - поддержка механизма DDE в приложениях, несколько копий которых может быть запущено в данный момент времени. Однако приложение НЕ МОЖЕТ использовать более одной копии DDEML.
Фильтр транзакции оптимизирует эффективность
системы путем предотвращения передачи нежелательных типов транзакций в функцию обратного вызова. Приложение устанавливает фильтр транзакции при вызове функции DdeInitialze. Приложение должно указать флаг фильтра транзакции для каждого типа транзакции, которые не будут обрабатываться в функции обратного вызова. Однако любое приложение может изменить фильтр транзакции путем дополнительного вызова функции DdeInitialize. Приведем пример инициализации DDE-диалога.
DWORD idInst = 0;
HINSTAINCE hInst;
DdeInitialize( &idIns, // Копия приложения
( PFNCALLBACK ) DdeCallback, // Адрес CallBack функции CBF_FAIL_EXECUTES | // Фильтр XTYPE_EXECUTE CBF_SKIP_ALLNOTIFICATIONS, 0 );// Фильтр NOTIFICATIONS
Каждое приложение должно вызывать функцию
DdeUninitialize, когда оно больше не собирается
использовать DDEML. Эта функция прекращает текущий диалог и освобождает ресурсы DDEML, предоставленные системой для установления диалога.
Основное назначение и работа функции обратного вызова
Приложение, которое использует DDEML, должно
содержать функцию обратного вызова, которая обрабатывает события, полученные приложением. DDEML уведомляет приложение о таких событиях путем посылки транзакций в функцию обратного вызова данного приложения.
В зависимости от флага фильтра транзакции,
сформированного при вызове функции DdeInitialize, функция обратного вызова получает отсортированные транзакции вне зависимости от того, является ли данное приложение клиентом, сервером или тем и другим одновременно. Следующий пример демонстрирует наиболее типичное использование функции обратного вызова.
HDDEDATA CALLBACK DdeCallback( uType, uFmt, hconv, hsz1, hsz2, hdata, dwData1, dwData2 )
UINT uType; // Тип транзакции
UINT uFmt; // Формат почтого ящика
HCONV hconv; // Идентификатор диалога
HSZ hsz1; // Идентификатор строки #1
HSZ hsz2; // Идентификатор строки #2
HDDEDATA hdata; // Идентификатор глобального объекта памяти DWORD dwData1; // Данные текущей транзакции #1
DWORD dwData2; // Данные текущей транзакции #2
switch (uType)
case XTYP_REGISTER:
case XTYP_UNREGISTER:
. . .
return (HDDEDATA) NULL;
case XTYP_ADVDATA:
. . .
return (HDDEDATA) DDE_FACK;
case XTYP_XACT_COMPLETE:
. . .
return (HDDEDATA) NULL;
case XTYP_DISCONNECT:
. . .
return (HDDEDATA) NULL;
default:
return (HDDEDATA) NULL;
Параметр uType идентифицируеттиппосланной
транзакции в функцию обратного вызова при помощи DDEML. Значения оставшихся параметров зависят от типов транзакции. Типы транзакций будут обсуждены нами в разделе "Обработка Транзакций".
Обработка строк
Для того, чтобы работать в режиме диалога,
большинство DDEML функций требуют наличия доступа к строкам. Например, клиент должен в явном виде указывать service и topic имена, когда приложение вызывает функцию DdeConnect для установления диалога с сервером. Приложение указывает строку путем передачи ее идентификатора в соответствующее место (также как и в случае указателя на DDEML функцию). Идентификатор строки - это двойное слово, определяемое системой.
Приложение может получить идентификатор строки
путем вызова соответствующей функции
DdeCreateStringHandle. Эта функция регистрирует строку в системе и возвращает ее идентификатор приложению. Следующий пример получает идентификатор строки для строк System topic и Service-name.
HSZ hszServName;
HSZ hszSysTopic;
. . .
hszServName = DdeCreateStringHandle(
idInst, // Копия приложения
"MyServer", // Строка для регистрации
CP_WINANSI); // Кодоваястраница Windows ANSI
hszSysTopic = DdeCreateStringHandle(
idInst, // Копияприложения
SZDDESYS_TOPIC, // Строкадлярегистрации CP_WINANSI); // Кодоваястраница Windows ANSI
. . .
Параметр idInst содержит идентификатор,
возвращенный функцией DdeInitialize.
Функция обратного вызова получает один или более строковых идентификаторов при обработке большинства DDE-транзакций. Например, сервер получает два идентификатора строк в процессе транзакции типа XTYP_REQUEST: один идентификатор - это строка, описывающая topic имя, а другой - item.
Приложение может получать длину строки,
соответствующую идентификатору строки и копировать эту
строку в некоторый буфер, предварительно зарезервированный
приложением.
Все вышеуказанные действия можно проделать при помощи вызова функции DdeQueryString, как продемонстрировано в следующем примере:
DWORD idInst;
DWORD cb;
HSZ hszServ;
PSTR pszServName;
. . .
cb = DdeQueryString(idInst, hszServ, (LPSTR) NULL, 0,
CP_WINANSI) + 1;
pszServName = (PSTR) LocalAlloc(LPTR, (WORD) cb);
DdeQueryString(idInst, hszServ, pszServName, cb, CP_WINANSI);
. . .
Итак, функция DdeQueryString создает строку,
используя строковый идентификатор, а затем функция
DdeCreateStringHandle создает строковый идентификатор из строки. Следует отметить, что два идентификатора НЕ СУЩЕСТВУЮТ в одно и тоже время.
DWORD idInst;
DWORD cb;
HSZ hszInst, hszNew;
PSZ pszInst;
. . .
DdeQueryString(idInst, hszInst, pszInst, cb, CP_WINANSI); hszNew = DdeCreateStringHandle(idInst, pszInst, CP_WINANSI); // hszNew != hszInst !
. . .
При возвращении некоторого значения функцией
обратного вызова идентификатор строки портится. В
приложении можно сохранить идентификатор при помощи функции DdeKeepStringHandle и использовать этот идентификатор после вызова функции CallBack.