Почему COM-объекты возвращают странные числа вместо Булево в 1С 7.7 и как это исправить?

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

При интеграции современного торгового оборудования и внешних драйверов, изначально разработанных под платформу «1С:Предприятие 8.х», программисты часто сталкиваются с аппаратной и программной несовместимостью в среде «1С:Предприятие 7.7». Одна из наиболее загадочных проблем — некорректное представление логических (булевых) возвращаемых значений.

В этой статье мы подробно разберем, почему методы внешних компонент или COM-объектов, работающие через OLE-автоматизацию, вместо привычных «Истина» или «Ложь» (или чисел 1 и 0) возвращают странные и, на первый взгляд, случайные миллионные числа, такие как 1245183 и 1179648. Мы проанализируем физическую природу этого явления на уровне системных типов Windows, а также рассмотрим варианты исправления ситуации как на стороне драйвера (C++), так и средствами встроенного языка 1С 7.7.

---

Симптомы проблемы и постановка задачи

Представим стандартную ситуацию: мы подключаем современный COM-драйвер кассового аппарата (ККТ), разработанный по стандартам требований к драйверам подключаемого оборудования. В платформе «1С:Предприятие 8» вызов метода закрытия смены отрабатывает отлично и возвращает тип Булево (значение Истина). Однако при вызове этого же метода в среде «1С:Предприятие 7.7» (легендарных «клюшках») мы получаем странный результат:

Проверим тип возвращаемого значения с помощью стандартной функции 1С 7.7:


// Вызов метода драйвера
Результат = ИсполнитьМетодДрайвера();
Сообщить("Тип значения: " + ТипЗначенияСтр(Результат));
Сообщить("Значение: " + Результат);

В окне сообщений мы увидим, что тип значения определен как "Число", а само число равно 1179648 или 1245183. Давайте разберемся, откуда берутся эти абстрактные величины.

---

Математический и системный анализ «магических» чисел

Для того чтобы понять причину нестыковки, переведем полученные десятичные числа в шестнадцатеричный формат (HEX), который используется компиляторами и операционной системой для адресации памяти и определения типов:

Проанализируем структуру полученных 32-битных значений. Любая переменная, передаваемая через COM/OLE-интерфейсы в Windows, упаковывается в специальную системную структуру, которая называется VARIANT. Физический размер этой структуры составляет 16 байт, а ее первые два байта (поле vt) всегда хранят идентификатор типа данных (VARTYPE).

Посмотрим на наши HEX-данные как на упакованное значение, где старшие 16 бит определяют тип, а младшие 16 бит — само значение:

  1. Старшие 16 бит (0x0012): В десятичной системе это число 18. Обратившись к официальной спецификации типов Microsoft Windows (wtypes.h), мы увидим, что константа 18 соответствует типу VT_UI2 (двухбайтовое целое число без знака — unsigned short).
  2. Младшие 16 бит (0xFFFF и 0x0000):
    • Значение 0xFFFF в знаковом 16-битном представлении (short) равно -1. Именно значение -1 (все биты установлены в 1) в технологии COM принято за логическую истину (VARIANT_TRUE).
    • Значение 0x0000 равно 0, что в COM соответствует логической лжи (VARIANT_FALSE).

Таким образом, внешняя компонента (драйвер) возвращает структуру VARIANT, в которой явно указан тип VT_UI2 (unsigned short), содержащий внутри себя логическое значение -1 или 0.

---

Почему 1С 8.х понимает этот тип, а 1С 7.7 — нет?

Выясним причину разного поведения платформ при разборе одного и того же COM-ответа:

Тип VT_UI2 (unsigned short, код 18) платформой 1С 7.7 официально не поддерживается. Столкнувшись с неподдерживаемым типом данных, OLE-парсер «семерки» не выдает ошибку, а просто считывает сырые 32 бита регистра возврата целиком, объединяя код типа (18) и само значение (-1 или 0) в один длинный регистр. В результате мы получаем склеенные числа 1245183 и 1179648.

---

Решение проблемы №1: Исправление кода драйвера на C++

Самое правильное и системное решение — это доработка кода внешней компоненты (драйвера) ее разработчиком. Для обеспечения 100% совместимости с любыми версиями 1С (как 7.7, так и 8) нет необходимости определять версию вызывающей платформы. Достаточно строго следовать спецификации COM-технологий при возврате логических типов.

