How Shadowsocks-Windows Handles System Power Suspend/Resume
Shadowsocks-Windows registers a SystemEvents.PowerModeChanged handler in Program.cs that calls MainController.Stop() on suspend and MainController.Start(true) on resume, waiting 10 seconds after wake-up to allow network adapters to re-initialize before restarting the proxy services.
The shadowsocks/shadowsocks-windows client must gracefully handle Windows sleep cycles to prevent network leaks and ensure seamless reconnection. The application hooks into the operating system's power management events to coordinate the shutdown and recovery of its core ShadowsocksController.
How Power State Changes Are Detected
The application subscribes to Microsoft.Win32.SystemEvents.PowerModeChanged during initialization in Program.cs. This Windows Forms event provides real-time notifications whenever the OS enters or exits a low-power state.
// Registration in Program.cs (lines 110-119)
SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
The handler SystemEvents_PowerModeChanged inspects the PowerModeChangedEventArgs.Mode property to determine whether the system is suspending or resuming.
Suspend Handling: Graceful Shutdown
When PowerModes.Suspend is detected, the application immediately shuts down all active networking components to ensure the OS can safely enter sleep mode without holding open sockets or proxy connections.
The MainController.Stop() method performs the following actions:
- Closes the local SOCKS5 listener
- Shuts down the PAC daemon and PAC server
- Releases all active proxy processes and sockets
- Logs "controller stopped" and "os suspend" for diagnostics
This cleanup is critical because Windows may freeze or terminate applications that hold network resources during sleep transitions.
Key Code Path for Suspend
In Program.cs (lines 110-117 and 210-217), the suspend branch executes:
case PowerModes.Suspend:
// cleanly stop everything before the OS sleeps
MainController.Stop();
break;
Resume Handling: Controller Recovery
On PowerModes.Resume, the application does not immediately restart. Instead, it spawns a background task that waits 10 seconds before invoking MainController.Start(true). This delay allows Windows to re-initialize network adapters and restore connectivity.
case PowerModes.Resume:
// give the OS a moment, then restart the controller
Task.Factory.StartNew(() =>
{
Thread.Sleep(10_000);
MainController.Start(true);
});
break;
The systemWakeUp Flag
The ShadowsocksController.Start(bool systemWakeUp) method (implemented in ShadowsocksController.cs, lines 14-30) uses the boolean parameter to distinguish between a cold start and a wake-from-suspend recovery:
- Configuration reload:
Reload()refreshes settings and restarts the listener and PAC services - System proxy re-application: The controller reapplies the system proxy settings to restore traffic redirection
- Skip first-run logic: When
systemWakeUpistrue, the method skips version-upgrade checks (_config.firstRunOnNewVersion), allowing immediate recovery
public void Start(bool systemWakeUp = false)
{
// skip first‑run upgrade when waking from suspend
if (_config.firstRunOnNewVersion && !systemWakeUp) { /* upgrade logic */ }
Reload(); // re‑load config, listeners, PAC, etc.
if (!systemWakeUp) // normal launch: register hotkeys
HotkeyReg.RegAllHotkeys();
}
Key Implementation Details
| Component | File Path | Role in Power Management |
|---|---|---|
| Power Event Registration | shadowsocks-csharp/Program.cs (lines 110-119) |
Subscribes to SystemEvents.PowerModeChanged |
| Suspend Logic | shadowsocks-csharp/Program.cs (lines 110-117) |
Calls MainController.Stop() to release resources |
| Resume Logic | shadowsocks-csharp/Program.cs (lines 188-202) |
Delays 10s then calls MainController.Start(true) |
| Controller Start | Controller/ShadowsocksController.cs (lines 14-30) |
Implements Start(bool systemWakeUp) with wake-up detection |
| Controller Stop | Controller/ShadowsocksController.cs |
Implements Stop() to close listeners and PAC daemon |
| PAC Services | Controller/Service/PACDaemon.cs / PACServer.cs |
Restarted by controller during resume recovery |
Summary
- Shadowsocks-Windows hooks
SystemEvents.PowerModeChangedinProgram.csto monitor power state transitions. - During suspend,
MainController.Stop()cleanly terminates the proxy listener, PAC daemon, and network sockets to prepare for sleep. - During resume, a background task waits 10 seconds for network adapter initialization, then calls
MainController.Start(true). - The
systemWakeUpflag inShadowsocksController.Start()skips first-run upgrade checks and triggers rapid recovery of proxy services and system proxy settings.
Frequently Asked Questions
Why does Shadowsocks-Windows wait 10 seconds after resume?
The 10-second delay in Program.cs (lines 188-202) allows Windows to complete network adapter re-initialization and restore connectivity. Starting the proxy controller too early results in binding failures or unreachable remote servers because the network stack is not yet fully operational during the early seconds of wake-up.
What components are stopped during system suspend?
According to the source code in ShadowsocksController.Stop(), the application shuts down the local SOCKS5 listener, the PAC daemon, the PAC server, and any active proxy processes. This releases all sockets and allows the OS to enter low-power states safely without hanging on open network connections.
What is the purpose of the systemWakeUp flag in Start()?
The systemWakeUp parameter in ShadowsocksController.Start(bool systemWakeUp) signals that the application is recovering from suspend rather than launching fresh. When true, the method skips version-upgrade prompts and first-run configuration dialogs, immediately reloading settings and restarting services to minimize downtime.
Where is the power event handler registered?
The handler is registered in shadowsocks-csharp/Program.cs at lines 110-119 using SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;. This single subscription captures both suspend and resume events, routing them to the appropriate controller methods based on the PowerModes enum value.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →