При интеграции современного торгового оборудования и внешних драйверов, изначально разработанных под платформу «1С:Предприятие 8.х», программисты часто сталкиваются с аппаратной и программной несовместимостью в среде «1С:Предприятие 7.7». Одна из наиболее загадочных проблем — некорректное представление логических (булевых) возвращаемых значений.
В этой статье мы подробно разберем, почему методы внешних компонент или COM-объектов, работающие через OLE-автоматизацию, вместо привычных «Истина» или «Ложь» (или чисел 1 и 0) возвращают странные и, на первый взгляд, случайные миллионные числа, такие как 1245183 и 1179648. Мы проанализируем физическую природу этого явления на уровне системных типов Windows, а также рассмотрим варианты исправления ситуации как на стороне драйвера (C++), так и средствами встроенного языка 1С 7.7.
---
Представим стандартную ситуацию: мы подключаем современный COM-драйвер кассового аппарата (ККТ), разработанный по стандартам требований к драйверам подключаемого оборудования. В платформе «1С:Предприятие 8» вызов метода закрытия смены отрабатывает отлично и возвращает тип Булево (значение Истина). Однако при вызове этого же метода в среде «1С:Предприятие 7.7» (легендарных «клюшках») мы получаем странный результат:
1 (Истина) или 0 (Ложь) переменная-результат принимает значение 1245183 (в случае успеха) или 1179648 (в случае неудачи или ошибки).Проверим тип возвращаемого значения с помощью стандартной функции 1С 7.7:
// Вызов метода драйвера
Результат = ИсполнитьМетодДрайвера();
Сообщить("Тип значения: " + ТипЗначенияСтр(Результат));
Сообщить("Значение: " + Результат);
В окне сообщений мы увидим, что тип значения определен как "Число", а само число равно 1179648 или 1245183. Давайте разберемся, откуда берутся эти абстрактные величины.
---
Для того чтобы понять причину нестыковки, переведем полученные десятичные числа в шестнадцатеричный формат (HEX), который используется компиляторами и операционной системой для адресации памяти и определения типов:
0x0012FFFF.0x00120000.Проанализируем структуру полученных 32-битных значений. Любая переменная, передаваемая через COM/OLE-интерфейсы в Windows, упаковывается в специальную системную структуру, которая называется VARIANT. Физический размер этой структуры составляет 16 байт, а ее первые два байта (поле vt) всегда хранят идентификатор типа данных (VARTYPE).
Посмотрим на наши HEX-данные как на упакованное значение, где старшие 16 бит определяют тип, а младшие 16 бит — само значение:
18 соответствует типу VT_UI2 (двухбайтовое целое число без знака — unsigned short).0xFFFF в знаковом 16-битном представлении (short) равно -1. Именно значение -1 (все биты установлены в 1) в технологии COM принято за логическую истину (VARIANT_TRUE).0x0000 равно 0, что в COM соответствует логической лжи (VARIANT_FALSE).Таким образом, внешняя компонента (драйвер) возвращает структуру VARIANT, в которой явно указан тип VT_UI2 (unsigned short), содержащий внутри себя логическое значение -1 или 0.
---
Выясним причину разного поведения платформ при разборе одного и того же COM-ответа:
VT_UI2 со значениями 0xFFFF или 0x0000, он автоматически производит неявное приведение типов (маршалинг) и транслирует результат в системный тип Булево (значения Истина или Ложь).VARIANT. В 1С 7.7 нет встроенного типа "Булево", а логика реализуется через обычные числа (0 — ложь, любое другое — истина). Более того, список поддерживаемых OLE-типов в 1С 7.7 ограничен следующими константами: VT_EMPTY, VT_I2, VT_I4, VT_R4, VT_R8, VT_CY, VT_DATE, VT_BSTR, VT_DISPATCH и VT_BOOL.Тип VT_UI2 (unsigned short, код 18) платформой 1С 7.7 официально не поддерживается. Столкнувшись с неподдерживаемым типом данных, OLE-парсер «семерки» не выдает ошибку, а просто считывает сырые 32 бита регистра возврата целиком, объединяя код типа (18) и само значение (-1 или 0) в один длинный регистр. В результате мы получаем склеенные числа 1245183 и 1179648.
---
Самое правильное и системное решение — это доработка кода внешней компоненты (драйвера) ее разработчиком. Для обеспечения 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);
При такой реализации:
VT_BOOL и преобразует его в системное Булево (Истина/Ложь).VT_BOOL входит в список базовых поддерживаемых типов OLE-парсера 1С 7.7, движок успешно извлечет логическое значение и вернет стандартное число -1 (для Истины) или 0 (для Лжи). Эти числа без проблем отрабатывают в стандартных условиях Если ... Тогда.---
Если у нас нет исходных кодов драйвера и мы не можем связаться с его разработчиком для оперативного выпуска обновления, проблему можно решить непосредственно на стороне встроенного языка 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С:Предприятия» без необходимости написания дополнительных «костылей» в коде конфигураций.