2005 г.
Скачать пример (C# проект).
1. Вступление.
В данной статье я хочу поделиться решением, которое наверняка будет многим полезно. Началось с того, что передо мной была поставлена задача организовать чтение данных из книги 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,
void FAR* pDst,
DWORD cbMax,
DWORD cbOff
);
Функция должна получить на вход следующие параметры:
- hData — идентификатор порции данных в глобальной области памяти;
- pDst – указатель на буфер, куда будут скопированы данные из глобальной области.
- cbMax – размер буфера в байтах. Если фактический размер данных больше размера области в буфере, которая выделяется под эти данные(см. ниже параметр cbOff), то будут скопированы
только первые (cbMax – cbOff) байт. Иначе функция скопирует все данные в буфер. - cbOff – смещение в буфере относительно начала, с которого функция поместит в буфер данные из глобальной области.
Функция возвратит количество фактически скопированных байт данных. Если вместо ссылки на буфер через параметр pDst передать нулевое значение, то функция вернет фактический размер порции
данных в глобальной области памяти, при этом значение параметров cbMax и cbOff будут проигнорированы.
Канал связи DDE создается с помощью функции DdeConnect:
HCONV WINAPI DdeConnect(
DWORD idInst,
HSZ hszService,
HSZ hszTopic,
CONVCONTEXT FAR* pCC);
В качестве параметров функция должна получить следующее:
- idInst — программный идентификатор приложения, полученный при регистрации в библиотеке DDEML;
- hszService – идентификатор строки названия сервиса, который необходимо предварительно получить вызовом функции DdeCreateStringHandle
- hszTopic – идентификатор строки названия раздела, который также заранее запрашивается у библиотеки DDEML вызовом функции DdeCreateStringHandle;
- pCC – указатель на специальную структуру типа CONVCONTEXT, в которой указывается информация о национальном языке и кодовой странице. В большинстве случаев (в нашем тоже) достаточно
указать нулевое значение, что означает использование кодовой страницы ANSI.
Функция возвращает идентификатор созданного канала связи. В случае ошибки функция вернет нулевое значение. Полученный идентификатор канала необходимо хранить в течение всего сеанса связи.
Когда приложение завершает работу с каналом, оно должно закрыть его, вызвав функцию DdeDisconnect:
BOOL WINAPI DdeDisconnect(HCONV hConv);
В качестве параметра функция получает идентификатор канала, который нужно закрыть. Функция возвращает true, если канал успешно закрыт и false в случая возникновения ошибок при закрытии канала.
После того, как был создан канал связи, можно начинать обмен данными. Обмен происходит посредством транзакций с помощью функции DdeClientTransaction и функции обратного вызова
DdeCallbackFunction. Если приложение (независимо от того, клиент или сервер) хочет отправить данные, то оно должно подготовить их, оформить контекст с помощью функций библиотеки DDEML , а потом
вызвать функцию DdeClientTransaction. При этом принимающему приложению будет отправлено сообщение, которое осуществит вызов функцию обратного вызова принимающей стороны. Функции обратного
вызова представляют особой обработчик с множественным ветвлением, каждая ветвь которого обрабатывает соответствующую ей транзакцию. Если транзакция не поддерживается, то функция обратного вызова
должна вернуть нулевое значение, иначе – один из допустимых для обработанной транзакции, кодов возврата.
Функция обратного вызова имеет следующий заголовок:
HDDEDATA EXPENTRY DdeCallbackFunction(
WORD wType,
WORD wFmt,
HCONV hConv,
HSZ hsz1,
HSZ hsz2,
HDDEDATA hData,
DWORD dwData1,
DWORD dwData2
);
где:
- wType — Код транзакции. Коды транзакций предопределены протоколом DDE. Значения и названия соответствующих им констант можно посмотреть в технической документации. Забегая вперед,
отмечу, что в нашем примере будут использоваться транзакции XTYP_ADVSTART для запуска потока данных по каналу, XTYP_ADVSTOP – для остановки потока данных,
XTYP_ADVDATA – транзакция с уведомлением наличии данных от сервера. - wFmt – формат данных (в нашем случае данные представляют собой текстовую строку, поэтому этому параметру при вызове будет присвоено значение CF_TEXT, равное единице).
- hConv – идентификатор канала. Этот идентификатор получен при создании канала.
- hsz1 – идентификатор строки названия раздела.
- hsz2 – идентификатор строки названия элемента данных.
- hData – идентификатор глобальной области в памяти, где находятся данные от сервера. Данные необходимо получить с помощью функции DdeGetData.
В свою очередь функция запуска транзакции DdeClientTransaction имеет следующий заголовок:
HDDEDATA WINAPI DdeClientTransaction(
void FAR* pData,
DWORD cbData,
HCONV hConv,
HSZ hszItem,
UINT uFmt,
UINT uType,
DWORD dwTimeout,
DWORD FAR* pdwResult
);
- pData — ссылка на данные, передаваемые транзакцией.
- cbData — размер передаваемых данных
- hConv — идентификатор канала связи, полученный заранее функцией DdeConnect
- hszItem — идентификатор элемента данных, в нашем случае — ячейки. Идентификатор должен быть получен заранее, с помощью функции DdeCreateStringHandle.
- uFmt — формат данных. Для случая с Excel указывается константа CF_TEXT(1)
- uType — код транзакции. Определяется комбинацией битовых флагов. В случае организации горячего канала выполняется транзакция XTYP_ADVSTART — для начала цикла получения данных
из ячейки (подписки на ячейку) и XTYP_ADVSTOP — для прекращения цикла получения данных из ячейки (отписки от ячейки). - dwTimeout — тайм-аут для синхронных транзакций — максимальное время выполнения синхронной транзакции. Если в качестве параметра передать 0, то будет запущена асинхронная транзакция. При
запуске синхронной транзакции, приложение ждет ее завершения. При этом максимальное время выполнения транзакции определяется значением параметра. При запуске асинхронной транзакции приложение не ждет
завершения транзакции и продолжает свою работу. По завершению транзакции клиент получит транзакцию XTYP_XACT_COMPLETE. - pdwResult — ссылка на двойное слово, в которое будет записан код завершения транзакции. Изначально эта переменная должна быть приравнена к нулю. (По рекомендации Microsoft, не
рекомендуется использовать этот параметр, так как, возможно, в дальнейшем он поддерживаться не будет). - Возвращает нулевое значение, если транзакция была выполнена с ошибкой, или ненулевую величину, смысл которой зависит от транзакции, (В нашем случае будет возвращена единица) при нормальном
выполнении.
3. Отображение библиотеки DDEML в .NET
Библиотека DDEML представляет собой 32-разрядную библиотеку платформы Win32. Ее функции не могут быть вызваны непосредственно из приложения .NET. Для того, чтобы иметь возможность работать с этой
библиотекой, нужно создать ее «отображение» в среде .NET, используя специальные средства. Таким образом, типы и структуры, с которыми работает библиотека будут автоматически преобразовываться
средствами .NET в типы .NET и наоборот. Для доступа к функциям используется класс DLLImportAttribute, который описан в пространстве имен System.Runtime.InteropServices. Что касается типов параметров
функций, как уже было сказано, среда .NET в большинстве случаев автоматически осуществляет все необходимые преобразования. В таблице 1 показаны подобные преобразования:
Win32 | .NET |
DWORD | uint |
HCONV | IntPtr |
HSZ | IntPtr |
UINT | uint |
DWORD * | ref uint |
void* far | IntPtr или можно указать [Out] byte[], где [Out] – класс System.Runtime.InteropServices, который организовывает передачу данных от вызываемого объекта к вызывающему. |
Для функции обратного вызова необходимо создать делегат, который имеет соответствующую сигнатуру, и указать его в качестве параметра в функции DdeInitialize:
/// <summary>
/// Делегат функции обратного вызова DDE
/// </summary>
internal delegate IntPtr DDECallBackDelegate(
uint wType, // Код транзакции
uint wFmt, // Формат данных
IntPtr hConv, // Идентификатор канала
IntPtr hsz1, // Идентификатор строки (в нашем случае, строки раздела)
IntPtr hsz2, // Идентификатор строки (в нашем случае, элемента данных)
IntPtr hData, // Идентификатор глобальной области данных, где находятся данные
uint dwData1, // Дополнительный параметр (В нашей работе не рассматривается)
uint dwData2 // Дополнительный параметр (В нашей работе не рассматривается)
);
При этом отображение функции DdeInitialize в среде .NET будет выглядеть так:
internal class DDEML
{
[DllImport(«user32.dll», EntryPoint=»DdeInitialize», CharSet=CharSet.Ansi)]
internal static extern uint DdeInitialize(
ref uint pidInst, DDECallBackDelegate pfnCallback, uint afCmd, uint ulRes);
…
}
Ниже, я привожу пример вызова функции DdeInitialize в среде .NET:
public class ExcelDDEHotConnection
{
// Ссылка на делегат-переходник для функции обратного вызова DDE
private DDECallBackDelegate _DDECallBack = null;
// Обработчик функции обратного вызова
private IntPtr DDECallBack(
uint uType,
uint uFmt,
IntPtr hConv,
IntPtr hsz1,
IntPtr hsz2,
IntPtr hData,
uint dwData1,
uint dwData2)
{
switch(uType)
{
// Мы обрабатываем только транзакции с данными
case DDEML.XTYP_ADVDATA:
// Выполняем обработку транзакции
…
// Возвращаем управление
return new IntPtr(DDEML.DDE_FACK);
}
// Все остальные транзакции мы не обрабатываем
return IntPtr.Zero;
}
// Идентификатор приложения
private uint idInst = 0;
public ExcelDDEHotConnection()
{
// Создаем делегат-переходник для функции обратного вызова
_DDECallBack = new DDECallBackDelegate(DDECallBack);
// Регистрация в библиотеке DDEML
DDEML.DdeInitialize(ref idInst, _DDECallBack, 0, 0);
// выполняем остальные инициализирующие действия
…
}
…
}
Прошу обратить внимание на то, что ссылку на делегат функции обратного вызова мы храним все время работы с DDEML. Если этого не сделать, то сборщик мусора .NET уничтожит этот делегат при очередной
сборке мусора, что приведет к тому, что во внутренних структурах библиотеки DDEML ссылка на функцию обратного вызова будет указывать на уничтоженный объект. Это, естественно, вызовет
NullPointerException при попытке библиотеки DDEML вызвать функцию обратного вызова. Поэтому вызов функции DdeInitialize следующего вида нежелателен:
// Регистрация в библиотеке DDEML
DDEML.DdeInitialize(ref idInst, new DDECallBackDelegate(DDECallBack), 0, 0);
Забегая вперед, отмечу, что для отображения необходимых функций и констант библиотеки DDEML в компоненте ExcelDDEHotConnection служит класс DDEML.
Ниже приведен список остальных функций для работы с DDE:
Делегат функции обратного вызова:
internal delegate IntPtr DDECallBackDelegate(
uint wType, //Код транзакции
uint wFmt, // Формат данных
IntPtr hConv, // Идентификатор канала
IntPtr hsz1, // Идентификатор строки (в нашем случае, строки раздела)
IntPtr hsz2, // Идентификатор строки (в нашем случае, элемента данных)
IntPtr hData, // Идентификатор глобальной области данных, где находятся данные
uint dwData1, // Дополнительный параметр (В нашей работе не рассматривается)
uint dwData2 // Дополнительный параметр (В нашей работе не рассматривается)
);
Отображение функции DdeInitialize:
[DllImport(«user32.dll», EntryPoint=»DdeInitialize», CharSet=CharSet.Ansi)]
internal static extern uint DdeInitialize(
ref uint pidInst, DDECallBackDelegate pfnCallback, uint afCmd,uint ulRes);
Отображение функции DdeUninitialize:
[DllImport(«user32.dll», EntryPoint=»DdeUninitialize», CharSet=CharSet.Ansi)]
internal static extern bool DdeUninitialize(uint idInst);
Отображение функции DdeCreateStringHandle:
[DllImport(«user32.dll», EntryPoint=»DdeCreateStringHandle», CharSet=CharSet.Ansi)]
internal static extern IntPtr DdeCreateStringHandle(
uint idInst, string psz,int iCodePage);
Отображение функции DdeFreeStringHandle:
[DllImport(«user32.dll», EntryPoint=»DdeFreeStringHandle», CharSet=CharSet.Ansi)]
internal static extern bool DdeFreeStringHandle(uint idInst, IntPtr hsz);
Отображение функции DdeConnect:
[DllImport(«user32.dll», EntryPoint=»DdeConnect», CharSet=CharSet.Ansi)]
internal static extern IntPtr DdeConnect(
uint idInst, IntPtr hszService, IntPtr hszTopic, IntPtr pCC);
Отображение функции DdeDisconnect:
[DllImport(«user32.dll», EntryPoint=»DdeDisconnect», CharSet=CharSet.Ansi)]
internal static extern bool DdeDisconnect(IntPtr hConv);
Отображение функции DdeClientTransaction:
[DllImport(«user32.dll», EntryPoint=»DdeClientTransaction», CharSet=CharSet.Ansi)]
internal static extern IntPtr DdeClientTransaction(
IntPtr pData, uint cbData, IntPtr hConv, IntPtr hszItem, uint uFmt,uint uType,
uint dwTimeout, ref uint pdwResult);
Отображение функции DdeGetData:
[DllImport(«user32.dll», EntryPoint=»DdeGetData», CharSet=CharSet.Ansi)]
internal static extern uint DdeGetData(
IntPtr hData, [Out] byte[] pDst, uint cbMax, uint cbOff);
Отображение функции DdeQueryString:
[DllImport(«user32.dll», EntryPoint=»DdeQueryString», CharSet=CharSet.Ansi)]
internal static extern uint DdeQueryString(
uint idInst, IntPtr hsz, StringBuilder psz, uint cchMax, int iCodePage);
4. Организация горячего канала Excel – приложение DDE.
В этой главе вкратце описано, как осуществить корректное подключение и отключение от ячеек Excel. Например, необходимо получить доступ к ячейке, расположенной во втором столбце и первой строке на
странице с названием «Лист1» рабочей книги «Книга1». Для начала необходимо зарегистрироваться в библиотеке DDEML и получить программный идентификатор idInst:
// Создаем делегат-переходник для функции обратного вызова
_DDECallBack = new DDECallBackDelegate(DDECallBack);
// Регистрация в библиотеке DDEML
DDEML.DdeInitialize(ref idInst, _DDECallBack, 0, 0);
После этого создаем канал связи с нужным разделом. В нашем случае, как было упомянуто выше, название сервиса: «EXCEL», а название раздела «[Книга1.xls]Лист1». Необходимо
помнить, что расширение файла необходимо указывать, если эта книга открыта из файла. Если осуществляется подключение к созданной, но еще не сохраненной книге, то расширение не указывается.
// Формируем название раздела
string szTopic = “[Книга1.xls]Лист1”;
// Получение идентификатора сервиса
IntPtr hszService = DDEML.DdeCreateStringHandle(_idInst, «EXCEL», DDEML.CP_WINANSI);
// Получаем идентификатор раздела
IntPtr hszTopic = DDEML.DdeCreateStringHandle(_idInst, szTopic, DDEML.CP_WINANSI);
// Подключаемся к разделу
IntPtr hConv = DDEML.DdeConnect(_idInst, hszService, hszTopic , (IntPtr) null);
// Проверяем результат
if(hConv!=IntPtr.Zero)
{
…
}
// Освобождаем идентификаторы строк
DDEML.DdeFreeStringHandle(_idInst, hszService);
DDEML.DdeFreeStringHandle(_idInst, hszTopic);
После создания канала информируем Excel о том, чтобы приложение получало содержимое нужной ячейки, как только оно изменится («горячий канал»). Для этого посылаем Excel транзакцию
XTYP_ADVSTART:
// Формируем название ячейки
string szItem = “R1C2”;
// Создаем идентификатор строки
IntPtr hszItem = DDEML.DdeCreateStringHandle(_idInst, szItem, DDEML.CP_WINANSI);
// Подписываемся на тему
uint pwdResult = 0;
IntPtr hData = DDEML.DdeClientTransaction((IntPtr)null, 0, hConv, hszItem, DDEML.CF_TEXT, DDEML.XTYP_ADVSTART, 1000,ref pwdResult);
if(hData!=IntPtr.Zero)
{
…
}
// Освобождаем идентификатор строки
DDEML.DdeFreeStringHandle(_idInst, hszItem);
Отключение производим в обратном порядке, сначала информируем сервер о том, что данные из ячейки нам больше не нужны, посылая Excel транзакцию XTYP_ADVSTOP:
// Формируем название ячейки
string szItem = “R1C2”;
// Создаем идентификатор строки
IntPtr hszItem = DDEML.DdeCreateStringHandle(_idInst, szItem, DDEML.CP_WINANSI);
// Подписываемся на тему
uint pwdResult = 0;
IntPtr hData = DDEML.DdeClientTransaction((IntPtr)null, 0, hConv, hszItem, DDEML.CF_TEXT, DDEML.XTYP_ADVSTOP, 1000, ref pwdResult);
if(hData!=IntPtr.Zero)
{
…
}
// Освобождаем идентификатор строки
DDEML.DdeFreeStringHandle(_idInst, hszItem);
После завершения транзакции, закрываем канал:
//Закрываем канал
DDEML.DdeDisconnect(hConv);
И завершаем работу с библиотекой DDEML:
// Отключаемся от DDEML
DDEML.DdeUninitialize(idInst);
Необходимо отметить, что для всех трех режимов создается одинаковый канал. При этом для одних ячеек мы можем указывать «горячий» режим, для других – «теплый», а с третьими работать по явному
запросу. Для того, чтобы включить «теплый» канал, необходимо отправить Excel транзакцию, код которой состоит из побитной комбинации кода транзакции XTYP_ADVSTART и флага
XTYPF_NODATA.
5. Компонент ExcelDDEConnection.
Компонент ExcelDDEConnection представляет готовое решение, позволяющее организовать «горячий» канал DDE между приложением .NET и Excel. Компонент состоит из нескольких классов, главный из
которых — ExcelDDEHotConnection. Экземпляр данного класса автоматически инициализируется в библиотеке DDEML при создании и отключается от ее при завершении своего существования.
Ниже приведены основные методы и свойства класса ExcelDDEHotConnection:
Название |
Описание |
ExcelDDEHotConnection() |
Конструктор. Осуществляет регистрацию в библиотеке DDEML |
TopicDescriptorCollection Topics |
Свойство. Ссылка на коллекцию разделов. Раздел адресуется названием книги и названием страницы |
void Dispose() |
Завершить работу объекта. Закрывает все каналы и производит отключение от библиотеки DDEML |
event AdviseDelegate Data |
Событие. Происходит при изменении содержимого любой из подписанных ячеек. Событие вызывается для каждой изменившейся ячейки. |
Коллекция разделов TopicDescriptorCollection.
Коллекция разделов представляет собой набор объектов, описывающий разделы. При добавлении раздела в коллекцию, происходит автоматическое создание канала, а при удалении – закрытие канала.
Коллекция не допускает дублирование одинаковых разделов. Раздел добавляется в коллекцию только в том случае, если удалось создать канал для этого раздела.
Название | Описание |
Result Add(TopicDescriptor descriptor) |
Добавить дескриптор раздела в коллекцию. В качестве параметра передается дескриптор раздела. При добавлении происходит попытка создать канал с разделом, который описывает данный дескриптор. Если |
Result Add(string book, string sheet) |
Добавить дескриптор раздела в коллекцию. В качестве параметра передается название книги book и страницы — sheet. При добавлении происходит попытка создать канал с разделом, который |
Result Remove(TopicDescriptor descriptor) |
Удалить дескриптор из коллекции. В качестве параметра передается дескриптор раздела. При удалении происходит закрытие канала связи, при этом для всех ячеек раздела выполняется транзакция |
Result Remove(string book, string sheet) |
Удалить дескриптор из коллекции. В качестве параметра передается название книги — book и страницы — sheet. При удалении происходит закрытие канала связи, при этом для всех ячеек |
void Clear() |
Метод удаляет все дескрипторы разделов из коллекции, при этом происходит закрытие всех каналов. |
void Dispose() |
Метод завершает работу коллекции – закрывает все каналы и удаляет все дескрипторы. |
int Count |
Свойство возвращает количество разделов, зарегистрированных в коллекции. |
TopicDescriptor this[string book, string sheet] |
Возвращает дескриптор по названию книги и страницы. |
TopicDescriptor this[int index] |
Возвращает дескриптор по его порядковому номеру в коллекции. |
TopicDescriptor this[string topic] |
Возвращает дескриптор по названию раздела в формате Excel. |
Дескриптор раздела TopicDescriptor.
Экземпляр класса описывает раздел данных. Каждый раздел содержит в себе коллекцию элементов данных типа ItemDescriptor, описывающих ячейки. При добавлении ячейки происходит отправка Excel
транзакции на подписку на эту ячейку, при удалении – транзакция на завершение работы с ячейкой.
Название | Описание |
TopicDescriptor(string book, string sheet) |
Конструктор. В качестве параметров получает название книги и страницы. |
Result Add(ItemDescriptor descriptor) |
Добавить дескриптор ячейки в список подписанных ячеек. В качестве параметров получает дескриптор ячейки . При добавлении происходит отправка транзакции Excel на подписку на эту ячейку. В случае |
Result Add(int row, int col) |
Добавить дескриптор ячейки в список подписанных ячеек. В качестве параметров метод получает номер строки row и номер столбца col. При добавлении происходит отправка транзакции Excel |
Result Remove(ItemDescriptor descriptor) |
Удалить ячейку из списка подписанных ячеек. В качестве параметров принимает дескриптор ячейки. При удалении происходит отправка транзакции Excel на отписку от данной ячейки. Если при выполнении |
Result Remove(int row, int col) |
Удалить ячейку из списка подписанных ячеек. В качестве параметров принимает номер строки row и номер столбца col. При удалении происходит отправка транзакции Excel на отписку от |
void Clear() |
Метод отменяет подписку на все ячейки из списка подписанных ячеек и очищает список. |
void Dispose() |
Метод отменяет подписку на все ячейки из списка подписанных ячеек и очищает список. |
int Count |
Свойство возвращает количество дескрипторов ячеек в списке. |
string Book |
Свойство возвращает название книги раздела, который описывает данный дескриптор. |
string Sheet |
Свойство возвращает название страницы раздела, который описывает данный дескриптор. |
string Topic |
Свойство возвращает название раздела в формате Excel, который описывает данный дескриптор. |
ItemDescriptor this[int index] |
Получить дескриптор ячейки по его порядковому номеру в списке подписанных ячеек. |
ItemDescriptor this[int row, int col] |
Получить дескриптор ячейки по номеру строки row и столбца col ячейки. |
ItemDescriptor this[string item] |
Получить дескриптор ячейки по названию в формате Excel. |
Дескриптор ячейки ItemDescriptor.
Описывает ячейку Excel.
Название | Описание |
ItemDescriptor(int row, int col) |
Конструктор. Создает дескриптор ячейки строки номер row и столбца номер col. |
int Row |
Номер строки ячейки |
int Col |
Номер столбца ячейки |
string Item |
Название ячейки в формате MS Excel |
byte[] Data |
Массив с текущим содержимым ячейки. |
Аргумент события Data – AdviseEventArgs.
Экземпляр класса передается в качестве аргумента в событии Data.
Название | Описание |
ItemDescriptor ItemDescriptor |
Дескриптор ячейки, в которой произошли изменения. Получить новое содержимое ячейки можно, воспользовавшись свойством Data |
TopicDescriptor TopicDescriptor |
Дескриптор раздела, в котором находится ячейка, содержимое которой изменилось. |
6. Литература и ссылки.
- Фролов А.В. Фролов Г.В. Операционная система Microsoft Windows 3.1. для программиста. Дополнительные главы – М.: «ДИАЛОГ-МИФИ» 1995. (Библиотека системного программиста. Т.17)
- Троелсен Э. С# и платформа .NET. Библиотека программиста – СПб.: Питер, 2004.
- http://custom.programming-in.net – Описание отображения функций WINAPI в .NET.
- Техническая документация MSDN – протокол DDE.
Обмен приложение — МРВ как DDE-сервер
Запрос значения атрибута R
Запрос значений других атрибутов
Задание значений атрибутов
При обмене с локальным МРВ из приложения –
DDE-клиента – возможно:
чтение атрибута (0, R) каналов (в режиме
REQUEST или ADVISE);
чтение (REQUEST) и запись (POKE) любых других
атрибутов каналов.
Запись значений в атрибуты, вычисляемые в канале,
не имеет смысла.
Если в узле существуют каналы с одинаковым именем,
то выполнение запроса влияет только на канал с наименьшим ID.
Если клиентом DDE является Excel, то DDE-обмен с
МРВ может быть сконфигурирован с помощью формул Excel и/или
макросов VBA Формула Excel может быть использована только для
запроса значения (в режиме ADVISE или REQUEST) – в этом случае она
имеет следующий формат:
=<server>|<topic>!<item>
где
server – имя сервера в формате
RTM<k>, где k – индивидуальный
номер узла;
topic – тема запроса (см. примеры
ниже);
item – имя канала или уточненное имя
атрибута.
Уточненное имя атрибута имеет следующий формат:
<имя
канала>.<номер
атрибута>
Любой из перечисленных параметров DDE-формулы Excel
следует заключить в одинарные кавычки в следующих случаях:
если параметр содержит пробелы или служебные
символы, которые используются в формулах Excel (двоеточие, минус и
т.п.);
если параметр имеет вид ссылки на ячейку (примером
такого параметра может служить имя канала ch1).
В приведенных ниже примерах узел имеет
индивидуальный номер 2.
Запрос
значения атрибута R
Для запроса реального значения канала pila в
режиме ADVISE в ячейку таблицы Excel нужно записать следующую
формулу:
=RTM2|GET!pila
В этом режиме значение в ячейке обновляется
автоматически.
В режиме ADVISE монитор посылает клиенту значение
канала при каждом его пересчете.
Для запроса реального значения канала pila в
режиме REQUEST в ячейку таблицы Excel нужно записать одну из
следующих формул:
=RTM2|PUT!pila
=RTM2|PUT!pila.0
Во второй формуле тема запроса может быть
произвольной. В этом режиме значение канала запрашивается и
записывается в ячейку однократно при исполнении формулы.
Запросить значение атрибута (0, R) канала в
режиме REQUEST можно также с помощью одного из следующих макросов
VBA Excel (во втором макросе тема запроса может быть
произвольной):
Sub read_pila_R()
chNum = Application.DDEInitiate(«RTM2»,
«GET»)
Worksheets(«Sheet1»).Range(«F1») =
Application.DDERequest(chNum, «pila»)
Application.DDETerminate chNum
End Sub
Sub read_pila_R()
chNum = Application.DDEInitiate(«RTM2»,
«LL»)
Worksheets(«Sheet1»).Range(«F1») =
Application.DDERequest(chNum, «pila.0»)
Application.DDETerminate chNum
End Sub
Запрос
значений других атрибутов
Для запроса значения атрибутов (3, C),
(4, I), (7, P); (8, W), (26, HL), (27,
LL), (28, HA), (29, LA), (30, HW), (31,
LW), (79, CODE) и (127, NAME) могут быть
использованы запросы двух видов:
topic – короткое имя атрибута, item
– имя канала;
topic – произвольное значение (из
допустимых), item – уточненное имя атрибута.
Для запроса значений других атрибутов могут быть
использованы запросы только второго вида.
Таким образом, для запроса, например, верхнего
предела (26, HL) канала ch1 в ячейку таблицы Excel
нужно записать одну из следующих формул:
=RTM2|HL!’ch1′
=RTM2|PUT!ch1.26
Запросить значение атрибута из приведенного выше
перечня (ниже в примерах запрашивается кодировка) можно также с
помощью одного из следующих макросов (для запроса значений других
атрибутов нужно использовать второй макрос):
Sub read_ch1_CODE()
chNum = Application.DDEInitiate(«RTM2»,
«CODE»)
Worksheets(«Sheet1»).Range(«F1») =
Application.DDERequest(chNum, «ch1»)
Application.DDETerminate chNum
End Sub
Sub read_ch1_CODE()
chNum = Application.DDEInitiate(«RTM2»,
«LL»)
Worksheets(«Sheet1»).Range(«F1») =
Application.DDERequest(chNum, «ch1.79»)
Application.DDETerminate chNum
End Sub
Задание
значений атрибутов
Для задания значения атрибутов (2, In), (3,
C), (4, I), (7, P); (8, W), (26,
HL), (27, LL), (28, HA), (29, LA), (30,
HW), (31, LW), (79, CODE) и (127, NAME)
могут быть использованы макросы двух видов:
topic – короткое имя атрибута, item
– имя канала;
topic – произвольное значение (из
допустимых), item – уточненное имя атрибута.
Для задания значений других атрибутов могут быть
использованы макросы только второго вида.
Таким образом, для записи в атрибут (2, In)
канала ch1 значения из ячейки C3 таблицы Excel можно
использовать один из следующих макросов:
Sub write_ch1_In()
chNum = Application.DDEInitiate(«RTM2»,
«PUT»)
Application.DDEPoke chNum, «ch1»,
Worksheets(«Sheet1»).Range(«C3»)
Application.DDETerminate chNum
End Sub
Sub write_ch1_In()
chNum = Application.DDEInitiate(«RTM2»,
«HA»)
Application.DDEPoke chNum, «ch1.2»,
Worksheets(«Sheet1»).Range(«C3»)
Application.DDETerminate chNum
End Sub
Download a Free Trial Version.It allows you to try all features! Data logger plugins can be downloaded separately.
Problem scenario:
The lab equipment has an RS232 port, which outputs measurement data every 10 minutes. I want to use parsed values in forms, formulas and my VBA logic in a Microsoft Excel spreadsheet.
Requirements:
- Advanced Serial Data Logger Professional, Enterprise, or a trial version;
- DDE Server
It is assumed that:
You can work in Microsoft Excel, specifically create and use forms.
Solution:
Note: This tutorial is based on the previous tutorial named «Serial port data and Excel. Real-time charting». This example will use the parser settings and the data example from this tutorial. Therefore, you may read it before continuing.
Note 2: Office 365 or Office 2019+ may have DDE disabled. You should enable it in the following menu: File → Options →
- Advanced → General → → Ignore other applications that use Dynamic Data Exchange (DDE) → Off.
- Trust Center → Trust Center Settings → External content, set to Enable or Prompt
- Trust Center → Trust Center Settings → External content → Enable Dynamic Data Exchange server lookup → On
The parser is ready. We’ve tested it in the previous tutorial. If the parser had been correctly set up, then you should see all parser items and their values (fig.1) in the DDE server window below.
Fig.1. Serial port data, Excel, and DDE. DDE server window.
All parser items are now ready for the usage from Microsoft Excel. The variable name is displayed in the «Name» column. You should know the DDE server name and the topic name to retrieve values via DDE. This information exists on the following page (fig.2 pos.1). Please, note that the server name may be different on your PC.
Fig.2. Serial port data, Excel, and DDE. DDE server parameters.
Click the «OK» button and close the DDE Server plugin configuration window and the «OK» button in the options window.
Because we want to draw a chart in a real-time environment, then we should prepare an Excel file before the next step. In this example, we’ve created a diagram with three plots. Each plot is based on data from columns A, B, or C. We’ll place FLOW1, VOLW1, and TEMP1 to a corresponding column, and the chart will be automatically updated every time. We’ll use the last 30 values on the diagram only. In this example, we’ve placed three links to DDE items to cells D21-F21 like «=testcore|ddesrv!srviFLOW1» (w/o quotes) (fig.3 and 4). As you can see, the server name, the topic name, and the variable name are specified in this DDE link.
Fig.3. Serial port data, Excel, and DDE. DDE links.
After you’ve placed the DDE link to the spreadsheet, Microsoft Excel will connect to the data logger and retrieve the data automatically. If the data logger is already started, Microsoft Excel will try to read the existing instance data. If the data logger didn’t receive and didn’t parse the data yet, Microsoft Excel will display a text about the broken link and read a value after successful parsing.
We are now ready to create a VBA code, which will process the DDE data. This code will copy DDE values to columns, move old values, and add rows to the history list (fig.4, 5).
Fig.4. Serial port data, Excel, and DDE. Source spreadsheet.
The figure above contains the following elements:
- The DDE data will be placed here, and it is a source for plots in this example;
- Diagram;
- History list.
VBA code from the Excel file
Private Sub btnClear_Click() lbHistory.Clear 'MsgBox (CStr(Target.Row) + ":" + CStr(Target.Column)) End Sub Public Sub Process_Value(ByVal X_Coord As Long, ByVal ValueName As String, ByVal Value As Variant) Dim TmpValue As Variant Dim LastIndex As Long ' if we've read and non-numeric value, then replace it by ### in the grid If IsNumeric(Value) Then TmpValue = Value Else TmpValue = "####" End If ' search for an empty cell LastIndex = 0 For I = 1 To 30 If Cells(I, X_Coord).Value = "" Then LastIndex = I Exit For End If Next ' if we did not find an empty cell the move old value If LastIndex = 0 Then LastIndex = 30 For I = 2 To 30 Cells(I - 1, X_Coord).Value = Cells(I, X_Coord).Value Next End If ' place new value to the cell Cells(LastIndex, X_Coord).Value = TmpValue ' add new value to the history list lbHistory.AddItem (ValueName + ": " + CStr(TmpValue)) End Sub Private Sub btnClearData_Click() For I = 1 To 30 Cells(I, 1).Value = "" Cells(I, 2).Value = "" Cells(I, 3).Value = "" Next End Sub Private Sub btnClearHistory_Click() lbHistory.Clear End Sub Private Sub Worksheet_Calculate() On Error GoTo ErrHandler Application.EnableEvents = False Call Process_Value(1, "FLOW1", Cells(21, 4).Value) Call Process_Value(2, "VOLW1", Cells(21, 5).Value) Call Process_Value(3, "TEMP1", Cells(21, 6).Value) Application.EnableEvents = True ErrHandler: Application.EnableEvents = True End Sub
The Excel file, which we’ve created you can download here and use it in your work.
OK, all settings have been completed, and we are ready to capture data to an Excel file.
Connect your device and power it on if necessary. Check to see if you can receive a data block from the specified serial port. If the parser had been correctly set up, then you should see all parser items values (Fig.5) in the Excel spreadsheet.
Fig.5 Serial port data, Excel, and DDE. Results.
The figure above contains the following elements:
- Last 30 values of the DDE data;
- Last DDE values;
- DDE data history.
The Excel file with all values and plots you can download here.
This data retrieving method is complex and might prove to be difficult for the unskilled user, but allows you to process the data as you want. Later, you may make the following improvements in this example like:
- Mark some values by color;
- Add date/time stamp to the history list and auto-clear the list;
- Use formulas and calculations.
Related articles: Send data to Excel through the DDE interface
- RS232 to Excel. Four methods to send data from RS232 to Excel Plugins: Various
- Write data to an XLS file without Microsoft Excel installed Plugins: Local database
- Send data to Excel through the DDE interface Plugins: DDE Server
- Export data and charting in Excel in real-time Plugins: Direct Excel Connection
- Write data to a CSV text file Plugins: Local database, DDE Server
- Export real-time data and charting in OpenOffice or LibreOffice Calc Plugins: Direct OpenOffice/LibreOffice Connection
- Write data to an XLS file without Excel Tags: advanced export, rich formatting, many options. Plugins: Excel Export Pro
- Write data to an XLS file without Excel Tags: variant #2. Plugins: Local database
Содержание
- Связь по протоколу DDE с приложением MS Windows на примере Excel
- Функция DDE
- Замечания
- Получение данных в Excel через интерфейс DDE с помощью модуля DDE Server
Связь по протоколу DDE с приложением MS Windows на примере Excel
МРВ как DDE-сервер
Организуем запрос реальных значений каналов узла разработанного проекта приложением MS Windows в качестве, которого выберем книгу MS Excel. Для этого выполним:
· запуск приложения MS Excel;
· запишем в двух ячейках первого столбца запросы на получение данных:
=RTM0|GET!Параметр
=RTM0|GET!Управление
где 0 – индивидуальный номер узла в проекте;
· запустим на исполнение узел АРМ RTM _1;
· в меню таблицы MS Excel Правка выберем команду Связи, выделим оба параметра и нажмем кнопку Обновить, после чего закроем окно кнопкой ОК.
Убедимся, что значения в ячейках книги Excel изменяются вместе с соответствующими реальными значениями каналов узла (значения канала Параметр меняется постоянно, а канала Управление – после введения нового значения с помощью ГЭ Кнопка):
МРВ как DDE-клиент
где в атрибуте Имя:
· Excel – имя приложения;
· Sheet1 – имя листа книги MS Excel;
· R3C3 – адрес ячейки.
ADVISE – режим посылки клиенту значения при каждом его изменении.
Таким образом, в результате будем наблюдать следующее:
Подключение реального внешнего модуля ввода сигналов
Создание канала аналогового ввода данных от модуля I-7017
Создать один DCS-модуль аналогового ввода:
открыть слой Источники/приемники и через ПК создать в нем группу Распределенное УСО ( DCS ):
открыть созданную группу I 7000_1 двойным щелчком ЛК и через ПК создать в ней подгруппу I 7017#1:
открыть созданную подгруппу I 7011#1 двойным щелчком ЛК и перейти к созданным компонентам-источникам модуля I-7017:
выделить ЛК компонент AIn #1 и двойным щелчком ЛК перейти в режим редактирования его атрибутов;
Дата добавления: 2019-11-16 ; просмотров: 180 ;
Источник
Функция DDE
С помощью функции DDE можно начать сеанс DDE с другим приложением, запросить из него информацию и вывести ее в элементе управления в форме или отчете.
Например, функцию DDE можно использовать в свойстве Данные текстового поля для отображения данных из указанной ячейки листа Microsoft Office Excel 2007.
DDE( приложение, документ, раздел )
Функция DDE имеет следующие аргументы:
Строковое выражение, которое определяет приложение, участвующее в сеансе DDE. Как правило, приложение — это имя EXE-файла (без расширения EXE) приложения Microsoft Windows, такого как Excel. Например, чтобы запустить сеанс DDE с приложением Excel, нужно задать для аргумента приложение значение «Excel».
Строковое выражение, которое представляет имя документа, распознаваемое приложением. Аргумент документ часто является документом или файлом данных. Список допустимых значений см. в документации приложения.
Строковое выражение, которое представляет имя элемента данных, распознаваемое приложением. Список допустимых разделов см. в документации приложения.
Замечания
Функция DDE пытается запустить сеанс DDE с указанным приложением и документом и запрашивает данные в разделе. Если ей это удается, функция DDE возвращает строку, содержащую запрашиваемые данные.
При запросе данных из Excel разделом может быть идентификатор строки и столбца, например » «R1C1» «, или имя диапазона ячеек. В следующем примере функция DDE запрашивает данные ячейки в строке 1 и столбце 1 листа Excel. Это выражение можно ввести в поле Данные текстового поля на странице свойств элемента управления:
=DDE(«Excel», «Sheet1», «R1C1»)
Функцию DDE можно использовать только в свойстве Данные текстового поля, группы переключателей, флажка или поля со списком. Нельзя вызывать функцию DDE из модуля Visual Basic для приложений (VBA).
При использовании функции DDESend элемент управления становится доступным только для чтения в режиме формы и предварительного просмотра. Например, если вы укажете функцию DDE в текстовом поле, в нем будет невозможно изменять текст. Текст будет необходимо редактировать в другом приложении. Так как свойство Данные доступно только для чтения в режиме формы и предварительного просмотра, изменять элемент управления нужно через Конструктор.
Максимальное количество сеансов DDE, которые можно открыть одновременно, определяется памятью и ресурсами компьютера и Microsoft Windows. Если не удается начать сеанс, так как другое приложение не запущено или не распознает указанный документ либо уже достигнуто максимальное количество сеансов, функция DDE возвращает NULL.
Примечание: Другое приложение может быть настроено таким образом, что будет игнорировать запросы на сеанс DDE. В этом случае функция DDE возвращает значение NULL. Кроме того, можно настроить Access таким образом, чтобы он игнорировал запросы от других приложений: в меню Файл выберите команду Параметры Access, в диалоговом окне Параметры приложения выберите пункт Дополнительно. В группе Операции DDE установите флажок Пропуск команд DDE.
Для управления объектами других приложений из Access можно воспользоваться автоматизацией.
В следующей таблице показано, как работает функция DDE при использовании с каждым из элементов управления.
Эле мент управления
Аргумент раздел может ссылаться на текст или числа. Если раздел ссылается более чем на одну единицу данных, например на именованный диапазон листа Excel, который содержит несколько ячеек, функция DDE возвращает первую запись. Эту функцию можно использовать с текстовым полем, чтобы отображать данные, содержащиеся в ячейке листа.
Функция DDE заполняет поле со списком данными, на которые ссылается аргумент раздел. Вводить данные в текстовую часть поля нельзя. Функцию DDE можно использовать с полем со списком, чтобы отобразить список стран и регионов, хранящийся в листе Excel.
Для свойства OptionValue каждого переключателя в группе задан номер. Как правило, номер первого переключателя — 1, второго — 2 и т. д. Возвращаемое функцией DDE число определяет, какой переключатель будет выбран.
Например, если функция DDE возвращает 2, будет выбран второй переключатель. Если функция возвращает значение, не соответствующее ни одному из возможных значений свойства OptionValue, переключатель выбран не будет. Если раздел ссылается более чем на одну единицу данных, например на именованный диапазон листа Excel, который содержит несколько ячеек, функция DDE возвращает первую запись.
Источник
Получение данных в Excel через интерфейс DDE с помощью модуля DDE Server
Верси для печати
Сценарий проблемы:
Лабораторный инструмент имеет RS232 порт, через который выводятся данные каждые 10 минут. Я хотел бы использовать значения из выводимых данных в формах, формулах и моих скриптах VBA в документах Microsoft Excel.
Требования:
Подразумевается что:
Вы настроили параметры связи с устройством (скорость, количество бит данных, контроль передачи и т.п.) в логгере и можете принимать данные без каких либо ошибок.
Вы можете работать в Microsoft Excel, в частности создавать и использовать формы.
Решение:
Замечание: Этот пример базируется на предыдущем примере с именем «Данные из последовательного порта и Excel. Рисование графиков в реальном времени». Этот пример будет использовать настройки парсера и пример данных их этого примера. Поэтому вы можете прочитать этот обучающий пример перед продолжением чтения этого примера.
Парсер готов и мы протестировали его в предыдущем примере. Если парсер верно настроен, то в окне DDE сервера будут отображены наши переменные и их значения (рис.1).
Рис.1. Данные из COM порта, Excel и DDE. Окно DDE сервера.
Все переменные теперь готовы для использования в Microsoft Excel. Имя переменной отображается в колонке «Имя». Вы должны знать имя DDE сервера и имя топика для того, чтобы получать данные через DDE. Эта информация представлена на следующем рисунке (рис.2 поз.1). Пожалуйста, обратите внимание, что имя сервера может отличаться на вашем компьютере.
Рис.2. Данные из COM порта, Excel и DDE. Параметры сервера DDE.
Кликните на кнопке «OK» и закройте окно настройки модуля сервера DDE, а затем кликните кнопку «OK» в окне настройки конфигурации.
Поскольку мы хотим рисовать графики в реальном времени, то мы должны подготовить файл Excel перед следующим шагом. В этом примере мы создали диаграмму с тремя графиками. Каждый график базируется на данных их колонок A, B или C. Мы будет помещать значения переменных FLOW1, VOLW1 и TEMP1 в соответствующую колонку, а Excel будет автоматически обновлять нашу диаграмму. Мы будем использовать только последние 30 значений на нашей диаграмме. В этом примере мы поместили 3 ссылки на DDE переменные в ячейки D21-F21 вида: «=testcore|ddesrv!srviFLOW1» (без кавычек) (рис.3 и 4). Как вы можете видеть, имя сервера, имя топика и имя переменной указаны в этой ссылке на данные DDE.
Рис.3. Данные из COM порта, Excel и DDE. Ссылки на данные DDE.
После того, как вы создали ссылки на данные DDE в вашем документе, Microsoft Excel автоматически запустит логгер и получит данные. Если логгер уже запущен, то Microsoft Excel попробует считать данные из запущенной программы. Если логгер еще не получил и не обработал никаких данных, то Microsoft Excel отобразит информацию о неверной ссылке, но считает значения, как только данные будут получены и обработаны логгером.
Теперь мы готовы для создания кода VBA, который будет обрабатывать наши DDE данные. Этот код будет копировать значения DDE в колонки, передвигать старые значения и добавлять строки в историю значений (рис.4, 5).
Рис.4. Данные из COM порта, Excel и DDE. Исходный документ Excel.
Рисунок выше содержит следующие элементы:
Код VBA из файла Excel
Файл Excel, который мы создали, вы можете скачать здесь и использовать в вашей работе.
Хорошо. Процедура настройки завершена и пришло время попробовать получить данные в Excel.
Подсоедините ваше устройство или включите его, если это необходимо. Попробуйте принять пакет данных от устройства. Если парсер верно настроен, то в Excel будут отображены наши DDE переменные и их значения (рис.5).
Рис.5 Данные из COM порта, Excel и DDE. Результаты.
Рисунок выше содержит следующее:
Файл Excel со всеми значениями и графиками вы можете скачать здесь.
Этот метод получения данных сложен и может быть непонятен для неподготовленного пользователя, но позволяет вам обрабатывать и отображать данные как вам хочется. Позднее, вы можете сделать следующие улучшения:
Источник