Страницы: 1
RSS
Событие для ячееек с DDE-запросом
 
Здравствуйте.  
 
На листе три ячейки (среди прочих :) В каждой своя формула с DDE- запросом.  
Изменение данных по одному из них вызывает событие для листа WorkSheet.Calculate, но не .Change.    
 
Я ужЕ всю бОшку сломал - как определить, какая из ячеек вызывает Calculate?  
 
Единственный криво-коленно-локтевой способ придумал - разместить эти запросы на отдельных листах... Но у меня их не один десяток :(  
 
Значение не обязательно меняется - может поступать и то же самое. Т.е. сравнение не канает.  
.  
.
 
a1 и a2  - изменяемые ячейки  
в b1 и b2 - пользовательская функция, изменяющая глобальную переменную  
 
по событию calculate переменная обнуляется(но до этого можно выполнить какой-либо код)
Живи и дай жить..
 
функцию можно использовать и без параметра, используя для выяснения адреса выражение application.caller.address, если добавить ее(+fun1()) в ячейку, зависящую от DDE
Живи и дай жить..
 
Благодарствуем. Неплохая идея. Щаз попробуем.    
 
Я только не совсем понял насчет [q]...добавить ее(+fun1()) в ячейку, зависящую от DDE...[/q] Мы же таким образом получим конкатенацию результирующих строк? DDE выдает или Single, или Double. Не знаю, не специалист :) Может он дает string, а потом в Excel-е идет приведение типов...
 
Или будет суммирование? Если это арифметическая операция, то на быстродействие это сильно не повлияет, в отличие от конкатенации строк. Так?  
 
.
 
Все работает. Еще раз благодарю.  
Единственное - добавил проверку обнуления.  
 
If calc = "" Then calc = rng.Address
 
вызов функции происходит во время пересчета, значит сразу после этого выдается сообщение calculate и вызывается программа обработки, в которой, в свою очередь, обнуляется calc..  
 
 
да, если вы прервете обработку, то calc может и не обнулиться, но это не правило, а исключение(обычно при остановке отладки переменные все равно реинициализируются). Но в этом случае , если вы поставите предварительную проверку calc="" , то первый раз цепочка сработает по старому значению..  
 
не знаю, надо ли это вам?  
 
а насчет второго варианта - имени функции не присваивается значения, поэтому оно остается заданным по умолчанию при инициализации, т.е. пустым..
Живи и дай жить..
 
Немного не так. Я отталкивался от следующей предполагаемой  последовательности:  
1 Открытие книги.  
2 Инициализация DDE.  
3 Изменение одной из ячеек с DDE.  
4 Событие Calculate  
5 Запуск ф-ции (calc == "")  
6 Передача calc в обработчик  
7 Завершение обработки с обнулением calc  
8 Ожидание следующих данных  
 
Между событиями 3 и 8 могут возникнуть данные и в других DDE. В этом случае возникает такой винегрет... Все данные перепутываются. Чтобы исключить это я смирился с частичной потерей данных, через байпас при непустой calc. Обнуление calc является флагом готовности обработчика к следующим данным.  
 
Именно поэтому я и хочу уйти от обработки события листа и вообще от листа и загнать всю обработку в код. Привязаться именно к событию возникновения данных в собсвенно DDE (SetLinkOnData), исключив все промежуточные вычисления и события на листе, оставив только фиксацию данных в итоговую накопительную таблицу. Но как это сделать - не знаю... Экспериментировал с имеющимся в VBA инструментарием по DDE, но ни фига не вышло...  
.
 
Итак решение найдено.    
 
Есть такой изящный метод SetLinkOnData. Он устанавливает процедуру, выполняемую при поступлении данных по указанной DDE-связи. Примерно так:  
 
Sub ff()  
   ActiveWorkbook.SetLinkOnData "aaa|bbb!ccc?ddd", "gg"  
End Sub  
 
Sub gg()  
   Debug.Print "It's work!"  
End Sub  
 
Преимущества этого решения перед обсуждавшимися выше очевидны.  
 
Кроме этого, естественным образом организовалось еще одно огромное удобство - ff() можно вешать на кнопку (или куда-нибудь еще), включая и выключая обработку событий для этой связи. Это чрезвычайно полезно при отладке и работе с книгой.  
 
2 Slan: еще раз благодарю за первоначальную идею. Она свою роль выполнила.    
 
Также благодарю, что ткнул меня носом в хэлп в теме http://www.planetaexcel.ru/forum.php?thread_id=10827 - я, вобщем-то, этот метод со зла нашел (искал-то я в конексте той темы совершенно другое :) На будущее - тыкай носом кого-то куда-то только тогда, если сам это что-то знаешь "от зубов".    
 
