Создание сервера RealTimeData для Excel

Аннотация

Microsoft Excel предоставляет новую функцию листа RTD, которая позволяет вызывать сервер автоматизации модели COM для получения данных в режиме реального времени. В этой статье описывается, как с помощью Visual Basic создать сервер RealTimeData для использования с функцией Excel RTD.

Дополнительная информация

Функция листа RTD имеет следующий синтаксис:

=RTD(ProgID,Server,String1,[String2],...)

Первый аргумент, ProgID, представляет программный идентификатор (ProgID) сервера RealTimeData. Аргумент Server указывает имя компьютера, на котором выполняется сервер RealTimeData; Этот аргумент может быть пустой строкой или опущен, если сервер RealTimeData должен выполняться локально. Остальные аргументы просто представляют параметры для отправки на сервер RealTimeData; Каждое уникальное сочетание этих параметров представляет один "раздел", который имеет связанный "идентификатор раздела". Параметры чувствительны к регистру. Например, ниже показаны вызовы сервера RTD, которые могут привести к трем отдельным идентификаторам разделов:

=RTD("ExcelRTD.RTDFunctions",,"AAA", "10")

=RTD("ExcelRTD.RTDFunctions",,"AAA", "5")

=RTD("ExcelRTD.RTDFunctions",,"aaa", "5")

Чтобы com-сервер автоматизации был сервером RealTimeData для использования с функцией RTD Excel, он должен реализовать интерфейс IRTDServer. Сервер должен реализовать все методы IRTDServer:

  • ServerStart: вызывается, когда Excel запрашивает первый раздел RTD для сервера. ServerStart должен возвращать значение 1 при успешном выполнении и отрицательное значение или 0 при сбое. Первый параметр метода ServerStart — это объект обратного вызова, который сервер RealTimeData использует для уведомления Excel о том, когда он должен собирать обновления с сервера RealTimeData.

  • ServerTerminate: вызывается, когда Excel больше не требует RTD-разделов с сервера RealTimeData.

  • ConnectData: вызывается каждый раз, когда Excel запрашивает новый раздел RTD с сервера RealTimeData.

  • DisconnectData: вызывается, когда Excel больше не требует определенного раздела.

  • HeartBeat: вызывается Excel, если заданный интервал истек с момента последнего уведомления Excel об обновлениях с сервера RealTimeData.

  • RefreshData: вызывается, когда Excel запрашивает обновление по темам. RefreshData вызывается после того, как сервер уведомляет Excel о том, что обновления существуют, и возвращает количество разделов для обновления вместе с идентификатором и значением раздела для каждого раздела.

Создание примера сервера RealTimeData

