Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions cmake/packaging/sunshine.iss.in
Original file line number Diff line number Diff line change
Expand Up @@ -208,11 +208,13 @@ Source: "{#MySourceDir}\tools\SetDpi.exe"; DestDir: "{app}\tools"; Flags: ignore
Source: "{#MySourceDir}\tools\setreg.exe"; DestDir: "{app}\tools"; Flags: ignoreversion; Components: assets

; Autostart script
Source: "{#MySourceDir}\scripts\autostart-service.bat"; DestDir: "{app}\scripts"; Flags: ignoreversion; Components: autostart
; Always copy so users can run it manually as admin if they unchecked autostart.
Source: "{#MySourceDir}\scripts\autostart-service.bat"; DestDir: "{app}\scripts"; Flags: ignoreversion

; Firewall scripts
Source: "{#MySourceDir}\scripts\add-firewall-rule.bat"; DestDir: "{app}\scripts"; Flags: ignoreversion; Components: firewall
Source: "{#MySourceDir}\scripts\delete-firewall-rule.bat"; DestDir: "{app}\scripts"; Flags: ignoreversion; Components: firewall
; Always copy so users can run them manually as admin if they unchecked firewall.
Source: "{#MySourceDir}\scripts\add-firewall-rule.bat"; DestDir: "{app}\scripts"; Flags: ignoreversion
Source: "{#MySourceDir}\scripts\delete-firewall-rule.bat"; DestDir: "{app}\scripts"; Flags: ignoreversion

; Virtual Display Driver scripts & files
Source: "{#MySourceDir}\scripts\install-vdd.bat"; DestDir: "{app}\scripts"; Flags: ignoreversion
Expand All @@ -228,6 +230,12 @@ Source: "{#MySourceDir}\scripts\vmouse\install-vmouse.bat"; DestDir: "{app}\scri
Source: "{#MySourceDir}\scripts\vmouse\uninstall-vmouse.bat"; DestDir: "{app}\scripts\vmouse"; Flags: ignoreversion
Source: "{#MySourceDir}\scripts\vmouse\driver\*"; DestDir: "{app}\scripts\vmouse\driver"; Flags: ignoreversion recursesubdirs

; Virtual audio sink (microphone / VB-Cable) scripts
; Always shipped — there is no install-time component for this; the user can
; right-click + "Run as administrator" on install-vsink.bat to enable it.
Source: "{#MySourceDir}\scripts\install-vsink.bat"; DestDir: "{app}\scripts"; Flags: ignoreversion skipifsourcedoesntexist
Source: "{#MySourceDir}\scripts\uninstall-vsink.bat"; DestDir: "{app}\scripts"; Flags: ignoreversion skipifsourcedoesntexist

; Optional diagnostic tools
Source: "{#MySourceDir}\tools\dxgi-info.exe"; DestDir: "{app}\tools"; Flags: ignoreversion; Components: tools
Source: "{#MySourceDir}\tools\audio-info.exe"; DestDir: "{app}\tools"; Flags: ignoreversion; Components: tools
Expand Down Expand Up @@ -269,9 +277,12 @@ Filename: "https://docs.qq.com/aio/DSGdQc3htbFJjSFdO?p=DXpTjzl2kZwBjN7jlRMkRJ";
Filename: "{win}\explorer.exe"; Parameters: """{app}\assets\gui\{#MyAppGUIExeName}"""; Description: "{cm:FinishLaunchGUI}"; Flags: postinstall nowait skipifsilent