Почему финиш промежуточный? Я все-таки надеюсь найти решение, отвязанное от событий на листе, как это сформулировано в http://www.planetaexcel.ru/forum.php?thread_id=10827  
 
Может есть у кого-то практические соображения? Slan, you are welcome too :)  
.
 
Итак, все работает. Открылся маленький нюанс - в описанной схеме при попытке получить значение ячейки, по событию обновления DDE-связи в ней, получаем старые данные - лист еще не обновился. Необходимо его принудительно Calculate. Соответственно нужно и скорректировать формульную нагрузку на лист, т.к. количество пересчетов существенно увеличивается.  
 
Таким образом, подтверждается необходимость и актуальность получения данных непосредственно из DDE-связи, а не с листа.  
 
Slan, есть соображения? :))  
.
 
Chen,    
Ваших советов Слэн не просил, он в них и не нуждается. Наоборот, это Вы просите советов, поэтому будьте готовы к любым :-) Давайте из этого исходить, а раздражение оставлять по свою сторону монитора. Если не согласны, то дальше обсуждать бесполезно.  
 
Итак, хотелось бы уточнить, что у Вас не получается.  
Обычно здесь задавались вопросы по накоплению данных, получаемых по каналу DDE, при этом в качестве сервера и источника данных выступали некие программы, в которые никто не хотел или не знал как влезть. У Вас, похоже, иная ситуация - сервером является Excel, и поэтому управление настройками DDE возможно, что может облегчить решение проблемы.  
 
Возможно, информация уже приводилась, но лучше уточнить, поэтому вопросы:  
1. Из какого приложения поступают данные в Excel?    
2. Кто сервер, кто клиент?    
3. Если сервер Excel, то в ячейках обновления формула подключения или используется VBA- код?  
4. Какое количество этих данных, или другими словами, сколько ячеек Excel принимают данные DDE?  
5. Ячейки-приемники  DDE данных могут быть расположены на разных листах?  
6. С каким периодом происходит обновление данных и какова примерно пауза после прихода пакета данных?  
7. Какую часть паузы (п.6) примерно занимает обработка данных?  
8. Допустимо ли запускать обработку не по факту обновления данных, а периодически, например, раз в минуту (уточнить период)?  
9. Что представляют из себя и куда пишутся результаты обработки макроса?  
 
Насколько я понял, проблема заключается в том, что по DDE обновляется несколько ячеек, а пока выполняется обработка части из них, DDE обновляет другие и не на каждое обновление Excel формирует свои события. Эта проблема известна: по-сути, работа с DDE противоречит объектной модели Excel, поэтому использование устаревшего DDE и не рекомендуется, а взамен обычно предлагается работать с ActiveX (OLE) - объектами. Но будем разбираться с тем, что есть.  
 
Варианты решения проблемы:  
 
А) Если обновление данных производится пакетно (обновление всех ячеек + пауза), то запуск макроса обработки можно производить по событию обновления не первой, а последней ячейки. Или дождаться паузы и обработать. Понятно, что время обработки должно быть меньше времени упомянутой выше паузы.  
 
Б) Периодическая обработка (см. п.8)  при которой данные обрабатываются с заданным периодом, а не по событию их обновления. Обычно это устраивает, хоть и не все изменения фиксируются. После обработки в ячейках все равно всегда будут последние данные, несмотря на то, что изменения во время обработки (время работы макроса) будут пропущены.  
 
В) Сервер пишет данные не в ячейки Excel, а в файл или в другое приложение (в Word или в отдельно отрытое приложение Excel), и каким-либо способом эти данные считываются (с удалением) в наше приложение Excel для обработки. Другими словами, создается FIFO-буфер, заполняемый по DDE и очищаемый макросом. При этом ни одно изменения не пропадет.  
 
И приложили бы пример с тем, что есть (данные + код) и что нужно получить (данные), тогда и мои романы будут покороче :-)
 
ZVI, благодарю за пристальное внимание. Через пару-другую отпишусь поподробнее (я, вобщем-то, часть испрашиваемого уже излагал, надо собрать все в кучу :)
 
Уточняю расклад:  
 
Данные поступают из внешнего приложения OEC. У Вас его заведомо быть не может, поэтому приводить пример смысла нет. Описываю на пальцах:  
 
Сервером, получается, выступает данное приложение. Клиентом - Excel  
 
1. Есть три независимых канала, по каждому из которых поступают данные независимо и асинхронно друг от друга в произвольные моменты времени с произвольной частотой.    
 
Частота может доходить до нескольких десятков в секунду. Это не значит, что с такой частотой они шарашат постоянно, просто иногда бывает, что за, скажем, 0.1 сек поступает несколько десятков. Это в пике. Нормально - нескоько штук в сек. Может и замирать все до пары-другой шт в минуту. Раз в сутки - перерыв на час. Паузы возникают в таком же режиме :)  
 