Вместо использования стандартных базовых типов C++ (таких как bool, которые компилятор может упаковать в неподдерживаемый 1С 7.7 тип VT_UI2 или VT_I1) необходимо использовать явное заполнение структуры VARIANT с типом VT_BOOL.

Рассмотрим пример правильной реализации возвращаемого значения в коде внешней компоненты на C++:


// ПРИМЕР НА C++: Корректное заполнение структуры VARIANT
// bSuccess - внутренняя переменная драйвера типа bool

VARIANT varResult;
VariantInit(&varResult);

// Явно указываем стандартный поддерживаемый тип VT_BOOL (код 11)
varResult.vt = VT_BOOL; 

// Заполняем значение с использованием COM-констант
if (bSuccess) {
    varResult.boolVal = VARIANT_TRUE;  // Равно -1
} else {
    varResult.boolVal = VARIANT_FALSE; // Равно 0
}

Если для генерации интерфейсов драйвера используется IDL-описание, метод должен быть объявлен следующим образом:


// Описание метода в IDL-файле внешнего COM-объекта
[id(1), helpstring("Метод ЗакрытьСмену")] HRESULT CloseShift([out, retval] VARIANT_BOOL* Result);

При такой реализации:

  1. В 1С 8.х: Платформа распознает стандартный VT_BOOL и преобразует его в системное Булево (Истина/Ложь).
  2. В 1С 7.7: Поскольку тип VT_BOOL входит в список базовых поддерживаемых типов OLE-парсера 1С 7.7, движок успешно извлечет логическое значение и вернет стандартное число -1 (для Истины) или 0 (для Лжи). Эти числа без проблем отрабатывают в стандартных условиях Если ... Тогда.

---

Решение проблемы №2: Обходной путь в коде 1С 7.7 (Workaround)

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

Проанализируем логику работы обертки. Нам необходимо корректно обрабатывать как аномальные системные числа (1245183 / 1179648), так и стандартные значения (на случай, если драйвер в будущем все-таки обновится).

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


// ==========================================
// Функция преобразует возвращаемое значение COM-объекта в 1 или 0
// ==========================================
Функция ПреобразоватьБулевоИзCOM(Знач ЗначениеВызова) Экспорт
    
    // Проверяем тип пришедшего значения. Если это не Число - возвращаем как есть.
    Если ТипЗначенияСтр(ЗначениеВызова) <> "Число" Тогда
        Возврат ЗначениеВызова;
    КонецЕсли;
    
    // Анализируем "магические" числа, вызванные неверной интерпретацией VT_UI2
    Если ЗначениеВызова = 1245183 Тогда
        // 0x0012FFFF - это упакованный VT_UI2 со значением TRUE (-1)
        Возврат 1; 
    ИначеЕсли ЗначениеВызова = 1179648 Тогда
        // 0x00120000 - это упакованный VT_UI2 со значением FALSE (0)
        Возврат 0;
    КонецЕсли;
    
    // Если драйвер вернул стандартный корректный VT_BOOL (-1 или 0)
    // или обычные целые числа 1 / 0
    Если ЗначениеВызова <> 0 Тогда
        Возврат 1; // Любое ненулевое число трактуется как ИСТИНА
    Иначе
        Возврат 0; // Ноль трактуется как ЛОЖЬ
    КонецЕсли;

КонецФункции

Теперь использование методов драйвера в кодовой базе 1С 7.7 будет выглядеть безопасно и стабильно:


// Пример вызова метода закрытия смены с использованием обертки
РезультатВызова = Драйвер.ЗакрытьСмену();
Успех = ПреобразоватьБулевоИзCOM(РезультатВызова);

Если Успех = 1 Тогда
    Предупреждение("Смена успешно закрыта, Z-отчет распечатан!");
Иначе
    Предупреждение("Внимание! При закрытии смены произошла ошибка!");
КонецЕсли;

---

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

При проектировании кросс-платформенных внешних компонент и COM-серверов, ориентированных на работу со старыми конфигурациями 1С, всегда помните о суровых ограничениях OLE-движка версии 7.7. Избегайте использования неподдерживаемых unsigned-типов (таких как VT_UI2, VT_UI4) при передаче данных. Использование проверенного временем типа VT_BOOL гарантирует корректную работу вашего оборудования в любой версии «1С:Предприятия» без необходимости написания дополнительных «костылей» в коде конфигураций.

← На главную