Поделиться через


Основные понятия (DDE)

Эти понятия являются ключевыми для понимания динамического обмена данными (DDE) и библиотеки управления динамическими данными (DDEML).

Взаимодействие с клиентом и сервером

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

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

Транзакции и функция обратного вызова DDE

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

DDEML передает транзакцию в определяемую приложением функцию обратного вызова DDE, которая выполняет действие, соответствующее типу транзакции. Например, когда клиентское приложение пытается установить беседу с серверным приложением, клиент вызывает функцию DdeConnect. Эта функция приводит к отправке XTYP_CONNECT транзакции DDEML в функцию обратного вызова DDE сервера. Функция обратного вызова может разрешить беседу, возвращая значение TRUE в DDEML или запрещая беседу, возвращая значение FALSE. Подробные сведения о транзакциях см. в разделе "Управление транзакциями".

Имена служб, имена разделов и имена элементов

Сервер DDE использует трехуровневое имя службы иерархии (называемое "имя приложения" в предыдущей документации по DDE), имя раздела и имя элемента для уникальной идентификации единицы данных, которую сервер может обмениваться во время беседы.

Имя службы — это строка, на которая серверное приложение реагирует, когда клиент пытается установить беседу с сервером. Клиент должен указать это имя службы, чтобы установить беседу с сервером. Хотя сервер может отвечать на множество имен служб, большинство серверов отвечают только на одно имя.

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

Имя элемента — это строка, которая определяет единицу данных, которую сервер может передать клиенту во время транзакции. Например, имя элемента может определить целое число, строку, несколько абзацев текста или растровое изображение.

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

Системный раздел

Системный раздел содержит контекст для получения сведений об общих интересах для любого клиента DDE. Рекомендуется, чтобы серверные приложения всегда поддерживали системный раздел. Системный раздел определен в DDEML. Файл заголовка H как SZDDESYS_TOPIC.

Чтобы определить, какие серверы присутствуют и какие сведения они могут предоставить, клиентское приложение может запросить беседу в системном разделе при запуске, присвоив имя устройства значение NULL. Такие беседы с подстановочными знаками являются дорогостоящими с точки зрения производительности системы, поэтому они должны храниться как минимум. Дополнительные сведения об инициировании бесед DDE см. в разделе "Управление беседами".

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

Позиция Description
SZDDE_ITEM_ITEMLIST Список элементов, поддерживаемых в разделе, отличном от системы. (Этот список может отличаться от момента до момента и от раздела до раздела.)
SZDDESYS_ITEM_FORMATS Список строк с разделителями табуляции, представляющих все форматы буфера обмена, потенциально поддерживаемые приложением-службой. Строки, представляющие стандартные форматы буфера обмена, эквивалентны значениям CF_ с удаленным префиксом "CF_". Например, формат CF_TEXT представлен строкой TEXT. Эти строки должны быть в верхнем регистре, чтобы дополнительно определить их как предопределенные форматы. Список форматов должен отображаться в порядке наиболее полнофункционированного содержимого до наименьшего количества содержимого. Дополнительные сведения о форматах буфера обмена и данных отрисовки см. в разделе Буфер обмена.
SZDDESYS_ITEM_HELP Читаемые пользователем сведения об общих интересах. Этот элемент должен содержать как минимум сведения об использовании функций DDE серверного приложения. Эта информация может включать в себя, но не ограничивается темами, как указать элементы в разделах, какие строки могут выполнять сервер, какие транзакции мешка разрешены, и как найти справку по другим элементам раздела системы.
SZDDESYS_ITEM_RTNMSG Вспомогательная информация для последнего использованного сообщения WM_DDE_ACK . Этот элемент полезен, если требуются более 8 бит возвращаемых данных для конкретного приложения.
SZDDESYS_ITEM_STATUS Указание текущего состояния сервера. Как правило, этот элемент поддерживает только формат CF_TEXT и содержит строку Ready или Busy.
SZDDESYS_ITEM_SYSITEMS Список элементов, поддерживаемых в разделе System на этом сервере.
SZDDESYS_ITEM_TOPICS Список тем, поддерживаемых сервером в текущее время. (Этот список может отличаться от момента до момента.)

Эти имена элементов — это значения, определенные в DDEML. Файл заголовка H. Чтобы получить дескриптор строки для этих строк, приложение должно использовать функции управления строками DDEML, как и для любой другой строки в приложении DDEML. Дополнительные сведения об управлении строками см. в разделе "Управление строками".

Инициализация

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

Каждый экземпляр приложения или библиотеки DLL должен передавать идентификатор экземпляра в качестве параметра idInst любой другой функции DDEML, требующей ее. Цель нескольких экземпляров DDEML — поддерживать библиотеки DLL, которые должны использовать DDEML одновременно с приложением. Приложение не должно использовать несколько экземпляров DDEML.

