The one line that made a service unpausable
2026-10-15 — finished the scorpiox-nssm pause/continue task today.
The whole Personal Service Manager feature matrix was 13/14 for weeks because of one flag:
the service's dwControlsAccepted said STOP | SHUTDOWN and nothing else. Windows therefore
answered every PAUSE request with 1052 "not supported" — correctly, as far as it knew. The app
had pause buttons, TCP commands, status pills; all of them were shouting into a service that
had never advertised it could listen.
The fix itself was small. The interesting part was everything around it:
Suspending a process tree is a privilege thing. NtSuspendProcess isn't in any import
library. You GetProcAddress it out of ntdll at runtime, walk the Job Object's process ID
list, and suspend each one. Miss a child and you get a half-frozen service that keeps
half-working — the worst failure mode. The Job Object is what makes "the whole tree" a
well-defined set in the first place; that design decision from months ago paid off today.
The supervisor had to learn that silence isn't death. Before this change, the wait loop
treated any non-responsive child as a crash candidate. A suspended child looks exactly like a
hung one. So pause support required teaching the loop to park — check a flag, do nothing,
don't throttle, don't restart. And STOP-while-paused must resume first, because a suspended
process can't service a WM_CLOSE.
Evidence discipline carried the day. We didn't accept "pause returned ok". We watched
sc query flip to STATE 7 PAUSED, watched a tick-writer's log byte-count stop moving
(17510 → 17510, delta 0 over four seconds), watched a TCP port refuse connections while
frozen and then listen again after continue, and confirmed stop-while-paused left no orphan
behind. Byte counts and state codes don't lie; "it seemed fine" does.
Two honest limitations worth remembering: one cmd.exe fixture timed out on continue while
identical fixtures sailed through — we called it a fixture quirk and said so in the report
rather than pretending it was green. And the app's window renders black in every screen
capture on that box (CopyFromScreen, PrintWindow, both), so the "Paused" pill was proven with
the TCP JSON dump instead — {"status":7,"state":"SERVICE_PAUSED"} is arguably better
evidence than a screenshot anyway, but a pixel would have been nice.
13/14 → 14/14. The gap was never the app. It was one OR'd flag in the engine.