По п.3 - да, в трех ячейках - формулы подключения к каждому из каналов.  Через VBA-код определяется источник и на другой лист в три столбца, каждый из которых соответствует своему каналу, пишутся данные простым дописыванием в конец столбца. Для всех трех каналов используется общая переменная для номера строки в которую писать новые данные. Т.о. одна строка используется только для одного значения, независимо, от какого канала поступили данные.    
 
По п.5 - Ячейки-приемники DDE данных конечно, можно расположить на разных листах, но это неудобно, да и зачем? Я об этом думал, когда не мог дргуим спосоюом выделить источник.  
 
По п.8 - нужен реал-тайм.  
 
К сожалению, в данном варианте сервером предлагается DDE. Есть еще API, но использование API - блокирует упомянутое приложение. Допускается только один коннект к серверу в интернете. А мне само приложение тоже на экране нужно - я не могу все его функции реализовать в API, да еще морду ему написать работоспособную :)  
 
Что касается FIFO, в данном случае главная цель не статистическая (хотя про это я тоже не забываю и имею ввиду). Разумной частью особо быстро поступающих данных можно смело пожертвовать в 90 случаях из 100. Цель - графическое представление в виде диаграммы с тремя линиями, соответствующими своему каналу за внятный промежуток времни. Скажем час. Может два. Задачей естественного их представления по оси времени пока не заморачивался. Достаточно поштучного учета. Пока достаточно :)  
 
Файл сейчас почищу от рабочей грязи и выложу через пяток минут.  
 
Я эту тему затрагивал собственно в этой ветке и в:  
http://www.planetaexcel.ru/forum.php?thread_id=10827 и в  
http://www.planetaexcel.ru/forum.php?thread_id=10823
 
Собственно файл.
 
Пришлось соорудить отладочный DDE-сервер (DDEServer.exe).  
Сервер из VBA-кода запускается автоматически, читайте комментарии в коде.  
Пример решения приложен. Тестируйте на реальных данных.
 
Подправил отладочный DDE-сервер и VBA-код Excel клиента
 
Благодарю. Надо время. Чуть позже отпишусь.
 
ZVI, я, честно говоря, потрясен. Это же столько времени и сил надо было посвятить моей проблеме! Одни только даты файлов многое говорят :) Я приятно тронут внимательностью и тщательностью, с которыми ты отнесся к этому делу. Приятно видеть столь заботливо написанный код. Выражаю свою благодарность как могу.    
 
Я проверил на реальном подключении - все работает на ура. Визуально пропусков не видно. С оригинальными данными я могу сравнить только третью колнку, что обязательно сделаю позже.  
 
Разумеется, по ходу дела возникли вопросы по реализации, поэтому, пользуясь случаем, я их задам.    
 
1. Я совершенно не понял, как реализованы кнопки Start & Stop, запускающие соответствующие процедуры :) Нет стандартных событий, недоступны свойства... Что это за кнопочки?    
 
2. Все три процедуры Save1 2 3 вызывают единственную процедуру Update, передавая в нее свой номер. Я по простоте душевной считал, что если каждая Save_i будет обращаться к собственной Update_i, они не будут пересекаться при исполнении. Может я заблуждаюсь? Я не настоящий сварщик :)    
 
3. Касательно собственно Update. Если ее переменные определить на глобальном уровне это даст некоторую экономию по времени?    
.
 
Комментарии по пунктам сообщения выше:  
 
1. Кнопки – это 2 автофигуры, оформленные заливкой, прозрачностью и т.п.  
Макрос подключается так: Правый клик на РАМКЕ автофигуры – "Назначить макрос".    
Автофигуры для удобства сгруппированы в одну: из меню "Вид" – "Панели инструментов" - включить панель инструментов "Рисование". После этого в нижней части экрана добавляется панель, с помощью стрелки "Выбор объектов" с этой панели выделить обе автофигуры, выбрать слева внизу "Действия" – "Сгруппировать".  
 
2.  В VBA одновременно может  выполняться только один макрос. С помощью DoEvents макрос может приостанавливаться на некоторое малое время и отдавать эту часть своего времени другим процессам. Но ни многозадачность ни многопроцессорность в VBA не поддержаны, в т.ч. и в Excel 2007. Поэтому смысла в дублировании кода в разных процедурах для каждой DDE-ячейки нет.  
 
3. Использование глобальных переменных теоретически быстрее, так как на создание локальных требуется некоторое время поиска, выравнивания адресов, резервирования памяти и формирования структуры переменной. Но практически выигрыш, как правило, незначительный.    
Обычно больше тормозят строковые функции, например, Replace() и обращения из VBA к Excel (ячейки, диапазоны, объекты и методы Application) и наоборот. В коде есть некоторая избыточность, в частности исправление неправильного десятичного разделителя: If Err <> 0 Then v = Val(Replace(Str(.Value), ",", ".")) Но этот фрагмент запускается только, если десятичные разделители Клиента и Сервера не совпадают, а при правильной настройке Клиента и Сервера этот тормоз не сработает.  
Дооптизировать по скорости код можно (как и любой другой, наверное :), если в этом есть необходимость. Но в таких случаях я обычно действую по правилу: перед тем как решать проблему сначала нужно убедиться в том, что это действительно проблема :-)
 
Спасибо за более чем толковое объяснение.  
 
В контексте п.2 - получается, если мне подобных наборов надо несколько, по нескольким инструментам (конкретно - 5 шт), то они все равно, независимо от того, в одной ли книге они будут или в разных, все равно будут обрабатываться синхронно (в силу того, что под одним Excel-ем)? А если два Excel-я запустить? Или три? Да блин, все пять! На Core 2 Duo E6750 2.6GHz 4G, Server 2003  
 
А это ограничение и для Access справедливо? Не в курсе?  
.
 
{quote}{login=Chen}{date=28.10.2009 06:12}{thema=}{post}Спасибо за более чем толковое объяснение.  
 
В контексте п.2 - получается, если мне подобных наборов надо несколько, по нескольким инструментам (конкретно - 5 шт), то они все равно, независимо от того, в одной ли книге они будут или в разных, все равно будут обрабатываться синхронно (в силу того, что под одним Excel-ем)? А если два Excel-я запустить? Или три? Да блин, все пять! На Core 2 Duo E6750 2.6GHz 4G, Server 2003  
 
А это ограничение и для Access справедливо? Не в курсе?  
.{/post}{/quote}  
Если запустить несколько приложений Excel, но не несколько окон (книг) одного приложения Excel, то обработка будет идти независимо, то есть быстрее за счет параллельных процессах, в том смысле, как эта параллельность организована в операционной системе.
 
P.S. Да, все описанное выше справедливо и для Access
 
Да, я имел ввиду именно приложение... Слава Богу, Excel допускает запуск нескольких копий.    
 
Что ж. Теперь у меня материала для работы более чем достаточно. Еще раз благодарю за потраченное на меня время и труд.    
 
И все-таки, остался открытым вопрос: как же этот DDE-запрос в виде формулы в ячейке засунуть в форму, а еще лучше, в чистый код?
 
Формы могут быть приемником DDE-данных, но не в VBA (Excel), а в VB (Visual Studio 6). Пример реализации формы-клиента с тем же отладочным сервером приложен. Сначала нужно запустить приложение сервера, а затем – клиента. Любые изменения в текстбоксах сервера автоматически отображаются в соответствующих текстбоксах формы клиента.
 
А можно с исходниками?
 
Все коды VBA здесь приведены. А для создания форм клиента и сервера на VB не нужно ни одной строчки исходного текста - достаточно настроить DDE свойства форм и контролов. Кто хоть немного знаком с VB знает это, а если не знаком, то можно и начать изучать. По VB есть свои хорошие форумы, например: http://www.sql.ru/forum/actualtopics.aspx?bid=22
 
Спасибо за примеры. Я уже успел посмотреть в сторону VB. Даже библиотеку vb6.olb прицепил к проекту. Но, возникла проблема: как создать такое поле? В списке доступных контролов элементы VB не появляются, хотя в Object Browser я это поле с его свойствами вижу. Попробовал (по аналогии со стандартным полем Excel):  
ActiveSheet.OLEObjects.Add ClassType:="VB.TextBox.1", Link:=False, DisplayAsIcon:=False, Left:=73.5, Top:=203.25, Width:=77.25, Height:= 25.5  
 
- отвечает "Run-time error '1004 ' Вставка объекта неосуществима"  
 
На этом все и встало... В смысле с использованием поля и его встроенных DDE-свойств по аналогии с VB.  
.
 
В VB можно использовать VBA-формы, а наоборот - нет.  
Опять же, какая проблема решается и чем она подтверждена?
 
Ну вот :( Кто бы знал...    
Дык... Все та же. В Excel все работает отлично (в данной реализации). Но так и остались:  
1. Низкое быстродействие и ресурсоемкость за счет того, что листы приходится ворочать.  
2. Известное ограничение на число записей.  
 
Да и не его это дело с БД работать. В Access из таких вот простейших форм будет все по-тихому в какой-нибудь Recordset писАться и делОв нема. В смысле - хорошо бы так получилось.  
 
А как хорошо в API этого сервера - есть класс, есть его события. Ваяй - не хочу. Почему для DDE такого нет?    
 
К сожалению приходится вместо сервера напрямую этот DDE с приложением использовать.    
.
 
Интереснейшая тема спасибо за наводку OEC видимо OpenECry))хороший продукт  
 
А ZVI просто Гуру
Страницы: 1
Наверх