Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Описание протокола DDE.doc
Скачиваний:
32
Добавлен:
01.05.2014
Размер:
70.66 Кб
Скачать

. Вступление.

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

Перепробовав несколько решений, я остановился на механизме Dynamic Data Exchange, который, несмотря на преклонный возраст (DDE появился еще в Windows 3.0) и относительную громоздкость и трудоемкость в программировании остается наилучшим решением для организации быстрого обмена данными между приложениями. Однако программирование DDE приложения, тем более в среде .NET, которая не приветствует явного использования указателей и использование памяти вне ведома Garbage Collector, может вызвать вопросы, особенно у начинающих программистов. Вот на эти вопросы, которые в свое время возникли у меня, я и постараюсь ответить в данной статье. В конце статьи я приведу готовое решение – компонент ExcelDDEHotConnection, позволяющий организовать подписку на любую ячейку любой открытой книги, и скрывающий в себе все низкоуровневые операции. Данный компонент может быть свободно использован разработчиками в своих приложениях, при этом я не устанавливаю никаких ограничений или авторских прав на исходный код.

2. Описание протокола dde

Начнем с краткого описания протокола DDE. Материал статьи охватывает только ту часть API протокола, которая необходима для организации «горячего» канала DDE.

Протокол DDE подразумевает клиент-серверную архитектуру. Это значит, что одно их приложений выступает в качестве сервера, а второе – клиента. В нашем случае сервером выступает приложение MS Excel, а приложения .NET являются для него клиентами.  Обмен данными между приложениями происходит посредством транзакций. Управляет всем процессом специальное расширение ОС Windows - динамическая библиотека DDEML.

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

Клиентское приложение также сначала регистрирует себя в библиотеке DDEML. После этого клиентское приложение создает канал связи с сервером.

Протокол DDE поддерживает три вида обмена данными между клиентом и сервером:

  • По явному запросу

  • «Теплый канал»

  • «Горячий канал»

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

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

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

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

В библиотеке DDEML данные адресуются трехступенчатой схемой: сервис (service), раздел (topiс) и элемент данных (data item). Для сервера DDE приложения MS Excel, эта схема выглядит следующим образом:

  • Зарегистрированный сервис (service): “EXCEL”

  • Разделы данных (topics): “[название_книги]название_страницы”, например “[Книга1]Лист1”.

  • Элемент данных(item) – описание ячейки: “Rномер_строкиСномер_столбца”, например “R1C2” адресует ячейку в первой строке и во втором столбце. При этом нумерация строк в MS Excel начинается с единицы.

Для установления связи, приложение должно сначала зарегистрировать себя в библиотеке DDEML и получить свой программный идентификатор. Этот идентификатор необходимо хранить в течение всей работы, так как для каждого приложения, которое регистрируется в библиотеке DDEML, создается своя копия необходимых структур данных.

Регистрация в библиотеке DDEML происходит с помощью функции DdeInitialize, которая имеет следующую сигнатуру:

UINT WINAPI DdeInitialize(

DWORD FAR* pidInst,

PFNCALLBACK pfnCallback,

DWORD afCmd,

DWORD ulRes);

Параметры, которые передаются функции при вызове:

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

  • pfnCallback – указатель на функцию обратного вызова.

  • afCmd – набор битовых флагов инициализации, а также устанавливающий некоторые специфические условия работы с библиотекой.

  • ulRes – зарезервировано и должно быть равно нулю.

В случае успешной регистрации, функция DdeInitialize возвращает нулевое значение. Если при инициализации произошла ошибка, то функция вернет код ошибки.

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

BOOL WINAPI DdeUninitialize(DWORD idInst);

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

Адресация происходит посредством строк, однако в транзакциях используются их идентификаторы. Эти идентификаторы присваиваются каждой строке библиотекой DDEML и хранятся в специальной системной таблице идентификации строк. Для создания идентификатора строки, необходимо воспользоваться функцией DdeCreateStringHandle:

HSZ WINAPI DdeCreateStringHandle(

DWORD idInst,

LPCSTR psz,

Int iCodePage);

Функция получает следующие параметры:

  • idInst – программный идентификатор приложения, полученный при регистрации в библиотеке DDEML;

  • psz – адрес текстовой строки, завершенной двоичным нулем. Длина строки не должна превышать 255 байт.

  • iCodePage – кодовая страница, определяющая тип строки, получаемой на вход. При указании константы CP_WINANSI данная строка рассматривается как строка ANSI. Если указать константу CP_WINUNICODE, то строка рассматривается как состоящая из символов Unicode.

Функция возвращает идентификатор, который библиотека DDEML присвоила данной строке.

Для того, чтобы освободить ресурсы, связанные с зарегистрированной строкой, клиентское приложение должно вызвать функцию отмены регистрации данной строки DdeFreeStringHandle:

BOOL WINAPI DdeFreeStringHandle(

DWORD idInst,

HSZ hsz);

В качестве параметров функция получает следующее:

  • idInst – программный идентификатор приложения, полученный при регистрации в библиотеке DDEML;

  • hsz – идентификатор строки.

Функция возвращает значение true, если операция прошла успешно, и false - если при выполнении функции произошли ошибки.

Для того, чтобы получить строку по ее идентификатору, необходимо воспользоваться функцией DdeQueryString:

DWORD WINAPI DdeQueryString(

DWORD idInst,

HSZ hsz,

LPSTR psz,

DWORD cchMax,

Int iCodePage

);

В качестве параметров функция получает следующее:

  • idInst - программный идентификатор приложения, полученный при регистрации в библиотеке DDEML;

  • hsz – идентификатор строки, которую нужно получить.

  • psz – указательна буфер, в который будет записана строка

  • cchMax – максимальная длина строки в символах. Вид символа определяется следующим по порядку параметром и может быть ANSI (1 байт) или Unicode (2 байта)

  • iCodePage – определяет тип символов строки. Возможные значения CP_WINANSI – для символов стандарта ANSI (1 байт) и CP_WINUNICODE – для символов стандарта Unicode (2 байта).

Функция возвращает количество скопированных символов. При  этом если фактическая длина строки (в символах) меньше указанной в параметре cchMax, то функция скопирует cchMax символов и вернет это значение. Если cchMax больше фактической длины строки, то функция скопирует всю строку и вернет количество скопированных символов. Если передать через параметр   psz нулевое значение, то функция проигнорирует значение параметра cchMax и вернет фактическую длину строки в символах. Размер буфера в байтах для строки зависит от размера символа и определяется параметром iCodePage.

Для получения данных из глобальной области памяти по их идентификатору нужно воспользоваться функцией DdeGetData:

DWORD WINAPI DdeGetData(

HDDEDATA hData,