Как ошибка тайм-аута запроса улова 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 ответов
здесь есть несколько осложнений.
-
MSXML2.ServerXMLHTTP
не раскрывает COM-полезные события. Поэтому создать экземпляр объекта с помощьюWithEvents
и прикрепить к егоOnReadyStateChange
событие.
Событие есть, но стандартный способ обработки VBA не работает. - модуль, который может обрабатывать событие, не может быть создан с помощью IDE VBA.
- нужно позвонить
waitForResponse()
при использовании асинхронных запросы (дополнительно к вызовуsetTimeouts()
!) - нет
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), которая отмечает метод по умолчанию:
поэтому каждый раз, когда объект называется, на самом деле 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
класс был просто побочным продуктом ответа на этот вопрос.