← Back to Journal

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.