Почему закрывается 1С 7.7 при отправке больших POST-запросов и как это исправить?

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

При интеграции устаревших систем на платформе 1С:Предприятие 7.7 с современными интернет-магазинами, CRM и веб-сервисами разработчики часто сталкиваются с крайне неприятной проблемой. При попытке отправить большой объем данных (например, пакет из нескольких тысяч товаров с ценами и остатками) методом POST через HTTP-соединение, платформа 1С 7.7 мгновенно и без каких-либо предупреждений закрывается (вылетает). При этом небольшие пакеты данных (до 500-1000 позиций) отправляются абсолютно корректно.

В этой статье мы подробно разберем, почему возникает эта ошибка, проанализируем внутренние механизмы платформы и рассмотрим самые эффективные способы обхода данного ограничения с готовыми примерами кода.

Выясняем причину: почему происходит падение?

Многие разработчики ошибочно полагают, что проблема кроется в ограничениях принимающей стороны (веб-сервера) или в лимитах самого COM-объекта Microsoft.XMLHTTP (или WinHttp.WinHttpRequest.5.1). Однако это не так. Причина кроется в механизме взаимодействия встроенного языка 1С 7.7 с COM-объектами.

При передаче строкового параметра из 1С 7.7 во внешний COM-объект происходит процесс маршалинга — преобразования внутренней строковой переменной 1С в стандартный тип строки Automation — BSTR. Платформа 1С 7.7 проектировалась очень давно, и ее менеджер памяти имеет внутренние ограничения на объем непрерывного блока памяти, выделяемого под передачу текстовых параметров в библиотеки COM.

Эмпирический лимит: опытным путем установлено, что при попытке передать строковый параметр размером более 385–500 КБ напрямую в метод COM-объекта (например, в метод Send()), происходит переполнение буфера платформы. Программа закрывается аварийно по ошибке нарушения прав доступа к памяти (Access Violation, код исключения 0xc0000005), чаще всего ссылаясь на сбойные модули BASIC.DLL или bkend.dll.


Способ 1. Поэтапная отправка данных (пакетирование)

Самое очевидное и простое решение — это разбиение всего массива отправляемых данных на более мелкие порции. Если 4000 товаров приводят к падению, мы можем отправлять их пачками по 500–1000 штук в цикле.

Рассмотрим пример реализации такого алгоритма на встроенном языке 1С 7.7:


// Пример деления выгрузки на пакеты
Процедура ОтправитьДанныеПакетами()
    ВыгружаемыеТовары = СоздатьОбъект("СписокЗначений");
    // ... здесь происходит заполнение списка товаров, например, 4000 позиций ...
    
    РазмерПакета = 500;
    ВсегоТоваров = ВыгружаемыеТовары.РазмерСписка();
    ТекущийИндекс = 1;
    
    Пока ТекущийИндекс <= ВсегоТоваров Цикл
        СтрокаПакета = "";
        Для Шаг = 1 По РазмерПакета Цикл
            Если ТекущийИндекс > ВсегоТоваров Тогда
                Прервать;
            КонецЕсли;
            
            Товар = ВыгружаемыеТовары.ПолучитьЗначение(ТекущийИндекс);
            // Формируем строковое представление (например, строку JSON или XML-узел)
            СтрокаПакета = СтрокаПакета + Товар + ";"; 
            
            ТекущийИндекс = ТекущийИндекс + 1;
        КонецЦикла;
        
        // Отправляем сформированный пакет
        ОтправитьОдинПакет(СтрокаПакета);
    КонецЦикла;
КонецПроцедуры

Преимущества: простота реализации, не требует использования сторонних библиотек.

Недостатки: существенно увеличивается общее время обмена из-за накладных расходов на установку множества HTTP-соединений; принимающий веб-сервис должен уметь корректно обрабатывать частичные (порционные) данные.


Способ 2. Использование XML DOM-модели напрямую (Msxml2.DOMDocument)

Если формат вашего обмена — XML, то проблему лимита строк можно элегантно решить, полностью отказавшись от работы со строковыми переменными в 1С при отправке. Объект XMLHTTP умеет принимать в метод Send() не только строки, но и ссылки на интерфейсы других COM-объектов, например, дерево XML-документа.

При передаче COM-объекта вместо строки платформа 1С 7.7 передает лишь указатель на интерфейс IDispatch. Конвертация огромных строк в тип BSTR силами 1С не производится, и передача данных любого объема проходит абсолютно стабильно.

Разберем по шагам, как это реализовать:


Процедура ОтправитьЧерезDOM()
    // Создаем объект XML-документа
    ДомДокумент = СоздатьОбъект("Msxml2.DOMDocument.6.0");
    
    // Создаем корневой элемент
    КорневойУзел = ДомДокумент.createElement("root");
    ДомДокумент.appendChild(КорневойУзел);
    
    // Наполняем документ данными в цикле
    Для Номер = 1 По 4000 Цикл
        УзелТовара = ДомДокумент.createElement("product");
        УзелТовара.setAttribute("id", Номер);
        УзелТовара.setAttribute("name", "Товар " + Номер);
        КорневойУзел.appendChild(УзелТовара);
    КонецЦикла;
    
    // Создаем объект HTTP-запроса
    WinHttp = СоздатьОбъект("Msxml2.ServerXMLHTTP.6.0");
    WinHttp.Open("POST", "http://testtesttest.com/api/update_products", 0); 
    WinHttp.SetRequestHeader("Content-Type", "text/xml; charset=utf-8");
    
    // КРИТИЧЕСКИ ВАЖНО: передаем сам объект DOM-документа, а не его XML-строку!
    // Платформа 1С не упадет, даже если XML весит десятки мегабайт.
    WinHttp.Send(ДомДокумент);
    
    Сообщить("Статус ответа: " + WinHttp.Status);
КонецПроцедуры

Преимущества: максимальная надежность, полное отсутствие риска падения платформы из-за утечки памяти или переполнения буфера BSTR, корректная работа с кодировкой UTF-8 "из коробки".

Недостатки: данный подход работает исключительно для XML-формата. Если принимающий сервер требует JSON, напрямую передать DOM-объект не получится.


Способ 3. Передача больших файлов и JSON через ADODB.Stream

Что делать, если передаваемые данные должны быть отправлены в формате JSON или в виде бинарного файла, размер которого превышает критические 500 КБ? Здесь на помощь приходит объект ADODB.Stream.

Мы можем сформировать нужный нам JSON-файл на диске компьютера (например, в каталоге временных файлов), а затем прочитать его в двоичный поток ADODB.Stream и передать этот поток напрямую в метод Send().

Проанализируем пример кода:


Процедура ОтправитьБольшойJSON()
    ПутьКФайлу = КаталогВременныхФайлов() + "payload.json";
    
    // 1. Формируем JSON-файл на диске стандартным объектом Текст
    ТекстФайл = СоздатьОбъект("Текст");
    // Допустим, здесь очень длинная строка JSON, собранная ранее
    ТекстФайл.ДобавитьСтроку(БольшаяСтрокаJSON); 
    ТекстФайл.Записать(ПутьКФайлу);
    
    // 2. Считываем файл в двоичный поток ADODB.Stream
    Stream = СоздатьОбъект("ADODB.Stream");
    Stream.Type = 1; // Указываем тип данных: adTypeBinary (двоичный)
    Stream.Open();
    Stream.LoadFromFile(ПутьКФайлу);
    
    // 3. Выполняем отправку потока без создания длинных строк в памяти 1С
    WinHttp = СоздатьОбъект("Msxml2.ServerXMLHTTP.6.0");
    WinHttp.Open("POST", "http://testtesttest.com/api/update_products", 0); 
    WinHttp.SetRequestHeader("Content-Type", "application/json; charset=utf-8");
    
    // Передаем объект потока. Платформа успешно обработает этот вызов
    WinHttp.Send(Stream);
    
    Stream.Close();
    
    // Удаляем временный файл за собой
    ФС.УдалитьФайл(ПутьКФайлу);
    
    Сообщить("Ответ сервера: " + WinHttp.ResponseText);
КонецПроцедуры

Преимущества: универсальный метод для отправки текстовых файлов (JSON, XML, CSV) и любых бинарных файлов (изображения, архивы) неограниченного размера без риска падения программы.

Недостатки: необходимость выполнения операций записи и чтения с жесткого диска (файловый кэш), что может незначительно снизить производительность на медленных дисковых подсистемах.


Способ 4. Выполнение запроса через MSScriptControl.ScriptControl

Еще один оригинальный и очень эффективный способ — перенос выполнения HTTP-запроса целиком во внешнюю среду выполнения сценариев Windows Script Host (VBScript/JScript). Среда выполнения скриптов работает в своем собственном адресном пространстве памяти и лишена каких-либо ограничений платформы 1С 7.7 на маршалинг длинных строк.

Посмотрим, как выглядит архитектура такого решения:


