
HTTP-запросы из VB6 через MSXML2.XMLHTTP и ServerXMLHTTP: GET, POST, загрузка файлов
Компонент MSXML превращает VB6 в простой HTTP-клиент: можно скачать страницу, вызвать API или отправить форму. Разберём рабочие примеры и грабли, на которые наступает почти каждый.
Какой объект выбрать
В составе MSXML есть два похожих класса для HTTP-запросов, и разница между ними важнее, чем кажется. XMLHTTP построен на WinINet, той же библиотеке, что использовал Internet Explorer. Он берёт настройки прокси и безопасности из браузера, кэширует ответы и может показывать диалоги авторизации. Для настольной программы, которая работает от имени пользователя, это бывает удобно.
ServerXMLHTTP построен на WinHTTP. Он не зависит от настроек браузера, не показывает окон, не кэширует и позволяет задать тайм-ауты. Это правильный выбор для фоновых задач, служб и обращений к API.
Указывайте версию явно: MSXML2.ServerXMLHTTP.6.0 или MSXML2.XMLHTTP.6.0. MSXML 6 входит в Windows начиная с XP SP3, а безверсионный ProgID может привести к устаревшей и менее безопасной версии 3.0. Подключать ссылку на Microsoft XML не обязательно, с поздним связыванием код переносимее.
GET-запрос с проверкой статуса
Третий параметр метода Open задаёт режим: False означает синхронный запрос, при котором send не возвращает управление, пока не придёт ответ. Самая частая ошибка в простых примерах: отсутствие проверки статуса. Сервер может вернуть страницу 404 или 500, и программа примет текст ошибки за полезные данные.
Метод setTimeouts принимает четыре значения в миллисекундах: на разрешение имени, подключение, отправку и получение. Без него зависший сервер может заморозить программу на минуты.
Public Function HttpGet(ByVal strUrl As String) As String
Dim http As Object
Set http = CreateObject("MSXML2.ServerXMLHTTP.6.0")
http.setTimeouts 5000, 5000, 10000, 30000 ' resolve, connect, send, receive
http.Open "GET", strUrl, False ' False = синхронный запрос
http.setRequestHeader "User-Agent", "MyVB6App/1.0"
http.send
If http.Status <> 200 Then
Err.Raise vbObjectError + 513, "HttpGet", _
"HTTP " & http.Status & " " & http.statusText
End If
HttpGet = http.responseText
End Function
' Пример: MsgBox HttpGet("https://example.com/api/version")POST-запрос и кодирование параметров
Для отправки формы тело передаётся аргументом метода send, а заголовок Content-Type сообщает серверу формат. Значения полей обязательно кодируются: пробел, амперсанд или кириллица без кодирования испортят запрос. Встроенной функции UrlEncode в VB6 нет, поэтому пишем свою: получаем байты UTF-8 через ADODB.Stream и заменяем всё, кроме безопасных символов, на последовательности %XX.
Тело запроса затем собирается так: "name=" & UrlEncodeUtf8(txtName) & "&city=" & UrlEncodeUtf8(txtCity). Для JSON-API вместо этого передаётся строка JSON с заголовком application/json.
Public Function UrlEncodeUtf8(ByVal s As String) As String
Dim stm As Object, b() As Byte, i As Long, out As String
If Len(s) = 0 Then Exit Function
Set stm = CreateObject("ADODB.Stream")
stm.Type = 2: stm.Charset = "utf-8": stm.Open
stm.WriteText s
stm.Position = 0: stm.Type = 1 ' переключаемся на байты
stm.Position = 3 ' пропускаем BOM EF BB BF
b = stm.Read
stm.Close
For i = 0 To UBound(b)
Select Case b(i)
Case 48 To 57, 65 To 90, 97 To 122, 45, 46, 95, 126 ' 0-9 A-Z a-z - . _ ~
out = out & Chr$(b(i))
Case Else
out = out & "%" & Right$("0" & Hex$(b(i)), 2)
End Select
Next
UrlEncodeUtf8 = out
End Function
Public Function HttpPostForm(ByVal strUrl As String, ByVal strBody As String) As String
Dim http As Object
Set http = CreateObject("MSXML2.ServerXMLHTTP.6.0")
http.Open "POST", strUrl, False
http.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
http.send strBody
HttpPostForm = http.responseText
End FunctionКодировка ответа и скачивание файлов
Свойство responseText декодирует ответ по кодировке из заголовка Content-Type, а если её там нет, считает текст UTF-8. Старые сайты в windows-1251 часто не указывают charset, и вместо кириллицы получаются знаки вопроса. В таком случае берите сырые байты из responseBody и декодируйте их сами через ADODB.Stream с нужным Charset.
Тот же responseBody нужен для скачивания двоичных файлов: архивов, картинок, PDF. Его нельзя пропускать через строку, иначе байты исказятся. ADODB.Stream в двоичном режиме сохраняет их на диск как есть.
Public Sub DownloadFile(ByVal strUrl As String, ByVal strPath As String)
Dim http As Object, stm As Object
Set http = CreateObject("MSXML2.ServerXMLHTTP.6.0")
http.setTimeouts 5000, 5000, 10000, 120000 ' большой файл: долгий приём
http.Open "GET", strUrl, False
http.send
If http.Status <> 200 Then Err.Raise vbObjectError + 514, , "HTTP " & http.Status
Set stm = CreateObject("ADODB.Stream")
stm.Type = 1 ' adTypeBinary
stm.Open
stm.Write http.responseBody ' сырые байты ответа
stm.SaveToFile strPath, 2 ' adSaveCreateOverWrite
stm.Close
End SubHTTPS и TLS 1.2 на старых Windows
Код, который годами работал, однажды перестаёт подключаться к HTTPS-сайтам с ошибкой -2147012739 «An error occurred in the secure channel support». Сам код тут ни при чём: сервер отключил старые TLS 1.0 и 1.1, а клиентская Windows не умеет TLS 1.2 по умолчанию.
На Windows 7 и Server 2008 R2 для ServerXMLHTTP нужно установить обновление KB3140245 и прописать в реестре параметр DefaultSecureProtocols для WinHTTP. XMLHTTP, наоборот, подчиняется настройкам Internet Explorer, где TLS 1.2 включается флажком в свойствах браузера. На Windows XP TLS 1.2 недоступен вовсе, и единственный выход там это прокси или шлюз внутри сети.
Кэширование и зависание интерфейса
Ещё две ловушки встречаются особенно часто:
- XMLHTTP кэширует GET-ответы через WinINet, и повторный запрос возвращает старые данные. Добавьте заголовок Cache-Control: no-cache или уникальный параметр в URL либо перейдите на ServerXMLHTTP;
- синхронный запрос блокирует поток интерфейса: форма не перерисовывается, пока не придёт ответ. Короткие тайм-ауты и курсор ожидания делают это терпимым;
- асинхронный режим (третий параметр True) с опросом readyState в таймере снимает блокировку, но усложняет код. Используйте его, если запросы долгие;
- не создавайте объект на каждый запрос в цикле из тысяч итераций: переиспользуйте один экземпляр, вызывая Open заново.
Как это делают сегодня
Вопросы и ответы
Чем XMLHTTP отличается от ServerXMLHTTP?
XMLHTTP работает через WinINet: наследует настройки браузера и кэширует ответы. ServerXMLHTTP работает через WinHTTP: без кэша, без окон, с тайм-аутами. Для программ и служб предпочтителен второй.
Почему вместо русского текста приходят вопросительные знаки?
Сервер не указал charset, и responseText декодировал ответ как UTF-8. Возьмите responseBody и преобразуйте его через ADODB.Stream с Charset = "windows-1251".
Какую ссылку подключить в проект?
Можно Microsoft XML, v6.0, тогда доступны типы MSXML2.ServerXMLHTTP60. Но позднее связывание через CreateObject надёжнее: программа не зависит от набора библиотек на машине разработчика.