Как определить, что сеанс 1С запущен в терминальной сессии RDP

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

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

Анализ проблемы: почему стандартных средств недостаточно

Рассмотрим типовую ситуацию: в конфигурации "Управление торговлей" настройки торгового оборудования привязываются к конкретному имени компьютера. Когда несколько пользователей заходят на сервер через Remote Desktop Protocol (RDP), для системы они все работают на одном и том же "железе". В результате, если "Пользователь 1" настроит сканер на COM3, а "Пользователь 2" перенастроит его под себя на COM5, настройки первого пользователя затрутся, так как 1С видит одно и то же имя компьютера.

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

Способ 1: Использование переменной окружения SESSIONNAME

В операционной системе Windows для каждой сессии создаются переменные окружения. Самая важная для нас — %SESSIONNAME%. Если пользователь работает непосредственно за монитором сервера (консольный вход), значение этой переменной будет Console. Если же вход выполнен удаленно, значение будет начинаться с префикса RDP-Tcp# (для протокола Microsoft) или ICA (для Citrix).

Посмотрим на пример реализации функции определения терминальной сессии с использованием объекта WScript.Shell:


Функция ЭтоТерминальнаяСессия() Экспорт
    Попытка
        WShell = Новый COMОбъект("WScript.Shell");
        // Получаем значение переменной окружения SESSIONNAME
        ЗначениеПеременной = WShell.ExpandEnvironmentStrings("%SESSIONNAME%");
    Исключение
        Возврат Ложь;
    КонецПопытки;

    // Если в строке есть "RDP", значит сессия удаленная
    // Также можно проверять на неравенство значению "Console"
    Возврат (Найти(ВРег(ЗначениеПеременной), "RDP") > 0);
КонецФункции

Способ 2: Использование переменной CLIENTNAME для идентификации физического ПК

Хотя добавление логина пользователя к имени сервера (например, SERVER_IVANOV) решает проблему конфликта настроек, это не всегда удобно. Если один и тот же пользователь может заходить в 1С с разных физических компьютеров, настройки оборудования (которое подключено к конкретному ПК) должны "путешествовать" вместе с ним или жестко привязываться к железу.

Для этого мы можем использовать переменную %CLIENTNAME%. В ней хранится сетевое имя того компьютера, с которого пользователь подключился к терминалу. Рассмотрим, как расширить логику получения имени компьютера:


Функция ПолучитьУникальноеИмяКомпьютера() Экспорт
    ИмяСервера = ВРег(ИмяКомпьютера());
    
    Попытка
        WShell = Новый COMОбъект("WScript.Shell");
        ТипСессии = ВРег(WShell.ExpandEnvironmentStrings("%SESSIONNAME%"));
        ИмяКлиента = ВРег(WShell.ExpandEnvironmentStrings("%CLIENTNAME%"));
    Исключение
        Возврат ИмяСервера;
    КонецПопытки;

    // Проверяем, что мы в RDP, а не в консоли
    Если Найти(ТипСессии, "RDP") > 0 ИЛИ Найти(ТипСессии, "ICA") > 0 Тогда
        // Если имя клиента доступно, используем его для привязки к железу
        Если ЗначениеЗаполнено(ИмяКлиента) И ИмяКлиента <> "%CLIENTNAME%" Тогда
            Возврат ИмяСервера + "_" + ИмяКлиента;
        Иначе
            // Если имя клиента не определено, привязываемся к пользователю 1С
            Возврат ИмяСервера + "_" + ВРег(ИмяПользователя());
        КонецЕсли;
    КонецЕсли;

    Возврат ИмяСервера;
КонецФункции

Способ 3: Доступ через Win32 API (GetSystemMetrics)

Для более надежной проверки, которая не зависит от того, были ли переопределены переменные окружения, можно воспользоваться системным вызовом Windows API GetSystemMetrics с параметром SM_REMOTESESSION (индекс 0x1000). Этот метод возвращает ненулевое значение, если сеанс является удаленным.

В 1С это можно реализовать через запуск небольшого скрипта PowerShell или через создание внешней компоненты, однако для большинства бизнес-задач метод с WScript.Shell оказывается достаточным и более простым в поддержке.

Особенности работы в среде Citrix и нестандартных ОС

Проанализируем ситуацию, если ваша компания использует решения от Citrix. В этом случае проверка подстроки "RDP" не сработает. Выясним, как сделать код универсальным:

  1. Проверяйте значение %SESSIONNAME% на неравенство строке "Console". Это отсечет локальные входы.
  2. Учитывайте, что в старых операционных системах (например, Windows XP, использованная в качестве сервера) или при использовании патчей для многопользовательской работы, переменные могут инициализироваться некорректно. В таких случаях связка ИмяКомпьютера() + "_" + ИмяПользователя() является наиболее стабильным "костылем".
  3. Обратите внимание на динамические порты. При пробросе портов через RDP, локальный COM1 на сервере может отображаться как COM3. Использование уникального имени компьютера для каждого сеанса позволит 1С запомнить эти соответствия индивидуально.

Рекомендации по внедрению в типовые конфигурации

Для того чтобы внедрить эти изменения в конфигурации вроде "Управление торговлей 10.3", найдите общий модуль (например, ТОСервер или МенеджерОборудования) и функцию, отвечающую за получение имени компьютера (обычно она называется ПолучитьИмяКомпьютераТО). Замените стандартный возврат ИмяКомпьютера() на вызов созданной вами функции, учитывающей терминальный режим.

Важный момент: при использовании такого подхода настройки оборудования для пользователя "сбросятся" в первый раз, так как его "новый" компьютер в справочнике или регистре 1С теперь будет называться, например, OFFICE-SERVER_USER1 вместо простого OFFICE-SERVER. Потребуется единоразовая повторная настройка портов для всех рабочих мест.

← На главную