При работе в многопользовательском режиме на терминальном сервере (RDP) программисты 1С часто сталкиваются с проблемой идентификации рабочего места. Стандартная функция ИмяКомпьютера() в этом случае всегда возвращает имя сервера, что делает невозможным разделение настроек торгового оборудования (сканеров штрихкодов, ККТ, принтеров этикеток) для разных пользователей. Разберем подробно, как решить эту задачу с помощью системных переменных Windows и механизмов 1С.
Рассмотрим типовую ситуацию: в конфигурации "Управление торговлей" настройки торгового оборудования привязываются к конкретному имени компьютера. Когда несколько пользователей заходят на сервер через Remote Desktop Protocol (RDP), для системы они все работают на одном и том же "железе". В результате, если "Пользователь 1" настроит сканер на COM3, а "Пользователь 2" перенастроит его под себя на COM5, настройки первого пользователя затрутся, так как 1С видит одно и то же имя компьютера.
Для решения нам необходимо научить 1С отличать сеансы друг от друга, добавляя к имени компьютера уникальный идентификатор пользователя или сессии, но только в том случае, если работа идет через терминал. Проанализируем способы реализации этой проверки.
В операционной системе Windows для каждой сессии создаются переменные окружения. Самая важная для нас — %SESSIONNAME%. Если пользователь работает непосредственно за монитором сервера (консольный вход), значение этой переменной будет Console. Если же вход выполнен удаленно, значение будет начинаться с префикса RDP-Tcp# (для протокола Microsoft) или ICA (для Citrix).
Посмотрим на пример реализации функции определения терминальной сессии с использованием объекта WScript.Shell:
Функция ЭтоТерминальнаяСессия() Экспорт
Попытка
WShell = Новый COMОбъект("WScript.Shell");
// Получаем значение переменной окружения SESSIONNAME
ЗначениеПеременной = WShell.ExpandEnvironmentStrings("%SESSIONNAME%");
Исключение
Возврат Ложь;
КонецПопытки;
// Если в строке есть "RDP", значит сессия удаленная
// Также можно проверять на неравенство значению "Console"
Возврат (Найти(ВРег(ЗначениеПеременной), "RDP") > 0);
КонецФункции
Хотя добавление логина пользователя к имени сервера (например, SERVER_IVANOV) решает проблему конфликта настроек, это не всегда удобно. Если один и тот же пользователь может заходить в 1С с разных физических компьютеров, настройки оборудования (которое подключено к конкретному ПК) должны "путешествовать" вместе с ним или жестко привязываться к железу.
Для этого мы можем использовать переменную %CLIENTNAME%. В ней хранится сетевое имя того компьютера, с которого пользователь подключился к терминалу. Рассмотрим, как расширить логику получения имени компьютера:
Функция ПолучитьУникальноеИмяКомпьютера() Экспорт
ИмяСервера = ВРег(ИмяКомпьютера());
Попытка
WShell = Новый COMОбъект("WScript.Shell");
ТипСессии = ВРег(WShell.ExpandEnvironmentStrings("%SESSIONNAME%"));
ИмяКлиента = ВРег(WShell.ExpandEnvironmentStrings("%CLIENTNAME%"));
Исключение
Возврат ИмяСервера;
КонецПопытки;
// Проверяем, что мы в RDP, а не в консоли
Если Найти(ТипСессии, "RDP") > 0 ИЛИ Найти(ТипСессии, "ICA") > 0 Тогда
// Если имя клиента доступно, используем его для привязки к железу
Если ЗначениеЗаполнено(ИмяКлиента) И ИмяКлиента <> "%CLIENTNAME%" Тогда
Возврат ИмяСервера + "_" + ИмяКлиента;
Иначе
// Если имя клиента не определено, привязываемся к пользователю 1С
Возврат ИмяСервера + "_" + ВРег(ИмяПользователя());
КонецЕсли;
КонецЕсли;
Возврат ИмяСервера;
КонецФункции
Для более надежной проверки, которая не зависит от того, были ли переопределены переменные окружения, можно воспользоваться системным вызовом Windows API GetSystemMetrics с параметром SM_REMOTESESSION (индекс 0x1000). Этот метод возвращает ненулевое значение, если сеанс является удаленным.
В 1С это можно реализовать через запуск небольшого скрипта PowerShell или через создание внешней компоненты, однако для большинства бизнес-задач метод с WScript.Shell оказывается достаточным и более простым в поддержке.
Проанализируем ситуацию, если ваша компания использует решения от Citrix. В этом случае проверка подстроки "RDP" не сработает. Выясним, как сделать код универсальным:
%SESSIONNAME% на неравенство строке "Console". Это отсечет локальные входы.ИмяКомпьютера() + "_" + ИмяПользователя() является наиболее стабильным "костылем".COM1 на сервере может отображаться как COM3. Использование уникального имени компьютера для каждого сеанса позволит 1С запомнить эти соответствия индивидуально.Для того чтобы внедрить эти изменения в конфигурации вроде "Управление торговлей 10.3", найдите общий модуль (например, ТОСервер или МенеджерОборудования) и функцию, отвечающую за получение имени компьютера (обычно она называется ПолучитьИмяКомпьютераТО). Замените стандартный возврат ИмяКомпьютера() на вызов созданной вами функции, учитывающей терминальный режим.
Важный момент: при использовании такого подхода настройки оборудования для пользователя "сбросятся" в первый раз, так как его "новый" компьютер в справочнике или регистре 1С теперь будет называться, например, OFFICE-SERVER_USER1 вместо простого OFFICE-SERVER. Потребуется единоразовая повторная настройка портов для всех рабочих мест.