Фильтры транзакций оптимизируют производительность системы, предотвращая передачу нежелательных транзакций в функцию обратного вызова DDE приложения. Приложение задает фильтры транзакций в параметре DdeInitialize ufCmd. Приложение должно указать флаг фильтра транзакций для каждого типа транзакции, который он не обрабатывает в функции обратного вызова. Приложение может изменить фильтры транзакций с последующим вызовом DdeInitialize. Дополнительные сведения о транзакциях см. в разделе "Управление транзакциями".

В следующем примере показано, как инициализировать приложение для использования DDEML.

DWORD idInst = 0; 
HINSTANCE hinst; 
 
DdeInitialize(&idInst,         // receives instance identifier 
    (PFNCALLBACK) DdeCallback, // pointer to callback function 
    CBF_FAIL_EXECUTES |        // filter XTYPE_EXECUTE 
    CBF_SKIP_ALLNOTIFICATIONS, // filter notifications 
    0); 

Приложение должно вызывать функцию DdeUninitialize , если она больше не будет использовать DDEML. Эта функция завершает все беседы, открытые для приложения, и освобождает ресурсы DDEML, выделенные системой для приложения.

Функция обратного вызова

Приложение, использующее DDEML, должно предоставить функцию обратного вызова, которая обрабатывает события DDE, влияющие на приложение. DDEML уведомляет приложение таких событий, отправляя транзакции в функцию обратного вызова DDE приложения. Транзакции, получаемые функцией обратного вызова, зависят от того, какой фильтр обратного вызова помечает приложение, указанное в DdeInitialize , и независимо от того, является ли приложение клиентом, сервером или обоими. Дополнительные сведения см. в разделе DdeCallback.

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

HDDEDATA CALLBACK DdeCallback(uType, uFmt, hconv, hsz1, 
    hsz2, hdata, dwData1, dwData2) 
UINT uType;       // transaction type 
UINT uFmt;        // clipboard data format 
HCONV hconv;      // handle to conversation 
HSZ hsz1;         // handle to string 
HSZ hsz2;         // handle to string 
HDDEDATA hdata;   // handle to global memory object 
DWORD dwData1;    // transaction-specific data 
DWORD dwData2;    // transaction-specific data 
{ 
    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. Значения оставшихся параметров зависят от типа транзакции. Типы транзакций и события, создаваемые ими, описаны в следующих разделах. Подробные сведения о каждом типе транзакции см. в разделе "Управление транзакциями".

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

Для выполнения задачи DDE многие функции DDEML требуют доступа к строкам. Например, клиент должен указать имя службы и имя раздела при вызове функции DdeConnect , чтобы запросить беседу с сервером. Приложение задает строку путем передачи строкового дескриптора (HSZ), а не указателя в функции DDEML. Строковый дескриптор — это значение DWORD, назначенное системой, идентифицирующее строку.

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

HSZ hszServName; 
HSZ hszSysTopic; 
hszServName = DdeCreateStringHandle( 
    idInst,         // instance identifier 
    "MyServer",     // string to register 
    CP_WINANSI);    // Windows ANSI code page 
 
hszSysTopic = DdeCreateStringHandle( 
    idInst,         // instance identifier 
    SZDDESYS_TOPIC, // System topic 
    CP_WINANSI);    // Windows ANSI code page 
    

Параметр idInst в предыдущем примере указывает идентификатор экземпляра, полученный функцией DdeInitialize .

Функция обратного вызова DDE приложения получает одну или несколько строковых дескрипторов во время большинства транзакций DDE. Например, сервер получает две строковые дескрипторы во время транзакции XTYP_REQUEST : одна определяет строку, указывающую имя раздела, а другая — строку, указывающую имя элемента. Приложение может получить длину строки, соответствующей дескриптору строки, и скопировать строку в буфер, определяемый приложением, вызвав функцию DdeQueryString , как показано в следующем примере.

DWORD idInst; 
DWORD cb; 
HSZ hszServ; 
PSTR pszServName; 
cb = DdeQueryString(idInst, hszServ, (LPSTR) NULL, 0, 
    CP_WINANSI) + 1; 
pszServName = (PSTR) LocalAlloc(LPTR, (UINT) 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 ! 

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

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

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

Когда приложение вызывает DdeCreateStringHandle и указывает строку, которая уже существует в таблице, система увеличивает количество использования, а не добавляет другое вхождение строки. (Приложение также может увеличить количество использования с помощью DdeKeepStringHandle.) Когда приложение вызывает функцию DdeFreeStringHandle , система уменьшает количество использования.

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

Функции управления строками DDEML основаны на диспетчере атомов и подвергаются тем же ограничениям размера, что и атомы.

DDEML и Threads

Функция DdeInitialize регистрирует приложение с помощью DDEML, создавая экземпляр DDEML. Экземпляр DDEML основан на потоке, связанном с потоком, который называется DdeInitialize.

Все вызовы функции DDEML для объектов, принадлежащих экземпляру DDEML, должны быть сделаны из одного потока, который называется DdeInitialize для создания экземпляра. При вызове функции DDEML из другого потока функция завершится ошибкой. Невозможно получить доступ к беседе DDEML из потока, отличного от выделенного беседы.