При работе в терминальном режиме (RDP) часто возникает задача идентификации конкретного физического устройства, с которого подключился пользователь. Это может быть необходимо для привязки кассового оборудования, настройки принтеров или в целях безопасности. В этой статье мы подробно разберем, как получить IP-адрес клиента, используя стандартные средства Windows и возможности платформы 1С:Предприятие, не прибегая к установке сторонних компонент.
Самый простой и распространенный способ основан на том, что Windows при создании RDP-сессии сохраняет имя клиентского компьютера в переменной окружения CLIENTNAME. Зная имя компьютера, мы можем попытаться разрешить его в IP-адрес с помощью WMI-запроса к классу Win32_PingStatus.
Рассмотрим реализацию этого метода на языке 1С:
&НаКлиенте
Процедура ОпределитьIPПоИмениКлиента()
// Получаем доступ к оболочке Windows
WshShell = Новый COMОбъект("WScript.Shell");
WshSysEnv = WshShell.Environment("Process");
// Извлекаем имя клиентского ПК из переменной окружения
ИмяКомпьютераКлиента = WshSysEnv.Item("CLIENTNAME");
Если ЗначениеЗаполнено(ИмяКомпьютераКлиента) Тогда
IPАдрес = ПолучитьIpАдресПКПоИмениХоста(ИмяКомпьютераКлиента);
Сообщить("IP-адрес клиента: " + IPАдрес);
Иначе
Сообщить("Не удалось определить CLIENTNAME. Возможно, сеанс запущен не через RDP.");
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Функция ПолучитьIpАдресПКПоИмениХоста(АдресХоста)
Результат = "0.0.0.0";
Попытка
oLoc = Новый COMОбъект("WbemScripting.SWbemLocator");
oSrv = oLoc.ConnectServer(0, "/root/cimv2");
// Выполняем WMI запрос для пинга хоста и получения его адреса
ePng = oSrv.ExecQuery("SELECT * FROM Win32_PingStatus WHERE Address = '" + АдресХоста + "'");
Для Каждого СтатусХоста из ePng Цикл
Если СтатусХоста.StatusCode = 0 Тогда
Результат = СтатусХоста.ProtocolAddress;
КонецЕсли;
КонецЦикла;
Исключение
Сообщить("Ошибка WMI: " + ОписаниеОшибки());
КонецПопытки;
Возврат Результат;
КонецФункции
Важное примечание: Этот метод имеет ограничения. Он будет эффективно работать только в том случае, если сервер 1С может "достучаться" до клиентского ПК по его имени (через DNS или NetBIOS). Если клиент находится за NAT или в другой подсети без настроенного разрешения имен, метод может вернуть ошибку или неверный адрес.
Более надежный способ, который часто игнорируется, — это обращение к динамической ветке реестра текущего пользователя. При входе через RDP Windows создает раздел Volatile Environment, в котором хранятся параметры текущей сессии.
Проанализируем шаги для получения данных через реестр:
HKEY_CURRENT_USER\Volatile Environment.RDP_CLIENT_ADDRESS.Этот метод хорош тем, что не требует прав администратора и работает быстрее, чем WMI-запросы. Реализовать чтение можно через WScript.Shell и метод RegRead.
Если стандартные методы через COM-объекты не подходят, мы можем использовать проверенные временем системные утилиты, такие как GETTSCIP.exe. Чтобы не заставлять системных администраторов устанавливать дополнительные файлы на каждый сервер, применим небольшую хитрость: загрузим исполняемый файл прямо в конфигурацию 1С.
Разберем алгоритм реализации:
GETTSCIP.exe).Пример кода для извлечения и запуска:
ПутьКФайлу = КаталогВременныхФайлов() + "getip.exe";
ФайлНаДиске = Новый Файл(ПутьКФайлу);
Если Не ФайлНаДиске.Существует() Тогда
МакетПрограммы = УправлениеКлиентами.ПолучитьМакет("GETTSCIP");
МакетПрограммы.Записать(ПутьКФайлу);
КонецЕсли;
// Запуск и получение результата
WshShell = Новый COMОбъект("WScript.Shell");
Команда = """" + ПутьКФайлу + """";
// Далее читаем StdOut как показано в примерах обсуждения
В ситуациях, когда права доступа сильно ограничены, можно проанализировать активные соединения сервера. Мы знаем, что RDP обычно работает на порту 3389. Проанализируем ситуацию: если мы выполним команду netstat -n, мы увидим список всех установленных соединений.
Алгоритм действий:
netstat -n -o (флаг -o добавит PID процесса).rdpclip.exe, который уникален для каждой RDP-сессии).Хотя этот метод кажется сложным в реализации из-за необходимости парсинга текстовых строк, он является "неубиваемым", так как опирается на реальные сетевые факты, а не на переменные, которые могут быть не заполнены.
Подводя итог, можно сказать, что универсального метода "одной кнопкой" не существует, так как многое зависит от настроек сетевой безопасности предприятия. Наиболее профессиональным подходом для разработчика 1С будет комбинирование методов:
CLIENTNAME и RDP_CLIENT_ADDRESS из реестра или переменных окружения.Win32_PingStatus.Использование COMОбъект("MSScriptControl.ScriptControl"), как упоминалось в обсуждении, сейчас считается устаревшим (особенно в 64-битных системах), поэтому рекомендуем ориентироваться на прямую работу с WScript.Shell или использование PowerShell-скриптов, которые можно запускать аналогичным образом.