# hproxy, HProxy's command-line tool 0.2.8: https://hproxy.com/tools/ai # Usage: irm https://hproxy.com/install.ps1 | iex # Puts one file, hproxy.exe, in %LOCALAPPDATA%\hproxy (or $env:HPROXY_INSTALL_DIR) after checking # its SHA-256, and adds that folder to your PATH (not with $env:HPROXY_NO_PATH set). # Everything runs inside one block: this window keeps its settings, and gains only the PATH. & { $ErrorActionPreference = 'Stop' $ProgressPreference = 'SilentlyContinue' $version = '0.2.8' $base = 'https://hproxy.com/downloads/cli/0.2.8' # Adds $dir to the user's PATH in the registry, stored the way it was stored (%USERPROFILE% # stays %USERPROFILE%), unless it is there already. True when it was added. function Add-ToUserPath([string] $dir, [string] $subkey) { $key = [Microsoft.Win32.Registry]::CurrentUser.CreateSubKey($subkey) try { $raw = [string] $key.GetValue('Path', '', [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames) foreach ($entry in $raw -split ';') { if ($entry -and [Environment]::ExpandEnvironmentVariables($entry).TrimEnd('\') -eq $dir.TrimEnd('\')) { return $false } } $kind = if ($key.GetValueNames() -contains 'Path') { $key.GetValueKind('Path') } else { [Microsoft.Win32.RegistryValueKind]::ExpandString } $rest = $raw.TrimEnd(';') $key.SetValue('Path', $(if ($rest) { $rest + ';' + $dir } else { $dir }), $kind) return $true } finally { $key.Close() } } # Tells Explorer the environment changed, so a terminal opened from now on finds hproxy. function Send-EnvironmentChange { if (-not ('HProxyInstall.Native' -as [type])) { Add-Type -Namespace HProxyInstall -Name Native -MemberDefinition '[DllImport("user32.dll", CharSet = CharSet.Unicode)] public static extern IntPtr SendMessageTimeout(IntPtr hWnd, uint msg, UIntPtr wParam, string lParam, uint flags, uint timeout, out UIntPtr result);' } $result = [UIntPtr]::Zero [void] [HProxyInstall.Native]::SendMessageTimeout([IntPtr] 0xffff, 0x1a, [UIntPtr]::Zero, 'Environment', 2, 1000, [ref] $result) } # The system's processor: an x64 PowerShell on an ARM PC still gets the native file. $machine = $null try { $machine = [string] [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture } catch { } if (-not $machine) { $machine = if ($env:PROCESSOR_ARCHITEW6432) { $env:PROCESSOR_ARCHITEW6432 } else { $env:PROCESSOR_ARCHITECTURE } } $file = $null $sum = $null switch -Regex ($machine) { '^(X64|AMD64)$' { $file = 'hproxy-cli-windows-x86_64.exe'; $sum = '3c8b574b63e56d5dae200180570598d7dd12be460541693f21a8c0f03245ce4a' } '^ARM64$' { $file = 'hproxy-cli-windows-arm64.exe'; $sum = 'cfe0b7817d51c2f71379709173051d3031d9fcc72c1e52603425842eba688480' } default { throw "hproxy: there is no build for Windows on $machine yet." } } $dir = if ($env:HPROXY_INSTALL_DIR) { $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($env:HPROXY_INSTALL_DIR) } else { Join-Path $env:LOCALAPPDATA 'hproxy' } # PATH separates folders with ';': a folder name holding one would add a second folder to it. if ($dir.Contains(';')) { throw "hproxy: the install folder $dir holds a ';'. Choose another." } New-Item -ItemType Directory -Force -Path $dir | Out-Null $exe = Join-Path $dir 'hproxy.exe' $tmp = Join-Path $dir ('hproxy-download-' + [guid]::NewGuid().ToString('N') + '.exe') try { Write-Host "Downloading hproxy $version ($file)..." Invoke-WebRequest -UseBasicParsing -Uri "$base/$file" -OutFile $tmp if ((Get-FileHash -Algorithm SHA256 -LiteralPath $tmp).Hash -ne $sum) { throw 'hproxy: the download does not match its SHA-256. Nothing was installed.' } if (Test-Path -LiteralPath $exe) { Move-Item -Force -LiteralPath $exe -Destination (Join-Path $dir ('hproxy-old-' + [guid]::NewGuid().ToString('N') + '.exe')) } Move-Item -LiteralPath $tmp -Destination $exe } finally { if (Test-Path -LiteralPath $tmp) { Remove-Item -Force -LiteralPath $tmp } } # An old copy still running stays until the next install. Get-ChildItem -LiteralPath $dir -Filter 'hproxy-old-*.exe' | Remove-Item -Force -ErrorAction SilentlyContinue Write-Host "Installed: $exe" if ($env:HPROXY_NO_PATH) { Write-Host 'Your PATH was left as it was (HPROXY_NO_PATH).' } else { try { if (Add-ToUserPath $dir 'Environment') { # Without the message Explorer learns of it at the next sign-in; the PATH is written either way. try { Send-EnvironmentChange } catch { } Write-Host "Added $dir to your PATH." } } catch { Write-Host "Could not add $dir to your PATH ($($_.Exception.Message)). Run hproxy as $exe, or add the folder by hand." } # This window too, so hproxy works here right away. if (-not (($env:Path -split ';') | Where-Object { $_.TrimEnd('\') -eq $dir.TrimEnd('\') })) { $env:Path = $env:Path.TrimEnd(';') + ';' + $dir } } & $exe --version Write-Host 'Try: hproxy list --country DE --protocol socks5 --limit 5' }