Skip to content

Windows: every supervised module gets a console window; closing it kills the module (0xC000013A) #131

Description

@Qiiks

Upstream creates the module's console here, not at the daemon's own launch.

Two independent reasons this window exists, and only one of them was mine:

  1. Upstream gap. spawn_child_in_slot (crates/subc-daemon/src/supervise.rs:5620-5650)
    builds the module Command with piped stdout/stderr and stdin(null), but no
    creation_flags on Windows. A console-subsystem child launched without
    CREATE_NO_WINDOW gets a fresh console, so every supervised module (ck-aft,
    the Synapse module) opens one. Closing that window sends CTRL_C_EVENT, which
    the child reports as 0xC000013A STATUS_CONTROL_C_EXIT — so a user who just
    wants to tidy their desktop kills a healthy module and the supervisor restarts
    it. This is why CREATE_NO_WINDOW exists in setup/uninstall.rs upstream but
    never in supervise.rs.

  2. My own regression, in the job-object work. set_suspended_creation_flags
    called command.creation_flags(CREATE_SUSPENDED_FLAG), and
    std::process::Command::creation_flags replaces the whole mask rather than
    OR-ing into it. So a second CREATE_NO_WINDOW call added later would have
    silently dropped CREATE_SUSPENDED — leaving the child both uncontained and
    unresumable. Both flags now go in as one OR'd mask
    (CONTAINMENT_CREATION_FLAGS, crates/subc-jobobject/src/sys.rs).

The fix is one flag at the module spawn site, not at the daemon's own launch
site
— the daemon is already launched hidden by the OMP extension
(windowsHide: true), and CREATE_NO_WINDOW is inert for the daemon itself.

The doc comment on SupervisedChild::job already names the remaining
non-Unix gap in this area (drain-then-exit on Windows), which is the separate
follow-up in #111.

Activity

  1. subc-alfonso commented on Sep 24, 2026

    @subc-alfonso

    Thanks, the diagnosis is right on both counts. The fix went in with #111, since it's the same flags: modules are now spawned with CREATE_SUSPENDED | CREATE_NO_WINDOW as one mask, set in a single creation_flags call, so neither flag can drop the other. A new Windows test spawns a contained child and checks it has no console window. It reaches a running daemon with the next release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions