Как ошибка тайм-аута запроса улова VBA?

Я использую object msxml2 описывается.ServerXMLHTTP60 отправить запрос в webservice; с помощью этого объекта я могу ускорить загрузку данных асинхронные метод и избежать блокировки экрана Excel (не отвечает). Но у меня все еще есть проблема, когда ответ webservice в течение длительного времени, из настройки тайм-аута ServerXMLHTTP60, функция запроса была молчаливой, я не могу поймать ошибку тайм-аута. At еще вопрос, @osknows предлагает использовать xmlhttp status = 408 to ловить ошибки тайм-аута, но это не работает для меня.

Я подготовил тестовый файл, вы можете скачать здесь. Откройте источник VBA, нажав Atl + F8, вы увидите модуль класс CXMLHTTPHandler, что я скопировал из данное руководство

    If m_xmlHttp.readyState = 4 Then
        If m_xmlHttp.Status = 200 Then
            MsgBox m_xmlHttp.responseText
        ElseIf m_xmlHttp.Status = 408 Then 'Debug never run to here?
            MsgBox "Request timeout"
        Else
         'Error happened
        End If
    End If

как ошибка тайм-аута запроса улова VBA?

Спасибо за вашу помощь!

1 ответов


здесь есть несколько осложнений.

  1. MSXML2.ServerXMLHTTP не раскрывает COM-полезные события. Поэтому создать экземпляр объекта с помощью WithEvents и прикрепить к его OnReadyStateChange событие.
    Событие есть, но стандартный способ обработки VBA не работает.
  2. модуль, который может обрабатывать событие, не может быть создан с помощью IDE VBA.
  3. нужно позвонить waitForResponse() при использовании асинхронных запросы (дополнительно к вызову setTimeouts()!)
  4. нет timeout событие. Тайм-ауты выдаются как ошибка.

чтобы решить проблему #1:

обычно модуль класса VBA (также применяется к пользовательским формам или модулям листа) позволяет вам сделать это:

Private WithEvents m_xhr As MSXML2.ServerXMLHTTP

таким образом, вы можете определить обработчик событий следующим образом:

Private Sub m_xhr_OnReadyStateChange()
  ' ...
End Sub

не так MSXML2.ServerXMLHTTP. Это приведет к компиляции Microsoft Visual Basic Ошибка: "объект не является источником событий автоматизации".

видимо событие не экспортируется для COM использовать. Есть способ обойти это.

подпись для onreadystatechange читает

Property onreadystatechange As Object

таким образом, вы можете назначить объект. Мы могли бы создать модуль класса с onreadystatechange метод и назначить следующим образом:

m_xhr.onreadystatechange = eventHandlingObject

однако это не работает. onreadystatechange ожидает объект и всякий раз, когда событие срабатывает, объект is называется, а не метод, который мы определили. (Для ServerXMLHTTP экземпляр нет способа узнать, какой метод определяемого пользователем eventHandlingObject мы намерены использовать в качестве обработчика событий).

нам нужна callable объект, т. е. объект с метод по умолчанию (каждый COM-объект может иметь только одно).
(например: Collection объекты вызываются, вы можете сказать myCollection("foo"), который является сокращением для myCollection.Item("foo").)

решить проблему #2:

нужен модуль класса со свойством по умолчанию. К сожалению, они не могут быть созданы с помощью VBA IDE, но вы можете создать их с помощью текстового редактора.

  • подготовьте модуль класса, содержащий onreadystatechange функция в VBA IDE
  • экспортировать его в .cls файл с помощью правой кнопки мыши
  • открыть в текстовом редакторе и добавьте следующую строку под onreadystatechange подпись:
    Attribute OnReadyStateChange.VB_UserMemId = 0
  • удалите исходный модуль класса и повторно импортируйте его из файла.

это будет модифицированный метод как Default. Вы можете увидеть маленькую синюю точку в браузере объектов (F2), которая отмечает метод по умолчанию:

Default Method

поэтому каждый раз, когда объект называется, на самом деле the OnReadyStateChange метод называется.

для решения проблемы #3:

просто позвоните waitForResponse() после send().

m_xhr.Send
m_xhr.waitForResponse timeout

в случае таймаута: если вы не вызывали этот метод, запрос просто никогда не возвращается. Если вы это сделали, после timeout миллисекундах.

для решения проблемы #4:

нужно использовать On Error обработчик, который ловит ошибку тайм-аута и преобразует ее в событие для удобства.

все вместе

вот модуль класса VB I написал, что обертывает и обрабатывает в качестве обработчика событий, что стало возможным благодаря маркировке OnReadyStateChange() как метод по умолчанию.

имейте в виду, что если вы внесете изменения в OnReadyStateChange() вам нужно пройти процедуру экспорта/изменения/повторного импорта еще раз, так как VBA IDE не сохраняет " по умолчанию атрибут method.

класс предоставляет следующее интерфейс

  • методы:
    • HttpGet(url As String, [timeout As Long])
    • HttpPost(url As String, data As String, [timeout As Long])
    • Cancel()
  • свойства
    • IsRunning As Boolean
  • событий
    • Started()
    • Stopped()
    • Success(data As String, serverStatus As String)
    • Error(data As String, serverStatus As String, xhr As MSXML2.ServerXMLHTTP)
    • TimedOut(message As String)

используйте его в другом модуле класса, например в пользовательская форма, с WithEvents:

Option Explicit

Private WithEvents ajax As AjaxRequest

Private Sub UserForm_Initialize()
  Set ajax = New AjaxRequest
End Sub

Private Sub CommandButton1_Click()
  Me.TextBox2.Value = ""

  If ajax.IsRunning Then
    ajax.Cancel
  Else
    ajax.HttpGet Me.TextBox1.Value, 1000
  End If
End Sub

Private Sub ajax_Started()
  Me.Label1.Caption = "Running" & Chr(133)
  Me.CommandButton1.Caption = "Cancel"
End Sub

Private Sub ajax_Stopped()
  Me.Label1.Caption = "Done."
  Me.CommandButton1.Caption = "Send Request"
End Sub

Private Sub ajax_TimedOut(message As String)
  Me.Label1.Caption = message
End Sub

Private Sub ajax_Success(data As String, serverStatus As String)
  Me.TextBox2.Value = serverStatus & vbNewLine & data
End Sub

Private Sub ajax_Error(data As String, serverStatus As String, xhr As MSXML2.ServerXMLHTTP)
  Me.TextBox2.Value = serverStatus
End Sub

сделать улучшения, как вы считаете нужным. The AjaxRequest класс был просто побочным продуктом ответа на этот вопрос.