Как программно отличить терминального пользователя от физического в 1С?

Программист 1С v8.3 (Обычные формы) 1С:Управление торговлей Торговля и дистрибуция
← На главную

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

Рассмотрим, как с помощью кода 1С запросить у операционной системы сведения о сеансе и корректно отличить физический запуск от удаленного (терминального).

В чем заключается проблема штатного метода 1С?

Проанализируем стандартное поведение платформы: функция ИмяКомпьютера() опрашивает имя хоста, на котором выполняется клиентский процесс. Если пользователь заходит на терминальный сервер (или запускает 1С через RemoteApp), процесс 1cv8.exe физически выполняется на сервере. Соответственно, ИмяКомпьютера() вернет сетевое имя терминального сервера как для локального администратора, сидящего за консолью сервера, так и для всех удаленных пользователей.

Чтобы разделить настройки оборудования и рабочие места пользователей, нам необходимо обратиться к переменным окружения операционной системы Windows.

Использование переменных окружения Windows для определения сессии

При создании удаленного сеанса Windows автоматически формирует специфические переменные среды процесса:

  1. CLIENTNAME — содержит сетевое имя физического компьютера, с которого пользователь подключился по RDP. Если запуск произошел локально (на самом сервере или ПК), эта переменная пуста или содержат имя консоли.
  2. SESSIONNAME — указывает имя текущей сессии. Для локального входа значение обычно равно Console, а для удаленных пользователей — имеет вид RDP-Tcp#0 или аналогичный.

Для считывания этих системных переменных из 1С мы можем использовать COM-объект WScript.Shell.

Реализация считывания имени клиента через WScript.Shell

Рассмотрим базовая пример получения переменной окружения CLIENTNAME с помощью обращения к окружению процесса:


WSS = Новый COMОбъект("WScript.Shell");
WSSE = WSS.Environment("Process");
ИмяКлиентскогоПК = ВРег(WSSE.Item("ClientName"));

Если ПустаяСтрока(ИмяКлиентскогоПК) Или ИмяКлиентскогоПК = "CONSOLE" Тогда
    // Запуск произведен локально на сервере или в консольной сессии
    ИмяРабочегоМеста = ВРег(ИмяКомпьютера());
Иначе
    // Сеанс запущен через RDP / RemoteApp
    ИмяРабочегоМеста = ВРег(ИмяКомпьютера()) + "_" + ИмяКлиентскогоПК;
КонецЕсли;

Также проанализируем альтернативный метод expandEnvironmentStrings, который позволяет безопасно разворачивать подстановки переменных среды:


WshShell = Новый COMОбъект("WScript.Shell");
ИмяКомпьютераКлиента = WshShell.expandEnvironmentStrings("%CLIENTNAME%");
ИмяСессии = WshShell.expandEnvironmentStrings("%SESSIONNAME%");

Если ВРег(ИмяСессии) = "CONSOLE" Или ИмяКомпьютераКлиента = "%CLIENTNAME%" Тогда
    // Физический запуск на локальной машине
    ИмяРабочегоМеста = ИмяКомпьютера();
Иначе
    // Удаленный терминальный сеанс
    ИмяРабочегоМеста = ИмяКомпьютера() + "_" + ИмяКомпьютераКлиента;
КонецЕсли;

Готовая универсальная функция для интеграции в механизмы торгового оборудования

Объединим проверки в отказоустойчивую функцию с обработкой исключений. Это гарантирует, что даже при отсутствии COM-объекта в операционной системе (или при специфических правах доступа) программа не завершится с ошибкой.


Функция ПолучитьУникальноеИмяРабочегоМеста() Экспорт

    СформированноеИмя = ВРег(ИмяКомпьютера());
    ИмяКлиентаRDP = "";

    Попытка
        WshShell = Новый COMОбъект("WScript.Shell");
        ИмяКлиентаRDP = WshShell.expandEnvironmentStrings("%CLIENTNAME%");
    Исключение
        ИмяКлиентаRDP = "";
    КонецПопытки;

    ИмяКлиентаRDP = ВРег(СокрЛП(ИмяКлиентаRDP));

    // Проверяем, что переменная вернула корректное имя, а не исходный шаблон %CLIENTNAME% и не CONSOLE
    Если ЗначениеЗаполнено(ИмяКлиентаRDP) 
        И НЕ ИмяКлиентаRDP = "%CLIENTNAME%" 
        И НЕ ИмяКлиентаRDP = "CONSOLE" Тогда
        
        // Формируем составное имя: ИмяТерминальногоСервера_ИмяКлиентскогоПК
        СформированноеИмя = СформированноеИмя + "_" + ИмяКлиентаRDP;
    КонецЕсли;

    Возврат СформированноеИмя;

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

Важные технические ограничения и особенности

При внедрении данного решения обратите внимание на следующие нюансы:

  1. Платформа исполнения: Использование COMОбъект("WScript.Shell") работает исключительно в ОС Windows на стороне клиента (контекст &НаКлиенте). На клиентах под управлением Linux, macOS, а также при работе через веб-клиент COM-объекты недоступны.
  2. Контекст выполнения кода: Данную функцию необходимо вызывать строго на клиенте. Если запустить считывание переменных среды на сервере 1С, будут получены переменные окружения службы 1CV8SERVER, а не сеанса пользователя.
  3. Использование WinAPI: В системном API Windows существует функция GetSystemMetrics(SM_REMOTESESSION). Однако в современных ОС (Windows Server 2012 и новее) при включенном RemoteFX vGPU данный параметр может возвращать 0 даже для удаленных сеансов. По этой причине проверка переменных среды CLIENTNAME и SESSIONNAME является более надежным способом.
  4. Ключи запуска 1С: Если требуется разделить логику работы оборудования без привязки к имени ПК, можно передавать системный флаг в строке запуска 1С с помощью ключа /C (например, 1cv8.exe ... /C TerminalUser) и считывать его на старте программы функцией ПараметрЗапуска.
← На главную