param( [Parameter(Mandatory = $true)] [string]$InstallerPath, [Parameter(Mandatory = $true)] [string]$ConfigurationPath, [Parameter(Mandatory = $true)] [string]$ErrorPath ) $ErrorActionPreference = 'Stop' $logDirectory = Join-Path $env:ProgramData 'Inventera Client' $LogPath = Join-Path $logDirectory 'rustdesk-install.log' function Write-InstallLog { param([string]$Message) try { if (-not (Test-Path -LiteralPath $logDirectory)) { $null = New-Item -Path $logDirectory -ItemType Directory -Force } Add-Content -LiteralPath $LogPath -Encoding UTF8 -Value ( '{0:yyyy-MM-dd HH:mm:ss} {1}' -f (Get-Date), $Message ) } catch { # A logging failure must not prevent RustDesk installation. } } trap { $message = $_.Exception.Message Write-InstallLog "ОШИБКА: $message" if ($_.ScriptStackTrace) { Write-InstallLog "Стек: $($_.ScriptStackTrace -replace '[\r\n]+', ' ')" } try { [System.IO.File]::WriteAllText( $ErrorPath, "$message Журнал: $LogPath", [System.Text.UTF8Encoding]::new($true) ) } catch { } exit 1 } if (-not (Test-Path -LiteralPath $ConfigurationPath)) { throw "Файл параметров RustDesk не найден: $ConfigurationPath" } $configuration = @{} foreach ($line in Get-Content -LiteralPath $ConfigurationPath) { $separatorIndex = $line.IndexOf('=') if ($separatorIndex -lt 0) { continue } $name = $line.Substring(0, $separatorIndex) $value = $line.Substring($separatorIndex + 1) $configuration[$name] = $value } $Mode = [string]$configuration['Mode'] $IdServer = [string]$configuration['IdServer'] $RelayServer = [string]$configuration['RelayServer'] $Key = [string]$configuration['Key'] $Password = [string]$configuration['Password'] if ($Mode -notin @('automatic', 'manual')) { throw "Неизвестный режим настройки RustDesk: $Mode" } Write-InstallLog '------------------------------------------------------------' Write-InstallLog "Запуск установки RustDesk. Режим: $Mode" function Find-SystemRustDeskExecutable { $candidates = @() foreach ($basePath in @($env:ProgramFiles, ${env:ProgramFiles(x86)})) { if ($basePath) { $candidates += Join-Path $basePath 'RustDesk\rustdesk.exe' } } foreach ($candidate in $candidates) { if ($candidate -and (Test-Path -LiteralPath $candidate)) { return $candidate } } return $null } function Stop-RustDeskDesktopProcesses { $serviceProcessId = 0 try { $serviceInstance = Get-CimInstance ` -ClassName Win32_Service ` -Filter "Name = 'RustDesk'" ` -ErrorAction SilentlyContinue if ($serviceInstance) { $serviceProcessId = [int]$serviceInstance.ProcessId } } catch { Write-InstallLog "Не удалось определить PID службы RustDesk: $($_.Exception.Message)" } try { $rustDeskProcesses = @( Get-CimInstance ` -ClassName Win32_Process ` -Filter "Name = 'rustdesk.exe'" ` -ErrorAction SilentlyContinue ) foreach ($process in $rustDeskProcesses) { if ([int]$process.SessionId -eq 0) { continue } if (($serviceProcessId -gt 0) -and ([int]$process.ProcessId -eq $serviceProcessId)) { continue } $commandLine = [string]$process.CommandLine if ($commandLine -match '(?i)--[^\s"]*install[^\s"]*|--(?:option|password)') { continue } Write-InstallLog "Закрывается пользовательский процесс RustDesk, PID: $($process.ProcessId)." Stop-Process -Id $process.ProcessId -Force -ErrorAction SilentlyContinue } } catch { Write-InstallLog "Не удалось закрыть пользовательское окно RustDesk: $($_.Exception.Message)" } } if (-not (Test-Path -LiteralPath $InstallerPath)) { throw "Установщик RustDesk не найден: $InstallerPath" } Stop-RustDeskDesktopProcesses Write-InstallLog 'Запускается встроенный установщик RustDesk.' $installJob = Start-Job -ScriptBlock { param([string]$Path) $process = Start-Process ` -FilePath $Path ` -ArgumentList '--silent-install' ` -Wait ` -PassThru ` -WindowStyle Hidden [int]$process.ExitCode } -ArgumentList $InstallerPath try { for ($attempt = 0; $attempt -lt 120; $attempt++) { $installJob = Get-Job -Id $installJob.Id if ($installJob.State -ne 'Running') { break } # RustDesk opens its desktop window as a child of the installer. # Start-Process -Wait also waits for that window, so close it here. Stop-RustDeskDesktopProcesses Start-Sleep -Seconds 1 } $installJob = Get-Job -Id $installJob.Id if ($installJob.State -eq 'Running') { throw 'Тихая установка RustDesk не завершилась за 120 секунд.' } if ($installJob.State -ne 'Completed') { $jobError = ($installJob.ChildJobs[0].JobStateInfo.Reason | Out-String).Trim() throw "Процесс установки RustDesk завершился с ошибкой: $jobError" } $installJobOutput = @(Receive-Job -Job $installJob -ErrorAction Stop) if ($installJobOutput.Count -eq 0) { throw 'Установщик RustDesk завершился без кода возврата.' } $installExitCode = [int]$installJobOutput[-1] } finally { if ($installJob) { if ($installJob.State -eq 'Running') { Stop-Job -Job $installJob -ErrorAction SilentlyContinue } Remove-Job -Job $installJob -Force -ErrorAction SilentlyContinue } } Write-InstallLog "Установщик RustDesk завершён с кодом: $installExitCode" if ($installExitCode -ne 0) { throw "Установщик RustDesk завершился с кодом ошибки: $installExitCode." } $rustDeskExecutable = $null for ($attempt = 0; $attempt -lt 60; $attempt++) { $rustDeskExecutable = Find-SystemRustDeskExecutable if ($rustDeskExecutable) { break } Start-Sleep -Seconds 1 } if (-not $rustDeskExecutable) { throw "После установки не найден системный rustdesk.exe в Program Files. Код установщика RustDesk: $installExitCode." } Write-InstallLog "Найден RustDesk: $rustDeskExecutable" function Invoke-RustDeskCommand { param([string[]]$Arguments) $output = (& $rustDeskExecutable @Arguments 2>&1 | Out-String).Trim() $exitCode = if ($null -eq $LASTEXITCODE) { 0 } else { [int]$LASTEXITCODE } return [PSCustomObject]@{ ExitCode = $exitCode Output = $output } } function Set-RustDeskOption { param( [string]$Name, [string]$Value ) $lastSetExitCode = -1 $lastReadExitCode = -1 $lastReadOutput = '' for ($attempt = 1; $attempt -le 5; $attempt++) { Write-InstallLog "Применение параметра '$Name', попытка $attempt." $setResult = Invoke-RustDeskCommand -Arguments @('--option', $Name, $Value) $lastSetExitCode = $setResult.ExitCode Start-Sleep -Seconds 2 $readResult = Invoke-RustDeskCommand -Arguments @('--option', $Name) $lastReadExitCode = $readResult.ExitCode $lastReadOutput = $readResult.Output if (($lastSetExitCode -eq 0) -and ($lastReadExitCode -eq 0) -and ($lastReadOutput.Trim() -eq $Value.Trim())) { Write-InstallLog "Параметр '$Name' успешно применён." return } Start-Sleep -Seconds 2 } $readState = 'значение отличается от введённого' if ([string]::IsNullOrWhiteSpace($lastReadOutput)) { $readState = 'пустое значение' } throw "Параметр RustDesk '$Name' не был применён после 5 попыток. Код записи: $lastSetExitCode; код чтения: $lastReadExitCode; результат проверки: $readState." } function Remove-RustDeskService { $existingService = Get-Service -Name 'RustDesk' -ErrorAction SilentlyContinue if (-not $existingService) { return } if ($existingService.Status -ne 'Stopped') { Stop-Service -Name 'RustDesk' -Force -ErrorAction SilentlyContinue try { $existingService.WaitForStatus('Stopped', [TimeSpan]::FromSeconds(20)) } catch { Write-InstallLog 'Служба RustDesk не остановилась штатно; выполняется её пересоздание.' } } $existingService.Dispose() $null = & sc.exe delete 'RustDesk' 2>&1 for ($attempt = 0; $attempt -lt 30; $attempt++) { if (-not (Get-Service -Name 'RustDesk' -ErrorAction SilentlyContinue)) { return } Start-Sleep -Seconds 1 } throw 'Не удалось удалить некорректную службу RustDesk.' } function Install-RustDeskService { Write-InstallLog 'Устанавливается служба RustDesk.' $installServiceResult = Invoke-RustDeskCommand -Arguments @('--install-service') Write-InstallLog "Команда установки службы завершена с кодом: $($installServiceResult.ExitCode)" for ($attempt = 0; $attempt -lt 30; $attempt++) { $installedService = Get-Service -Name 'RustDesk' -ErrorAction SilentlyContinue if ($installedService) { return $installedService } Start-Sleep -Seconds 1 } throw 'Служба RustDesk не была установлена.' } $serviceInstance = Get-CimInstance ` -ClassName Win32_Service ` -Filter "Name = 'RustDesk'" ` -ErrorAction SilentlyContinue if ($serviceInstance) { $servicePath = [Environment]::ExpandEnvironmentVariables( [string]$serviceInstance.PathName ) if ($servicePath.IndexOf( $rustDeskExecutable, [StringComparison]::OrdinalIgnoreCase ) -lt 0) { Write-InstallLog "Обнаружена служба RustDesk с некорректным путём: $servicePath" Remove-RustDeskService } } $service = Get-Service -Name 'RustDesk' -ErrorAction SilentlyContinue if (-not $service) { $service = Install-RustDeskService } if ($service.Status -ne 'Running') { Write-InstallLog 'Запускается служба RustDesk.' try { Start-Service -Name 'RustDesk' } catch { Write-InstallLog "Первый запуск службы завершился ошибкой: $($_.Exception.Message)" Write-InstallLog 'Служба RustDesk будет пересоздана из системного rustdesk.exe.' $service.Dispose() Remove-RustDeskService $service = Install-RustDeskService Start-Service -Name 'RustDesk' } $service = Get-Service -Name 'RustDesk' $service.WaitForStatus('Running', [TimeSpan]::FromSeconds(30)) } Write-InstallLog 'Служба RustDesk работает.' if ($Mode -eq 'manual') { Set-RustDeskOption -Name 'custom-rendezvous-server' -Value $IdServer Set-RustDeskOption -Name 'relay-server' -Value $RelayServer Set-RustDeskOption -Name 'key' -Value $Key } if ($Password) { $passwordWasSet = $false for ($attempt = 1; $attempt -le 3; $attempt++) { Write-InstallLog "Задание фиксированного пароля, попытка $attempt." $passwordResult = Invoke-RustDeskCommand -Arguments @('--password', $Password) if (($passwordResult.ExitCode -eq 0) -and ($passwordResult.Output -match 'Done!')) { $passwordWasSet = $true break } Start-Sleep -Seconds 2 } if (-not $passwordWasSet) { throw "Не удалось задать фиксированный пароль RustDesk после 3 попыток. Код: $($passwordResult.ExitCode)." } Write-InstallLog 'Фиксированный пароль успешно задан.' } Write-InstallLog 'Перезапускается служба RustDesk для применения настроек.' Restart-Service -Name 'RustDesk' -Force $service = Get-Service -Name 'RustDesk' $service.WaitForStatus('Running', [TimeSpan]::FromSeconds(30)) Write-InstallLog 'Установка и настройка RustDesk успешно завершены.'