Рано или поздно любой “удалёнщик” открывает для себя по-настоящему крутую связку софта для удаленного управления из двух бесплатных приложений - Sunshine (серверная часть) и Moonlight (клиентская часть).
Что же такого крутого в этом софте - спросит любой давний поклонник этих Ваших “rdp,vnc, энидесков, тимвьюверов, замшелой удаленной консоли” и прочих типов удаленного доступа?
Если очень коротко, то суть именно этой “связки” софта в том, что она способна идеально “выжать” из возможностей Ваших видеокарты и процессора всё, на что они способны и реально выдать вплоть до 120FPS на 4K даже на не слишком “толстом” интернет-канале. А там где интернет-канал будет совсем “тонким”, эта "связка" софта способна “выжать” такие значения FPS, которые и не снились, ни rdp, ни прочим “хитрым” вариантам умной передачи экрана. Иными словами, Вы во многих ситуациях получите удаленную сессию управления, в которой будет абсолютно полное ощущение, что Вы работаете в конкретный момент не за удаленным рабочим столом, а просто локально. При нормальном интернете Вы даже сможете смотреть (или редактировать) 4K видео с 60FPS в удаленной сессии, если Вам это понадобится.
В этой статье я хотел бы рассказать, как я нестандартно решил проблему, которая неизбежно возникает у пользователей приложений Sunshine и Moonlight, в ситуации, если на серверной стороне к удаленному устройству подключено сразу несколько мониторов в режиме “расширить рабочее пространство сразу на несколько мониторов” + сама трансляция экрана в удаленную сессию настроена через виртуальный дисплей.
Отдельно стоит отметить, что использование виртуального рабочего дисплея с указанным софтом крайне рекомендовано по той причине, что с виртуальным рабочим дисплеем, установленным на серверной стороне, Вы можете задать любые разрешения удаленного экрана, с любой частотой обновления экрана, и Вы никак не будете ограничены реальными физическими параметрами физических мониторов (и их наличием вообще!), подключенных к удаленному устройству.
Кроме того, трансляция видеопотока с виртуального дисплея гораздо меньше “нагружает” серверную сторону и позволяет идеально оптимизировать сам поток данных.
Но с этими несомненными “плюсами” возникнет и та самая неизбежная проблема, о которой я напишу далее. Дело в том, что когда Вы подключитесь к удаленному виртуальному дисплею и начнете работать с различными приложениями, потом, когда Вы захотите вернуть “нормальный” режим работы на серверной стороне, Вы просто “потеряете” все окна приложений, которые ранее были запущены на виртуальном дисплее. И эти окна приложений просто перестанут отображаться на реальных физических дисплеях удаленного устройства, оставшись “жить” на виртуальном дисплее.
Как решить эту проблему? Очень просто!
Напишу на примере Windows:
Создайте такой скрипт на PowerShell C:\Scripts\fix_windows.ps1.
try { Add-Type @" using System; using System.Runtime.InteropServices; public class Win32FinalComfortFix { [DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); [DllImport("user32.dll")] public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); [DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr hWnd); [DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow); [DllImport("user32.dll")] public static extern bool SetProcessDpiAwarenessContext(IntPtr value); public struct RECT { public int Left; public int Top; public int Right; public int Bottom; } } "@ } catch {} try { [Win32FinalComfortFix]::SetProcessDpiAwarenessContext([IntPtr](-4)) } catch {} # 1. СПИСОК ПРОГРАММ $AppList = @( "proxifier", "firefox", "firefoxportable", "happ", "taskmgr", "bvssh", "idman", "sbiectrl", "notepad", "totalcmd", "AkelPad", "VirtualBoxPortable", "VirtualBox", "swriter", "scalc", "msedge", "truecrypt", "miranda32", "element", "fancontrol", "ThrottleStop", "SamsungMagician", "cmd", "WindowsTerminal", "WNetWatcher", "WifiInfoView", "LM Studio", "explorer" ) Start-Sleep -Seconds 2 # Делаем переменные координат глобальными для функции $script:offsetX = 200 $script:offsetY = 200 $windowWidth = 1920 $windowHeight = 1080 $uFlags = 0x0040 $SW_SHOWNORMAL = 1 # Функция перемещения окна (работает и для обычных программ, и для Проводника) function Move-TargetWindow($hwnd) { $rect = New-Object Win32FinalComfortFix+RECT if ([Win32FinalComfortFix]::GetWindowRect($hwnd, [ref]$rect)) { # Если окно улетело на координаты Sunshine if ($rect.Left -gt 6000 -or $rect.Left -lt -5000) { [Win32FinalComfortFix]::ShowWindowAsync($hwnd, $SW_SHOWNORMAL) Start-Sleep -Milliseconds 60 [Win32FinalComfortFix]::SetWindowPos($hwnd, [IntPtr]::Zero, $script:offsetX, $script:offsetY, $windowWidth, $windowHeight, $uFlags) [Win32FinalComfortFix]::SetForegroundWindow($hwnd) # Сдвиг лесенки $script:offsetX += 80 $script:offsetY += 50 if ($script:offsetX -gt 800) { $script:offsetX = 200 $script:offsetY = 200 } } } } $shellApp = New-Object -ComObject Shell.Application foreach ($appName in $AppList) { if ($appName -eq "explorer") { # Специальный перебор окон Проводника $explorerWindows = $shellApp.Windows() | Where-Object { $_.Name -eq "Проводник" -or $_.Name -eq "File Explorer" -or $_.FullName -like "*explorer.exe" } foreach ($window in $explorerWindows) { if ($window.HWND) { Move-TargetWindow ([IntPtr]$window.HWND) } } continue } # Стандартный перебор для остальных процессов $processes = Get-Process -Name $appName -ErrorAction SilentlyContinue foreach ($proc in $processes) { $hwnd = $proc.MainWindowHandle if ($hwnd -ne [IntPtr]::Zero) { Move-TargetWindow $hwnd } } }
В этом скрипте Вам, по сути, нужно настроить только один блок кода:
1. СПИСОК ПРОГРАММ
$AppList = @( “proxifier”, “firefox”, “firefoxportable”, “happ”, “taskmgr”, “bvssh”, “idman”, “sbiectrl”, “notepad”, “totalcmd”, “AkelPad”, “VirtualBoxPortable”, “VirtualBox”, “swriter”, “scalc”, “msedge”, “truecrypt”, “miranda32”, “element”, “fancontrol”, “ThrottleStop”, “SamsungMagician”, “cmd”, “WindowsTerminal”, “WNetWatcher”, “WifiInfoView”, “LM Studio”, “explorer” )
Тут перечислены имена процессов приложений, окна которых Вы как раз и хотите “вернуть” на основной рабочий стол удаленного устройства, после завершения работы с Sunshine и Moonlight.
Разумеется, этот список можно дополнить любыми нужными Вам приложениями.
Теперь Вы можете либо вручную запускать этот скрипт после завершения удаленной сессии, либо Вы можете настроить автоматический запуск этого скрипта в Sunshine.
Сделать это можно так:
Откройте браузер и перейдите в панель управления Sunshine: http://localhost:47990
Перейдите во вкладку “Applications” в верхнем меню.
Найдите в списке ваше приложение (обычно это “Desktop”) и нажмите кнопку “Edit” напротив него.
Прокрутите страницу вниз до блока настроек команд запуска.
Найдите поле “Undo Command” (команда, выполняемая после закрытия сессии Moonlight).
Вставьте в это поле следующую строку: powershell.exe -ExecutionPolicy Bypass -File “C:\Scripts\fix_windows.ps1”
Прокрутите страницу в самый низ и нажмите кнопку “Save”.
Как это работает в деталях?
Скрипт срабатывает через 2 секунды после закрытия сессии Moonlight (когда виртуальный экран уже отключился). Скрипт ищет окна программ только из белого списка ($AppList), поэтому, например, приложения, запущенные на внешних мониторах, которые Вы не добавили в скрипт, полностью в безопасности, и их окна не смещаются и не теряются, как при работе в удаленной сессии, так и при её завершении. Программы, оставшиеся в “пустоте” от виртуального дисплея, переносятся на основной экран в удобном крупном оконном разрешении и выстраиваются аккуратной лесенкой для быстрого переключения.
Конечно, описанный мной способ не идеальный в его реализации, но он совершенно рабочий. Рекомендую!