Функция ОтправитьЧерезСкрипт(АдресРесурса, ДанныеЗапроса)
    Попытка
        WinHttp = СоздатьОбъект("Msxml2.ServerXMLHTTP.6.0");
        Скрипт = СоздатьОбъект("MSScriptControl.ScriptControl");
        Скрипт.Language = "VBScript";
        
        // Описываем функцию отправки на VBScript, которая примет наши COM-объекты
        ТекстСкрипта = 
        "Function SendData(HttpObj, URL, Payload)
        |   HttpObj.Open ""POST"", URL, False
        |   HttpObj.setRequestHeader ""Content-Type"", ""application/x-www-form-urlencoded; charset=utf-8""
        |   HttpObj.Send Payload
        |   SendData = HttpObj.responseText
        |End Function";
        
        Скрипт.AddCode(ТекстСкрипта);
        
        // Вызываем функцию скрипта, передавая туда ссылку на объект HTTP и данные
        Результат = Скрипт.Run("SendData", WinHttp, АдресРесурса, ДанныеЗапроса);
        Возврат Результат;
    Исключение
        Предупреждение("Ошибка при отправке: " + ОписаниеОшибки());
        Возврат "";
    КонецПопытки;
КонецФункции

Преимущества: отправка больших строк происходит напрямую из памяти скрипта. Исключается необходимость создания временных файлов на диске. Полностью обходит баг платформы 1С 7.7.

Недостатки: требует установленной в системе библиотеки msscript.ocx, которая на современных 64-битных версиях Windows может требовать дополнительной регистрации в реестре.


Способ 5. Использование внешней утилиты cUrl

Для крупных проектов интеграции отличной альтернативой является использование консольной утилиты cUrl. Это решение раз и навсегда избавляет разработчика не только от проблем с памятью в 1С 7.7, но и решает еще одну критически важную проблему — поддержку современных протоколов безопасности шифрования.

Важное примечание: старые встроенные COM-объекты Windows (такие как Microsoft.XMLHTTP) используют системные библиотеки, которые на старых ОС не поддерживают современные защищенные протоколы TLS 1.2 и TLS 1.3. Из-за этого вызовы к современным сайтам могут завершаться ошибками рукопожатия SSL (Handshake Error). Утилита cUrl всегда содержит актуальные библиотеки шифрования.

Алгоритм работы через cUrl выглядит следующим образом:

  1. 1С 7.7 выгружает данные в текстовый файл на диск.
  2. Платформа запускает исполняемый файл curl.exe с необходимыми ключами командной строки через объект WshShell.
  3. cUrl отправляет файл и записывает ответ сервера в другой файл на диске.
  4. 1С считывает файл с ответом и продолжает работу.

Пример программного вызова внешнего cUrl:


Процедура ОтправитьЧерезCurl()
    ФайлДанных = КаталогВременныхФайлов() + "request.json";
    ФайлОтвета = КаталогВременныхФайлов() + "response.json";
    
    // Записываем данные в файл
    ТекстФайл = СоздатьОбъект("Текст");
    ТекстФайл.ДобавитьСтроку(БольшаяСтрокаJSON);
    ТекстФайл.Записать(ФайлДанных);
    
    // Формируем команду для curl.exe
    // Ключ -d @ указывает утилите прочитать тело запроса из файла
    Команда = "curl.exe -X POST -H ""Content-Type: application/json"" -d @" + ФайлДанных + " http://testtesttest.com/api/update_products -o " + ФайлОтвета;
    
    // Запуск через Windows Script Host Shell
    WshShell = СоздатьОбъект("Wscript.Shell");
    // Параметр 0 скрывает черное консольное окно, параметр 1 заставляет 1С ожидать завершения работы curl
    WshShell.Run(Команда, 0, 1);
    
    // Читаем ответ сервера
    Ответ = СоздатьОбъект("Текст");
    Ответ.Открыть(ФайлОтвета);
    Если Ответ.КоличествоСтрок() > 0 Тогда
        Сообщить("Ответ успешно получен и сохранен!");
    КонецЕсли;
    
    // Удаляем временные файлы
    ФС.УдалитьФайл(ФайлДанных);
    ФС.УдалитьФайл(ФайлОтвета);
КонецПроцедуры

Преимущества: 100% стабильность работы, поддержка любых объемов данных, поддержка TLS 1.2 и TLS 1.3, возможность тонкой настройки таймаутов и заголовков запроса.

Недостатки: необходимость наличия исполняемого файла curl.exe на рабочем компьютере или сервере, где выполняется обработка.


Резюме и рекомендации

Мы проанализировали причины нестабильной работы платформы 1С 7.7 при интенсивном веб-обмене и рассмотрели эффективные архитектурные решения. Чтобы выбрать наиболее подходящий вариант для вашей задачи, воспользуйтесь следующими рекомендациями:

← На главную