В следующем примере показано, как создать и использовать сервер RealTimeData с Microsoft Excel 2002. Этот сервер просто предоставляет счетчик, который обновляется каждые 10 секунд на листе. Сервер принимает до двух строк раздела. Первой строкой раздела может быть AAA, BBB и CCC; Любая другая строка раздела считается недопустимой, и сервер возвращает #VALUE! в функцию RTD. Вторая строка представляет собой числовое значение, представляющее способ увеличения возвращаемого значения. Если вторая строка опущена, значение приращения по умолчанию равно 1. Если вторая строка не является числом, сервер возвращает #NUM! в функцию RTD.

  1. Запустите новый проект библиотеки DLL ActiveX в Visual Basic.

  2. В меню "Проект " щелкните "Ссылки", выберите библиотеку объектов для своей версии Excel и нажмите кнопку "ОК ". Например, выберите один из следующих вариантов:

    • Для Microsoft Office Excel 2007 выберите библиотеку объектов Microsoft Excel 12.0.
    • Для Microsoft Office Excel 2003 выберите библиотеку объектов Microsoft Excel 11.0.
    • Для Microsoft Excel 2002 выберите библиотеку объектов Microsoft Excel 10.0.
  3. В меню "Проект" щелкните "Свойства Project1". Измените имя проекта на ExcelRTD и нажмите кнопку "ОК".

  4. Измените свойство Name модуля класса Class1 на RTDFunctions. Добавьте следующий код в RTDFunctions:

    Option Explicit
    
    Implements IRtdServer  'Interface allows Excel to contact this RealTimeData server
    
    Private m_colTopics As Collection
    
    Private Function IRtdServer_ConnectData(ByVal TopicID As Long, Strings() As Variant, GetNewValues As Boolean) As Variant
        '** ConnectData is called whenever a new RTD topic is requested
    
    'Create a new topic class with the given TopicId and string and add it to the
        'm_colTopics collection
        Dim oTopic As New Topic
        m_colTopics.Add oTopic, CStr(TopicID)
        oTopic.TopicID = TopicID
        oTopic.TopicString = Strings(0)
        If UBound(Strings) >= 1 Then oTopic.SetIncrement Strings(1)
    
    'For this example, the initial value for a new topic is always 0
        IRtdServer_ConnectData = oTopic.TopicValue
    
    Debug.Print "ConnectData", TopicID
    End Function
    
    Private Sub IRtdServer_DisconnectData(ByVal TopicID As Long)
       '** DisconnectData is called whenever a specific topic is not longer needed
    
    'Remove the topic from the collection
       m_colTopics.Remove CStr(TopicID)
    
    Debug.Print "DisconnectData", TopicID
    End Sub
    
    Private Function IRtdServer_Heartbeat() As Long
        '** Called by Excel if the heartbeat interval has elapsed since the last time
        '   Excel was called with UpdateNotify.
        Debug.Print "HeartBeat"
    End Function
    
    Private Function IRtdServer_RefreshData(TopicCount As Long) As Variant()
        '** Called when Excel is requesting a refresh on topics. RefreshData will be called
        '   after an UpdateNotify has been issued by the server. This event should:
        '   - supply a value for TopicCount (number of topics to update)
        '   - return a two dimensional variant array containing the topic ids and the
        '     new values of each.
    
    Dim oTopic As Topic, n As Integer
        ReDim aUpdates(0 To 1, 0 To m_colTopics.Count - 1) As Variant
        For Each oTopic In m_colTopics
            oTopic.Update
            aUpdates(0, n) = oTopic.TopicID
            aUpdates(1, n) = oTopic.TopicValue
            n = n + 1
        Next
        TopicCount = m_colTopics.Count
        IRtdServer_RefreshData = aUpdates
    
    Debug.Print "RefreshData", TopicCount & " topics updated"
    End Function
    
    Private Function IRtdServer_ServerStart(ByVal CallbackObject As Excel.IRTDUpdateEvent) As Long
        '** ServerStart is called when the first RTD topic is requested
    
    Set oCallBack = CallbackObject
        Set m_colTopics = New Collection
        g_TimerID = SetTimer(0, 0, TIMER_INTERVAL, AddressOf TimerCallback)
        If g_TimerID > 0 Then IRtdServer_ServerStart = 1       'Any value <1 indicates failure.
    
    Debug.Print "ServerStart"
    End Function
    
    Private Sub IRtdServer_ServerTerminate()
        '** ServerTerminate is called when no more topics are needed by Excel.
    
    KillTimer 0, g_TimerID
    
    '** Cleanup any remaining topics. This is done here since 
        '   IRtdServer_DisconnectData is only called if a topic is disconnected 
        '   while the book is open. Items left in the collection when we terminate
        '   are those topics left running when the workbook was closed.
    
    Dim oTopic As Topic
        For Each oTopic In m_colTopics
            m_colTopics.Remove CStr(oTopic.TopicID)
            Set oTopic = Nothing
        Next
    
    Debug.Print "ServerTerminate"
    
    End Sub
    
    
  5. В меню "Проект" щелкните "Добавить модуль класса". Измените свойство name модуля класса на Topic и измените свойство Instancing на Private. Добавьте следующий код в модуль класса Topic:

    Option Explicit
    
    Private m_TopicID As Long
    Private m_TopicString As String
    Private m_Value As Variant
    Private m_IncrementVal As Long
    
    Private Sub Class_Initialize()
        m_Value = 0
        m_IncrementVal = 1
    End Sub
    
    Friend Property Let TopicID(ID As Long)
        m_TopicID = ID
    End Property
    
    Friend Property Get TopicID() As Long
        TopicID = m_TopicID
    End Property
    
    Friend Property Let TopicString(s As String)
        s = UCase(s)
        If s = "AAA" Or s = "BBB" Or s = "CCC" Then
            m_TopicString = s
        Else
            m_Value = CVErr(xlErrValue) 'Return #VALUE if not one of the listed topics
        End If
    End Property
    
    Friend Sub Update()
        On Error Resume Next 'the next operation will fail if m_Value is an error (like #NUM or #VALUE)
        m_Value = m_Value + m_IncrementVal
    End Sub
    
    Friend Sub SetIncrement(v As Variant)
        On Error Resume Next
        m_IncrementVal = CLng(v)
        If Err <> 0 Then
            m_Value = CVErr(xlErrNum) 'Return #NUM if Increment value is not numeric
        End If
    End Sub
    
    Friend Property Get TopicValue() As Variant
        If Not (IsError(m_Value)) Then
            TopicValue = m_TopicString & ": " & m_Value
        Else
            TopicValue = m_Value
        End If
    End Property
    
  6. В меню "Проект" выберите "Добавить модуль". Добавьте следующий код в новый модуль:

    Public Declare Function SetTimer Lib "user32" (ByVal hWnd As Long, _
    ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long
    
    Public Declare Function KillTimer Lib "user32" (ByVal hWnd As Long, ByVal nIDEvent As Long) As Long
    
    Public Const TIMER_INTERVAL = 5000
    Public oCallBack As Excel.IRTDUpdateEvent
    Public g_TimerID As Long
    
    Public Sub TimerCallback(ByVal hWnd As Long, ByVal uMsg As Long, ByVal idEvent As Long, ByVal dwTime As Long)
        oCallBack.UpdateNotify
    End Sub
    
  7. В меню "Файл" нажмите кнопку "ExcelRTD.dll" для сборки компонента.

Использование сервера RTD в Excel

  1. Запустите новую книгу в Microsoft Excel.

  2. В ячейке A1 введите следующую формулу и нажмите клавишу ВВОД:

    =RTD("ExcelRTD.RTDFunctions","AAA", 5)

    Начальное возвращаемое значение — AAA: 0. Через пять секунд значение обновляется до "AAA: 10", а через 10 секунд — до AAA:15 и т. д.

  3. В ячейке A2 введите следующую формулу и нажмите клавишу ВВОД:

    =RTD("ExcelRTD.RTDFunctions","BBB", 3)

    Начальное возвращаемое значение — BBB: 0. Каждые пять секунд значение ячейки увеличивается на 3.

  4. В ячейке A3 введите следующую формулу и нажмите клавишу ВВОД:

    =RTD("ExcelRTD.RTDFunctions","AAA", 5)

    Начальное возвращаемое значение соответствует содержимому ячейки A1, так как это тот же "раздел", который используется в A1.

  5. В ячейке A4 введите следующую формулу и нажмите клавишу ВВОД: =RTD("ExcelRTD.RTDFunctions","AAA", 10)

    Начальное возвращаемое значение — "AAA: 0". Каждые пять секунд значение ячейки увеличивается, как и другие ячейки. Обратите внимание, что возвращаемое значение не соответствует содержимому ячейки A1 или A3, так как сочетание параметров, передаваемых на сервер, отличается.

На этом рисунке RTD-сервер был скомпилирован, а Excel использовал версию компонента во время выполнения. В целях отладки RTD-серверы можно запускать из интегрированной среды разработки Visual Basic.

Для запуска в режиме отладки:

  1. Закройте Microsoft Excel и перейдите к проекту в Visual Basic.
  2. Нажмите клавишу F5, чтобы запустить компонент. Если появится диалоговое окно "Свойства проекта", нажмите кнопку "ОК", чтобы выбрать параметр ожидания создания компонентов по умолчанию.
  3. Убедитесь, что в Visual Basic отображается окно интерпретации. При вводе формул в ячейки и при обновлении ячеек изучите содержимое окна интерпретации в Visual Basic, чтобы узнать, какие действия вызывают различные события.

Примечание.

Сведения о событии DisconnectData

Хотя Excel является подписчиком RTD-сервера, он активирует событие DisconnectData, когда ему больше не требуется раздел (например, при удалении или очистке формулы RTD в ячейке). Однако Excel не вызывает DisconnectData в каждом разделе для сервера RTD, когда книга закрыта или Excel завершает работу. Вместо этого Excel вызывает только ServerTerminate. При создании сервера RTD необходимо выполнить код для любой необходимой очистки разделов или других объектов при срабатывании события ServerTerminate.

(c) Microsoft Corporation 2001, все права зарезервированы. Участие: Лори Б. Тертер (Lori B. Turner), корпорация Майкрософт.