Как правильно обрабатывать кириллицу и строковые свойства во внешней компоненте 1С на C++?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Разработка внешних компонент для 1С по технологии Native API на языке C++ часто сопровождается рядом трудностей, особенно для начинающих разработчиков. Основные подводные камни обычно кроются в работе с памятью, типами данных, а также в кодировках при передаче кириллицы между различными системами, сетью и платформой 1С. Разберем типичные проблемы, возникающие при создании компоненты, которая обменивается данными по сети через сокеты, и детально проанализируем способы их решения.

Проблема 1: Искажение кириллицы (кракозябры) при получении данных

Представим ситуацию: у нас есть консольный клиент на C++, который отправляет текстовое сообщение на русском языке через TCP-сокет, а внешняя компонента его принимает. Однако вместо русского текста в компоненту приходят непонятные символы. Выясним причину такого поведения.

Если мы используем стандартную консоль Windows для ввода текста, она может работать в устаревшей OEM-кодировке (например, CP866). Для быстрого исправления ситуации на стороне консольного клиента можно принудительно установить кодировку Windows-1251 перед вводом данных:

system("chcp 1251");

Это позволит консоли правильно считывать вводимые русские символы. Однако в современной разработке передавать данные по сети принято в универсальной кодировке UTF-8. Платформа 1С:Предприятие внутри себя оперирует строками в формате Unicode (UTF-16), для которых в C++ используется тип wchar_t.

Чтобы корректно преобразовать полученные по сети байты в формат, понятный платформе 1С, использование стандартной функции C mbstowcs не всегда оправдано, так как ее результат зависит от текущей локали операционной системы. В Windows гораздо надежнее использовать функцию WinAPI MultiByteToWideChar, явно указав кодировку (например, CP_UTF8 или CP_ACP). Это гарантирует, что кириллица будет преобразована в Unicode без искажений, независимо от региональных настроек компьютера.

Проблема 2: Невозможность прочитать строковое свойство из 1С в компоненте

Рассмотрим следующую ситуацию: мы пытаемся передать строку из кода 1С в свойство компоненты. В коде 1С мы пишем:

ОбъектКомпоненты.Ip = "127.0.0.1";

А в коде компоненты на C++ проверяем тип переданного значения:


if (TV_VT(varPropVal) != VTYPE_PSTR)
    return false;

При отладке мы видим, что свойство равно пустой строке, а условие не выполняется. В чем же ошибка? Проблема заключается в том, что платформа 1С в современных версиях передает строки в формате Unicode. Тип переменной в структуре tVariant будет не VTYPE_PSTR (указатель на ANSI-строку), а VTYPE_PWSTR (указатель на Wide-строку), и само значение будет находиться в поле pwstrVal.

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


case 2: // Индекс нашего свойства
    if(TV_VT(var) == VTYPE_PSTR) { 
        delete[] Prop2;
        size_t len = std::strlen(var->pstrVal);
        Prop2 = new char[len + 1];
        std::strncpy(Prop2, var->pstrVal, len + 1);
        break;
    } else if(TV_VT(var) == VTYPE_PWSTR) {
        delete[] Prop2;
        // Здесь необходимо реализовать функцию конвертации
        // WCHAR_to_char(Prop2, var->pwstrVal); 
        // или использовать MultiByteToWideChar для обратного преобразования
        break;
    } else {
        return false;
    }

Важное примечание по работе с памятью: Если внешняя компонента должна вернуть строку обратно в 1С (через возвращаемое значение метода или чтение свойства), память под эту строку должна выделяться только через внутренний менеджер памяти 1С (используя метод AllocMemory интерфейса IMemoryManager). Использование new или malloc для возвращаемых в 1С строк приведет к аварийному завершению работы платформы, так как 1С попытается очистить эту память своими внутренними механизмами.

Проблема 3: Сравнение строк в C++ и бесконечные циклы

Иногда при написании логики обработки команд (например, команды закрытия порта) разработчики допускают синтаксические ошибки, специфичные для C/C++. Посмотрим на пример кода:


char msg[256];
// ... получение данных в msg ...
if (msg == "ClosePort") {
    break;
}

Этот код никогда не прервет цикл. В C++ переменная msg представляет собой указатель на массив символов. Оператор == в данном случае сравнивает не содержимое строк, а адреса памяти. Естественно, адрес буфера msg и адрес строкового литерала "ClosePort" никогда не совпадут.

Для корректного сравнения Си-строк необходимо использовать специальные функции:

  1. Для узких строк (char*) используйте: strcmp(msg, "ClosePort") == 0
  2. Для широких строк (wchar_t*) используйте: wcscmp(Data, L"ClosePort") == 0

Также, в современном C++ рекомендуется использовать классы std::string или std::wstring. Для них оператор == перегружен и корректно сравнивает именно текстовое содержимое, что делает код более безопасным и читаемым.

Проблема 4: Слетели русские названия методов и свойств при компиляции в Visual Studio

При сборке компоненты под разные архитектуры (32/64 bit) можно столкнуться с тем, что свойства и методы на русском языке перестают работать в 1С. В отладчике имена свойств могут выглядеть как иероглифы вида Статус. При этом английские идентификаторы продолжают работать исправно.

Это классическая проблема кодировки исходных файлов. Внешние компоненты Native API требуют, чтобы русские имена методов задавались широкими строками (литералы с префиксом L, например, L"Статус"). Если исходный файл сохранен в кодировке UTF-8, но компилятор (например, MSVC в Visual Studio) интерпретирует его как Windows-1251 (ANSI), русские символы будут скомпилированы неправильно.

Рассмотрим шаги для решения этой проблемы в Visual Studio:

  1. Сохранение файла в правильной кодировке: В меню Visual Studio выберите "File" -> "Save ... as". На кнопке "Save" нажмите стрелочку вниз и выберите "Save with Encoding". В появившемся окне выберите кодировку UTF-8 with signature (UTF-8 с BOM). Наличие BOM (Byte Order Mark) явно подскажет компилятору, как читать файл.
  2. Указание ключей компилятору: В свойствах проекта C++ (Properties -> C/C++ -> Command Line) необходимо добавить флаг компилятора /utf-8. Этот флаг принудительно указывает компилятору MSVC читать исходные файлы и формировать строковые литералы в исполняемом файле в кодировке UTF-8.

Применяя эти практики работы с кодировками, типами данных Native API и менеджером памяти 1С, вы сможете избежать большинства типовых ошибок и создать стабильно работающую внешнюю компоненту.

← На главную