[UninstallRun]
; Stop running processes first
; Stop running processes first. We force-kill the service binary
; (sunshinesvc.exe) here as well, otherwise `sc stop` later may block waiting
; on its stop handler — which is the typical "uninstaller appears frozen" symptom.
Filename: "taskkill"; Parameters: "/f /im sunshine-gui.exe"; Flags: runhidden; RunOnceId: "KillGUI"
Filename: "taskkill"; Parameters: "/f /im sunshine.exe"; Flags: runhidden; RunOnceId: "KillSunshine"
Filename: "taskkill"; Parameters: "/f /im sunshinesvc.exe"; Flags: runhidden; RunOnceId: "KillSunshineSvc"
; Remove system components
Filename: "{app}\scripts\delete-firewall-rule.bat"; Flags: runhidden waituntilterminated; RunOnceId: "DelFirewall"
Filename: "{app}\scripts\uninstall-service.bat"; Flags: runhidden waituntilterminated; RunOnceId: "UninstallService"
Expand Down
12 changes: 7 additions & 5 deletions src_assets/windows/misc/gamepad/install-gamepad.bat
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,10 @@ for /f "tokens=3" %%a in ('reg query "HKCU\Software\Microsoft\Windows\CurrentVer
rem get browser_download_url from asset 0 of https://api.github.com/repos/nefarius/vigembus/releases/latest
set latest_release_url=https://api.github.com/repos/nefarius/vigembus/releases/latest

rem Use curl to get the api response, and find the browser_download_url
for /F "tokens=* USEBACKQ" %%F in (`curl -s !proxy! -L %latest_release_url% ^| findstr browser_download_url`) do (
rem Use curl to get the api response, and find the browser_download_url.
rem `--connect-timeout 10 --max-time 20` ensures we don't hang for minutes if
rem GitHub or the local network is unreachable during install.
for /F "tokens=* USEBACKQ" %%F in (`curl -s --connect-timeout 10 --max-time 20 !proxy! -L %latest_release_url% ^| findstr browser_download_url`) do (
set browser_download_url=%%F
)

Expand All @@ -66,12 +68,12 @@ if "%browser_download_url%"=="" (

echo %browser_download_url%

rem Download the exe
rem Download the exe (with connect/transfer timeout to avoid install hang)
set "installer=%temp_dir%\virtual_gamepad.exe"
curl -f -s -L !proxy! -o "%installer%" "%browser_download_url%"
curl -f -s -L --connect-timeout 10 --max-time 120 !proxy! -o "%installer%" "%browser_download_url%"
if errorlevel 1 (
echo Direct download failed, trying mirror...
curl -f -s -L !proxy! -o "%installer%" "https://mirror.ghproxy.com/%browser_download_url%"
curl -f -s -L --connect-timeout 10 --max-time 120 !proxy! -o "%installer%" "https://mirror.ghproxy.com/%browser_download_url%"
)

if not exist "%installer%" (
Expand Down
147 changes: 42 additions & 105 deletions src_assets/windows/misc/path/update-path.bat
Original file line number Diff line number Diff line change
@@ -1,112 +1,49 @@
@echo off
rem ============================================================================
rem update-path.bat <add|remove>
rem
rem Adds or removes the Sunshine install directory and its `tools\` subdir to
rem the *machine* PATH (HKLM Session Manager Environment).
rem
rem Implementation note: previous pure-batch versions parsed PATH with delayed
rem expansion + `for` substring tricks. That is O(n^2) on PATH length and
rem brittle on PATHs containing `!`, `&` or unbalanced quotes — large machines
rem hit this and the Inno uninstaller appeared to hang. This version offloads
rem the parsing to PowerShell and writes the registry directly via
rem Set-ItemProperty (REG_EXPAND_SZ, no WM_SETTINGCHANGE broadcast — so no
rem 5-second SendMessageTimeout wait per top-level window).
rem ============================================================================
set "PATH=%SystemRoot%\System32;%SystemRoot%;%SystemRoot%\System32\Wbem;%SystemRoot%\System32\WindowsPowerShell\v1.0"
setlocal EnableDelayedExpansion
setlocal

rem Check if parameter is provided
if "%~1"=="" (
echo Usage: %0 [add^|remove]
echo add - Adds Sunshine directories to system PATH
echo remove - Removes Sunshine directories from system PATH
echo Usage: %~nx0 [add^|remove]
exit /b 1
)

rem Get sunshine root directory
for %%I in ("%~dp0\..") do set "ROOT_DIR=%%~fI"
echo Sunshine root directory: !ROOT_DIR!

rem Define directories to add to path
set "PATHS_TO_MANAGE[0]=!ROOT_DIR!"
set "PATHS_TO_MANAGE[1]=!ROOT_DIR!\tools"

rem System path registry location
set "KEY_NAME=HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment"
set "VALUE_NAME=Path"

rem Get the current path
for /f "tokens=2*" %%A in ('reg query "%KEY_NAME%" /v "%VALUE_NAME%"') do set "CURRENT_PATH=%%B"
echo Current path: !CURRENT_PATH!

rem Check if adding to path
if /i "%~1"=="add" (
set "NEW_PATH=!CURRENT_PATH!"

rem Process each directory to add
for /L %%i in (0,1,1) do (
set "DIR_TO_ADD=!PATHS_TO_MANAGE[%%i]!"

rem Check if path already contains this directory
echo "!CURRENT_PATH!" | findstr /i /c:"!DIR_TO_ADD!" > nul
if !ERRORLEVEL!==0 (
echo !DIR_TO_ADD! already in path
) else (
echo Adding to path: !DIR_TO_ADD!
set "NEW_PATH=!NEW_PATH!;!DIR_TO_ADD!"
)
)

rem Only update if path was changed
if "!NEW_PATH!" neq "!CURRENT_PATH!" (
rem Set the new path in the registry
reg add "%KEY_NAME%" /v "%VALUE_NAME%" /t REG_EXPAND_SZ /d "!NEW_PATH!" /f
if !ERRORLEVEL!==0 (
echo Successfully added Sunshine directories to PATH
) else (
echo Failed to add Sunshine directories to PATH
)
) else (
echo No changes needed to PATH
)
exit /b !ERRORLEVEL!
)

rem Check if removing from path
if /i "%~1"=="remove" (
set "CHANGES_MADE=0"

rem Process each directory to remove
for /L %%i in (0,1,1) do (
set "DIR_TO_REMOVE=!PATHS_TO_MANAGE[%%i]!"

rem Check if path contains this directory
echo "!CURRENT_PATH!" | findstr /i /c:"!DIR_TO_REMOVE!" > nul
if !ERRORLEVEL!==0 (
echo Removing from path: !DIR_TO_REMOVE!

rem Build a new path by parsing and filtering the current path
set "NEW_PATH="
for %%p in ("!CURRENT_PATH:;=" "!") do (
set "PART=%%~p"
if /i "!PART!" NEQ "!DIR_TO_REMOVE!" (
if defined NEW_PATH (
set "NEW_PATH=!NEW_PATH!;!PART!"
) else (
set "NEW_PATH=!PART!"
)
)
)

set "CURRENT_PATH=!NEW_PATH!"
set "CHANGES_MADE=1"
) else (
echo !DIR_TO_REMOVE! not found in path
)
)

rem Only update if path was changed
if "!CHANGES_MADE!"=="1" (
rem Set the new path in the registry
reg add "%KEY_NAME%" /v "%VALUE_NAME%" /t REG_EXPAND_SZ /d "!CURRENT_PATH!" /f
if !ERRORLEVEL!==0 (
echo Successfully removed Sunshine directories from PATH
) else (
echo Failed to remove Sunshine directories from PATH
)
) else (
echo No changes needed to PATH
)
exit /b !ERRORLEVEL!
)

echo Unknown parameter: %~1
echo Usage: %0 [add^|remove]
exit /b 1
set "SUNSHINE_PATH_ACTION=%~1"
for %%I in ("%~dp0\..") do set "SUNSHINE_PATH_ROOT=%%~fI"

powershell -NoProfile -ExecutionPolicy Bypass -Command ^
"$ErrorActionPreference = 'Stop';" ^
"$action = $env:SUNSHINE_PATH_ACTION;" ^
"$root = $env:SUNSHINE_PATH_ROOT;" ^
"$tools = Join-Path $root 'tools';" ^
"$key = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment';" ^
"$cur = (Get-ItemProperty -Path $key -Name Path -ErrorAction Stop).Path;" ^
"$parts = @($cur -split ';' | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne '' });" ^
"$targets = @($root, $tools);" ^
"switch ($action.ToLower()) {" ^
" 'add' { foreach ($t in $targets) { if (-not ($parts -icontains $t)) { $parts += $t } } }" ^
" 'remove' { $parts = @($parts | Where-Object { -not ($targets -icontains $_) }) }" ^
" default { Write-Host (\"Unknown action: \" + $action); exit 2 }" ^
"}" ^
"$new = ($parts -join ';');" ^
"if ($new -ne $cur) {" ^
" Set-ItemProperty -Path $key -Name Path -Value $new -Type ExpandString;" ^
" Write-Host 'PATH updated.';" ^
"} else {" ^
" Write-Host 'PATH unchanged.';" ^
"}"

exit /b %ERRORLEVEL%
21 changes: 15 additions & 6 deletions src_assets/windows/misc/service/uninstall-service.bat
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,19 @@ if %ERRORLEVEL%==0 (
echo !CONTENT!> "%SERVICE_CONFIG_FILE%"
)

rem Stop and delete the legacy SunshineSvc service
net stop sunshinesvc
sc delete sunshinesvc
rem Stop and delete the legacy SunshineSvc service (non-blocking)
sc stop sunshinesvc >nul 2>&1
sc delete sunshinesvc >nul 2>&1

rem Stop and delete the new SunshineService service
net stop SunshineService
sc delete SunshineService
rem Force-kill the service binary FIRST so SCM can transition the service to
rem STOPPED quickly. We deliberately avoid `net stop`, which blocks for up to
rem 30 seconds waiting on the service's stop handler — that is the typical
rem cause of "Inno uninstaller appears frozen".
taskkill /f /im sunshinesvc.exe >nul 2>&1

rem Issue a stop control as a courtesy (idempotent, returns quickly), then
rem delete. If still in stop-pending, SCM marks the service for deletion and
rem removes it once stopped — which is immediate after the taskkill above.
sc stop SunshineService >nul 2>&1
sc delete SunshineService >nul 2>&1
exit /b 0
32 changes: 27 additions & 5 deletions src_assets/windows/misc/vdd/uninstall-vdd.bat
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
@echo off
set "PATH=%SystemRoot%\System32;%SystemRoot%;%SystemRoot%\System32\Wbem;%SystemRoot%\System32\WindowsPowerShell\v1.0"
chcp 65001 >nul

rem Get sunshine root directory
for %%I in ("%~dp0\..") do set "ROOT_DIR=%%~fI"
Expand All @@ -9,12 +10,33 @@ set "DIST_DIR=%ROOT_DIR%\tools\vdd"
set "NEFCON=%ROOT_DIR%\tools\nefconw.exe"
if not exist "%NEFCON%" set "NEFCON=%DIST_DIR%\nefconw.exe"
if not exist "%NEFCON%" (
echo WARNING: nefconw.exe not found, skipping device node removal.
echo WARNING: nefconw.exe not found, skipping driver/device removal.
goto :cleanup
)
if exist "%DIST_DIR%" (
"%NEFCON%" --remove-device-node --hardware-id ROOT\ZakoVDD --class-guid 4d36e968-e325-11ce-bfc1-08002be10318

rem 1) Remove device node(s) first so the driver is no longer in use
echo Removing VDD device node...
"%NEFCON%" --remove-device-node --hardware-id Root\ZakoVDD --class-guid 4d36e968-e325-11ce-bfc1-08002be10318

rem Brief wait so the kernel finishes releasing the device handle before we
rem attempt to remove the driver package from the DriverStore.
timeout /t 1 /nobreak >nul 2>&1

rem 2) Uninstall the driver package from the DriverStore (requires INF path)
if exist "%DIST_DIR%\ZakoVDD.inf" (
echo Uninstalling VDD driver package...
"%NEFCON%" --uninstall-driver --inf-path "%DIST_DIR%\ZakoVDD.inf"
) else (
echo WARNING: ZakoVDD.inf not found in "%DIST_DIR%", skipping driver package uninstall.
)

rem 3) Best-effort second pass in case multiple device instances remain
"%NEFCON%" --remove-device-node --hardware-id Root\ZakoVDD --class-guid 4d36e968-e325-11ce-bfc1-08002be10318 >nul 2>&1

:cleanup
reg delete "HKLM\SOFTWARE\ZakoTech" /f
rmdir /S /Q "%DIST_DIR%"
echo Cleaning registry...
reg delete "HKLM\SOFTWARE\ZakoTech" /f 2>nul
if exist "%DIST_DIR%" (
rmdir /S /Q "%DIST_DIR%"
)
echo VDD uninstall completed.
33 changes: 23 additions & 10 deletions src_assets/windows/misc/vmouse/install-vmouse.bat
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,22 @@ rem ============================================================================
rem Stop Sunshine service to release HID device handle
rem ============================================================================

rem Use sc + taskkill (non-blocking) instead of `net stop`, which can block
rem for up to 30 seconds on a stuck stop handler.
echo Stopping Sunshine service...
set "SERVICE_WAS_RUNNING=0"
net stop SunshineService >nul 2>&1
sc query SunshineService >nul 2>&1
if not errorlevel 1 (
set "SERVICE_WAS_RUNNING=1"
sc query SunshineService | find /I "RUNNING" >nul 2>&1
if not errorlevel 1 (
set "SERVICE_WAS_RUNNING=1"
sc stop SunshineService >nul 2>&1
)
taskkill /f /im sunshinesvc.exe >nul 2>&1
timeout /t 1 /nobreak >nul 2>&1
echo Sunshine service stopped.
timeout /t 2 /nobreak 1>nul
) else (
echo Sunshine service not running, OK.
echo Sunshine service not installed, OK.
)

rem ============================================================================
Expand All @@ -70,16 +77,24 @@ rem ============================================================================

echo Cleaning up existing Virtual Mouse driver...

rem Remove ALL existing device nodes (loop until none remain)
rem Remove ALL existing device nodes (loop until none remain).
rem Hard cap at 20 iterations to prevent an infinite loop if nefcon reports
rem success without actually removing anything (observed on some builds).
set "CLEANUP_COUNT=0"
set "CLEANUP_MAX=20"
:remove_loop
"%NEFCON%" --remove-device-node --hardware-id Root\ZakoVirtualMouse --class-guid 745a17a0-74d3-11d0-b6fe-00a0c90f57da
if not errorlevel 1 (
set /a CLEANUP_COUNT+=1
if !CLEANUP_COUNT! GEQ !CLEANUP_MAX! (
echo Reached max remove iterations (!CLEANUP_MAX!), stopping.
goto after_remove_loop
)
echo Removed a device node, checking for more...
timeout /t 1 /nobreak >nul
goto remove_loop
)
:after_remove_loop
echo Removed !CLEANUP_COUNT! device node(s) via nefcon.

rem Fallback: use pnputil to remove any remaining device instances
Expand Down Expand Up @@ -144,12 +159,10 @@ rem Restart Sunshine service if it was running before
rem ============================================================================

if "!SERVICE_WAS_RUNNING!"=="1" (
echo Restarting Sunshine service...
net start SunshineService >nul 2>&1
sc query SunshineService >nul 2>&1
if not errorlevel 1 (
echo Sunshine service restarted.
) else (
echo WARNING: Could not restart Sunshine service.
echo Restarting Sunshine service...
sc start SunshineService >nul 2>&1
)
)

Expand Down
Loading
Loading