Skip to content

Releases

V9x Remote Agent v0.17.0

Choose a tag to compare

@github-actions github-actions released this 12 Nov 10:32

Everything since v0.15.0, newest first.

0.17.0 (2026-11-12)

Remote-help operator-friction improvements (2026-11-12).

Added

  • wait-desktop -DismissLogon: wait inside the guest, optionally cancel an
    English Windows 9x startup logon dialog, and report LogonDismissed.
    No credentials, provider changes or password-list changes. Unknown dialogs
    and NT logons are left alone. Cancellation is recorded in the activity log.
  • reboot / reset -DismissLogon: after reconnect proof, cancel the logon
    and wait for Explorer on the same connection. -WaitSeconds bounds each
    phase separately. Unsupported agents are refused before power control.
    MCP: dismiss_logon on the desktop wait and reboot/reset tools.
  • Protocol: DESKTOP_WAIT_REQUEST / response 0x0065 / 0x8065,
    capability V9X_CAP_LOGON_DISMISS 0x02000000 (Win9x only). One request
    waits at most 60 s, like PROCESS_WAIT, so a caller that drops mid-wait
    cannot hold a connection or relay session; CLI and MCP repeat it up to
    -WaitSeconds.
  • Positional guest paths for list, cat, stat, get and hash, including
    their aliases. Named paths remain supported; duplicate paths are rejected.
  • reg -Set -Key <key> -Name <value> -String <text> | -Dword <n> and
    reg -DeleteValue: one value per operation, with the old value returned
    and recorded in the activity log. Hive roots, HKLM system/security/hardware
    branches, HKCC/HKDD and the agent's startup entries are protected. No key
    tree deletion. New/current values are limited to 1024 bytes. MCP:
    v9x_reg_write (set_string, set_dword, delete_value).
  • display [-Restore]: current versus registry mode; restore calls
    ChangeDisplaySettings(NULL, 0). kill -RestoreDisplay waits for the
    process to exit before restoring. MCP: v9x_display, restore_display
    on v9x_kill. If the exit wait or restore fails, the completed kill is
    still reported (CLI: kill result plus Display.Error, exit 51).
  • input -Window <title>: select one window by literal, case-insensitive
    title substring, foreground it and verify focus before sending input.
    When the foreground lock refuses the switch, the agent retries once with
    its thread attached to the current foreground input queue.
    Missing/ambiguous/unfocusable targets are refused; losing focus stops
    remaining actions and releases keys/buttons pressed by that batch.
    New input replies include ForegroundBefore, KeyboardWindow (last
    key press/typed character) and ForegroundAfter, with HWND, PID and title. MCP
    input tools use window, also used for an optional screenshot crop.
  • Guest-side PROCESS_WAIT uses a real process handle in bounded chunks
    of at most 60 seconds, reducing repeated relay process-list requests.
    CLI/MCP use it for a PID or one exact-name match; wildcard/multiple-name
    waits and older agents retain the existing process-list path.

Changed

  • set-autologon.ps1 forwards -Relay / -RelayServer to every operation.
    Its -Dismiss now uses cancellation through wait-desktop -DismissLogon
    (requires 0.17.0+), without registry export or a foreground Enter key.
    -Enable retains its explicit blank-password logon behavior.
  • Relay reconnects now use the wait's remaining budget instead of opening
    every session with a minimum 150-second allowance. The relay's open
    deadline includes the initial HTTP request. Guest-side waits allow
    transport overhead separately, including the measured slow relay path.
  • Discovery's host fixture waits for an actual ready responder instead of
    a fixed startup sleep; network waits are bounded so failure cannot hang
    cleanup.

Live-tested in 86Box. Windows 98 SE: reset -DismissLogon cancelled the
startup logon dialog and reached Explorer; targeted input behind
another window and into a minimized one; display and kill -RestoreDisplay.
Windows 2000 SP4: targeted input past the foreground lock (including a
minimized target after another window had keyboard input), display,
kill -RestoreDisplay, registry set/change/delete with protected-key refusal,
and the native process wait. The physical machine's network-password variant
and the relay path are not yet live-tested. The original 0.15.0 process-wait
hang has not been reproduced: the reconnect-budget defect is fixed and the
new process wait avoids the repeated relay polling path, without claiming
that the original incident's root cause is proven.

0.16.0 (2026-11-06)

Crash detection in one call: wait until a program exits or a fault dialog
about it appears, and get the dialog's text back, instead of polling
windows and processes or taking screenshots.

Added

  • wait-fault [-Pid <id> | -Name <image>] [-Dismiss]: exit 0 with
    Outcome = Exited when the process ends, exit 50 with Outcome = Fault
    when a fault dialog about it appears. Fault holds the dialog's Title,
    Message, Details and Buttons. On Windows 9x, Details is the
    register dump ("CRASHPRB caused an invalid page fault in module
    CRASHPRB.EXE at 016f:004010e8. Registers: ..."), read without pressing
    Details. On Windows 2000 it waits for Dr. Watson to finish its log
    (closing the box earlier cancels the log) and gives the exception line
    ("c0000094 (divide by zero)"). The dialog is matched by the image name in
    its title or text as well as by owner, because Windows 9x shows it from
    the KERNEL32 system process and Windows 2000 from Dr. Watson or CSRSS.
    Faults are checked before exit, because the dialog outlives the process.
    Without -Pid/-Name it watches every program until -WaitSeconds
    (exit 0, Outcome = NoFault). -Dismiss closes the dialog. Alias
    wait-crash. MCP: v9x_wait_fault.
  • close-window -Title <text>: WM_CLOSE to the one matching top-level
    window, then wait until it is gone. It refuses the desktop, the taskbar
    and the agent. MCP: v9x_close_window.
  • windows -Controls: each window's buttons, labels and edit text.
    MCP: v9x_windows controls.
  • Protocol: WINDOW_TEXT_REQUEST 0x0063 / 0x8063 and
    WINDOW_CLOSE_REQUEST 0x0064 / 0x8064, capability
    V9X_CAP_WINDOW_TOOLS 0x01000000.
  • tests\integration\crashprb.c: a program that crashes (NULL write,
    divide by zero), exits, shows a message box or hangs on request.

Changed

  • exec -WaitForExit returns on a crash. A crashed program stays alive
    while its fault dialog is up, so the wait used to sit out -WaitSeconds
    (an hour by default). On a 0.16.0+ agent it uses the wait-fault probe:
    exit 50 with Outcome and Fault, and it takes -Dismiss (MCP
    wait_for_exit sets result: "fault" and takes dismiss). Older agents
    wait as before.

Tested on Windows 98 SE and Windows 2000 SP4 in 86Box: NULL write, divide
by zero, a delayed crash, clean exit, an ordinary message box (not a
fault), a quiet watch, and -Dismiss leaving no dialog behind.

0.15.1 (2026-11-06)

Fixed

  • update -Relay replaced only the agent. Its version check and the
    replacement of V9XVNC.EXE and SETUP.EXE ran as inner v9xctl calls
    that dropped -Relay and went to the local default port, so on
    remote-help machines VNC and the tray's Settings page stayed on the old
    version ("WARNING: V9XVNC.EXE was not updated: Agent at
    127.0.0.1:9869 is unreachable"). The inner calls now use the same
    relay machine and server. Host-side only; the guest agent is unchanged.

V9x Remote Agent v0.15.0

Choose a tag to compare

@github-actions github-actions released this 06 Nov 04:50

Everything since v0.13.1, newest first.

0.15.0 (2026-11-06)

What a tester at the machine can see: a chat that survives a reboot, and a
plain-English activity log of what the developer and the agent are doing.
Asked for by a remote-help tester.

Added

  • Activity log, C:\V9XREMOTE\EVENTS.LOG, opened from the tray's new
    Activity log... item (Notepad). One dated line per event a person at
    the machine cares about: agent started (with version), developer connected
    (LAN address or remote help), "Idle: no developer activity for 2 minutes
    (safe to reboot or shut down)"
    , reboot / shutdown / hard reset / agent
    update requested by the developer, remote help connected, unreachable,
    back in touch after a long silence, paused or resumed at the machine.
    Rotates to EVENTS.OLD at 64 KiB. AGENT.LOG stays the technical log.
  • note -Text "...": the developer adds a line to that log
    ("Developer: attempting driver test 12") before doing it, so the record
    is written as things happen rather than after. New EVENT_NOTE_REQUEST
    0x00A0 / EVENT_NOTE_RESPONSE 0x80A0, capability
    V9X_CAP_EVENT_LOG 0x00800000, 1-200 ASCII characters. MCP:
    v9x_note. Older agents: exit 23.
  • Chat history kept across reboots in C:\V9XREMOTE\CHAT.LOG
    (CHAT.OLD past 64 KiB); the chat window reloads the newest 8 KiB when
    the agent starts. Chat lines now carry the date.
  • remote-poll-gap in AGENT.LOG when a remote-help check-in comes more
    than 30 s after it was due, with how long the agent was silent: the
    measurement the "silent guest during 3D" issue asked for.

Changed

  • A busy remote-help machine is refused, not taken over. A second
    -Relay call (or MCP relay) while another caller is still using the
    machine's relay session (a long wait-window, say) used to end that
    call's session. The update server (plugin DB version 4) now refuses it,
    saying which verb holds the session and for how long, and v9xctl exits
    21. A caller's own reconnects still replace its session (each v9xctl
    process and each MCP tool call sends an owner id), a session nobody has
    touched for 60 s is replaced as before, and -RelayTakeOver (MCP
    relay_take_over) takes a live one on purpose.
  • Several relay calls at once. The agent runs up to three relay sessions
    side by side, each on its own thread and loopback connection, and says
    max_sessions=3 / sessions=a,b in its check-in; the server (plugin DB
    version 5) lets that many in, so a screenshot, windows or cat works
    while a long wait-window holds the first session. Check-ins keep their
    normal pace during a session (they used to drop to one every 15 s), and a
    session whose server vanished ends after 5 minutes. Agents before 0.15.0
    still get one session (and the busy refusal above); a 0.15.0 agent on an
    older server gets the single session it offers.
  • GET machines reports busy as null once the machine is offline (its
    last poll is too old to say), and its session row names the verb, age
    and whether it is in use.

0.14.0 (2026-10-29)

Waiting for long jobs (benchmarks, installers, test runs) without sleeping,
polling by hand or guessing how long they take.

Added

  • wait-process -Pid <id> | -Name <image>: returns when no such guest
    process runs (exit 49 at the deadline; WasRunning = false when nothing
    matched on the first look). MCP: v9x_wait_process. Works with 0.13.0
    agents (PROCESS_LIST).
  • exec -WaitForExit: launch detached, then wait until that process
    has exited: no 1-hour exec cap, no execution slot held (no output or exit
    code). MCP: wait_for_exit on v9x_exec.
  • The child's pid in exec results (Pid; MCP pid). New capability
    V9X_CAP_EXEC_PID 0x00400000 and EXEC option 0x0002 make
    EXEC_COMPLETE 32 bytes with the pid; hosts ask only when it is
    advertised. Against 0.13 agents -WaitForExit waits on the image name.
  • wait-file -Match <text> (and exec -WaitForFile ... -Match): until
    the file has a line containing the text, for reports that exist from the
    start and are marked finished later (Complete=yes). MCP: match on
    v9x_wait_file and on exec/shell. Agent 0.13.0+.
  • cat -Follow [-Until <text>]: print lines as the guest appends them,
    one flushed stdout line each, for a line-streaming monitor (Claude Code's
    Monitor tool). Waits for a missing file, restarts when the file shrinks,
    never prints a half-written last line, and -Until stops on the first
    line containing the text, including one already in the file.
  • AGENTS.md: "Long jobs" (run the blocking wait as a background command
    and be woken when it exits, instead of sleeping or holding the
    conversation) and "Old agents: update first".

Changed

  • Every v9xctl wait (wait-file, wait-window, wait-process, cat -Follow, the
    exec waits) polls every 250 ms for the first 10 s, then once a second, so
    a long wait barely loads a guest that is benchmarking; the MCP waits do
    the same.
  • A connection that drops in the middle of a v9xctl wait is reopened for
    the time the wait has left ([connection lost ...; reconnecting] on
    stderr) instead of failing with exit 20.
  • -WaitSeconds accepts up to 86400 (was 600); wait-process,
    exec -WaitForExit and cat -Follow default to 3600.

V9x Remote Agent v0.13.1

Choose a tag to compare

@github-actions github-actions released this 24 Oct 09:23

0.13.1 (2026-10-24)

Added

  • Unattended self-update for hosts. SETUP.EXE /update /S runs the
    same check, signature, version and SHA-256 checks as /update, with no
    windows or questions and never a reboot, so a host can trigger it over
    EXEC (V9 Retro Controller does, from the update server directly). The
    installed copy waits for its child, so the exit code is the result: 0
    staged, 1 failed, 3 nothing newer or nothing published. The outcome is
    also written to C:\V9XREMOTE\TEMP\UPDATE.INI ([result] status=
    staged/current/none/failed, installed=, latest=, message=). A staged
    update is the usual V9XNEW.EXE / V9XSNEW.EXE, so the host can apply it
    at once with UPDATE_REQUEST instead of waiting for a restart.

Changed

  • SETUP.EXE /update without /S is unchanged, except that "nothing newer"
    still exits 1 there (3 is the unattended mode's code).

V9x Remote Agent v0.13.0

Choose a tag to compare

@github-actions github-actions released this 23 Oct 12:00

Everything since v0.12.0, newest first.

0.13.0 (2026-10-23)

Added

  • Smaller, faster screenshots. screenshot and input -OutFile take
    -Region x,y,w,h, -Scale <0.01-1> and -Gray; the MCP's
    v9x_screenshot and every input tool take region, scale and gray.
    The result reports RegionX/Y/Width/Height and ScalePercent so image
    coordinates map back to the screen.
  • The agent shapes the capture before sending it (new capability
    V9X_CAP_SCREENSHOT_OPTIONS 0x00020000, an options block after the
    SCREENSHOT_REQUEST path; src\common\shotxf.c): crop, box-filtered
    downscale, and lossless packing. A screen with at most 256 colours (every
    8-bpp desktop and most dialogs) goes out as 8-bit with an exact palette,
    RLE8-compressed when smaller, instead of 24-bit. New hosts always ask for
    that, so even a plain screenshot crosses the wire (and the remote-help
    relay) in a fraction of the bytes. -Gray is the lossy option.
  • Against older agents, v9xctl and the MCP crop/scale/grey on the host after
    the download, so the options work everywhere.
  • The MCP reads 8-bit and RLE8 BMPs and sends greyscale PNGs as one channel.
  • The verbs agents kept improvising (each its own capability, so older
    agents are refused cleanly with exit 23):
    • cat -Path <file> [-Tail N] [-Match text] [-MaxBytes n] / MCP
      v9x_read_text: a text file inline, filtered and bounded in the guest,
      instead of get and reading the download (V9X_CAP_FILE_TOOLS
      0x00040000, FILE_READ_TEXT; filter in src\common\textscan.c).
    • hash -Path <file> [-Source <local>] / v9x_hash: size, CRC32 and
      SHA-256 computed in the guest, compared with a local file on request.
    • delete -Path <path> [-Recurse] [-Force] / v9x_delete; refuses roots,
      the Windows and system directories, C:\V9XREMOTE and their parents.
    • ps [-Name] / v9x_ps and kill -Pid|-Name / v9x_kill
      (V9X_CAP_PROCESS 0x00080000, toolhelp): processes started by exec
      are marked with their request id, so a job holding the slot (BUSY on
      screenshot, reboot or update) can be found and ended; its exec
      completes as usual. The agent and Windows core processes are refused.
      Exit 45.
    • reg -Key HKLM\... / v9x_reg_query (V9X_CAP_REGISTRY 0x00100000):
      subkeys and decoded values of one key, read-only, instead of
      REGEDIT /E and get. Exit 46.
  • Waits instead of sleeps. Transcripts showed thousands of
    Start-Sleep calls between v9xctl calls; each now has a verb or option
    that returns as soon as the condition holds (AGENTS.md "Never sleep and
    poll"):
    • wait-file -Path [-Gone] [-StableMs] and exec/shell -WaitForFile
      (stat polling on one connection; every agent). MCP v9x_wait_file,
      wait_for_file. Exit 47.
    • windows, wait-window -Title [-Gone], exec -WaitForWindow and
      screenshot -Window <title> (captures that window only) on a new
      WINDOW_LIST request (V9X_CAP_WINDOWS 0x00200000). MCP
      v9x_windows, wait_for_window, window. Exit 48.
    • Connections dropped before HELLO are retried twice by default;
      -WaitForAgent N (any verb) retries every transport failure for N s
      with backoff. wait-desktop and ping -WaitSeconds wait for the agent
      too. MCP: same retry, v9x_ping wait_seconds.
  • v9xctl usage: help <verb>; a one-line unknown-verb error (verb list, this
    copy's version and path) instead of the full help; verb aliases agents
    guess (ls, pull, status, rm, type, tasklist, wait-ready...);
    -Target host:port; -HostName/-EndpointPort aliases.
  • The tray icon carries a small cloud in its bottom-right corner while remote
    help is connected to the relay: white with a blue outline, or grey while
    remote help is paused. It sits on every state, so the white/yellow resting
    body and the lime command flash work as before.

Fixed

  • get (and stat) could return a stale or garbage copy of an INI file that
    a running program had just written with WritePrivateProfileString:
    Windows 9x caches that file in memory and writes it lazily, which is why
    shell TYPE (a DOS VM start flushes it) showed the right contents.
    stat, get, hash and cat now flush that file's cache first.
    Reproduced on 98 SE: a five-key INI read back as 17 bytes of garbage before,
    intact after.
  • help update (any help <verb>) failed parameter binding: the second
    positional argument only accepted vnc's status/start/stop.

0.12.1 (2026-10-20)

Added

  • One GUI step in one call: v9xctl.ps1 input -OutFile <image> and
    screenshot: true on every MCP input tool send the input, wait
    -SettleMs/settle_ms (default 500) and capture a screenshot on the same
    connection. Host-side only; no protocol or guest change.

Changed

  • v9x_input with raw moverel actions now checks that the agent supports
    relative mouse moves, as v9x_move already did.

V9x Remote Agent v0.12.0

Choose a tag to compare

@github-actions github-actions released this 20 Oct 00:45

Everything since v0.9.1, newest first.

0.12.0 (2026-10-20)

Added

  • Remote help (docs\remote-help.md): the owner of a real Windows 9x or
    2000 machine can lend it to the developer over the Internet, and the
    developer drives it with every existing verb and MCP tool.
    • Off by default. The tray's new "Remote help..." item opens
      SETUP.EXE /remote, which explains what lending allows, takes a name,
      GitHub user and email, and sets [remote] mode=on. The agent then
      registers its install ID with the update server, which issues a
      machine id (M-XXXXXX), and checks in every 60 s (2 s while a session
      or chat is active). "Pause remote help" on the tray blocks commands
      until the agent restarts; chat keeps working.
    • Signed relay. A session is a byte tunnel from the developer's PC to
      the agent's own handlers, through the server over plain HTTP. Every
      chunk towards the agent is Ed25519-signed on the developer's PC by the
      new build\tools\v9xsign.exe and checked by the agent against
      include\v9xremote\remote_key.h, bound to an agent-chosen nonce and a
      sequence number, so neither the server nor anyone on the path can run
      anything; a bad chunk ends the session (relay-bad-chunk).
    • Host side. v9x_mcp.py takes relay="M-XXXXXX" on every agent tool
      (or --relay), and adds v9x_relay_machines and v9x_chat;
      v9xctl.ps1 takes -Relay M-XXXXXX on every verb. Uploads keep 1 MiB
      of chunks in flight through the relay. Reboot and update proofs
      reconnect through a new session.
    • Chat. Two-way: a message from the developer pops up in the agent's
      chat window (tray: "Chat with developer..."), and the owner's replies
      reach the developer's dashboard and MCP.
    • In use. The agent remembers when the last command (anything but
      HELLO and PING) arrived and from where (a LAN address or relay). The
      tray tooltip reads "In use: last command N s ago from ..." for a minute
      afterwards; the age also goes to the server and to C:\V9XREMOTE\STATUS.INI,
      which the /remote page shows.
    • Server. The v9x_update_checker plugin (separate repository) gains
      the agent and developer endpoints, a Machines tab (online machines,
      owners, state, last command, chat) and MCP tools remote_list_machines,
      remote_get_machine, remote_chat_send, remote_chat_read and
      remote_set_machine, none of which can command a machine.
  • Self-update. Tray "Check for updates..." runs SETUP.EXE /update: it
    asks the update server, verifies the release's Ed25519-signed SIGNED.TXT
    against include\v9xremote\release_key.h and the downloaded zip's size
    and SHA-256 against that, shows the release notes, and on the owner's
    yes stages the new version through the normal upgrade path (it takes over
    at the next restart). [update] check_on_startup=1 (a tickbox on the
    /remote page) checks quietly after start and turns the tray item into
    "Install update x.y.z...". The crypto, inflate and zip reader are ported
    from the Velocity9x updater, with its host tests.
  • make-release.ps1 also builds v9x-remote-agent-<version>-win32.zip, the
    self-update package, and the release workflow publishes it;
    scripts\sign-release.ps1 signs it afterwards on the developer's PC.

Changed

  • The DOWNLOAD verb's HTTP client moved to src\guest\http.c, shared with
    remote help and SETUP.EXE; a silent server now times out after 60 s
    instead of hanging the request.
  • SETUP.EXE imports WSOCK32 (for /update).
  • publish-github.ps1 strips the update server's exact host name before its
    private-name check.

0.11.0 (released as part of 0.12.0)

Added

  • V9XVNC.EXE: live remote control over standard VNC (RFB 3.3/3.7/3.8), so
    any VNC viewer can watch the screen and drive the keyboard and mouse.
    • Off by default. It serves only when AGENT.INI has [vnc] enabled=1 (exit 9 otherwise). SETUP.EXE has an "Enable VNC remote
      control" option, unticked by default; /vnc and /novnc set it for a
      silent install. Ticked, setup writes enabled=1, starts the server from
      HKLM\...\Run at every logon (on Windows 9x and 2000, since it needs a
      desktop) and starts it now. Unticked, or on uninstall, setup removes that
      and stops a running server. INSTALL.BAT copies the binary but never
      enables it.
    • Separate process. It runs apart from the agent, like V9XSHOT.EXE,
      because GDI capture can hang in a broken display driver; the agent never
      imports GDI32. Inside the server every GDI call runs on one worker
      thread. A capture that takes over 5 s is logged as capture-stalled:
      the server keeps reading input and sends no frames until it recovers
      (capture-resumed). If setup or teardown is stuck for 15 s, it logs
      gdi-wedged-exit and exits 10, releasing every held key first.
    • Settings. One viewer at a time, security type None unless a password
      is set (see below), bind_address
      and allowed_client honoured. Port and poll rate are in [vnc] (default
      5900, 100 ms).
    • Frames. The screen is polled into a 32 bpp DIB section and only
      changed 16x16 tiles are sent, as Raw or Hextile, in whatever 8/16/32 bpp
      true-colour format the viewer asks for; a colour-map viewer gets a fixed
      3-3-2 palette. The cursor is drawn into the frame (Windows 98 and later).
      A mode change is sent as DesktopSize, or drops a viewer that does not
      support it.
    • Keys. X11 keysyms map to virtual keys, with extended keys flagged and
      Shift added when a layout needs it. Everything held is released when the
      viewer disconnects.
    • Cursor. A viewer that takes the RichCursor pseudo-encoding gets the
      cursor shape on its own and draws it locally, so the frame no longer
      contains it and moving the mouse dirties no tiles. The shape is rendered
      over black and over white to find transparent and inverting pixels.
      PointerPos reports moves the guest makes itself. Other viewers still get
      the cursor drawn into the frame.
    • A second viewer gets RFB's connection-failed reply ("Another VNC
      viewer is connected to this machine.") instead of hanging in the listen
      backlog.
    • ZRLE. The deflate encoder is written for this project
      (src\guest\vncz.c: fixed Huffman, greedy LZ77, a sync flush after each
      rectangle, one zlib stream per connection), since the build has no C
      runtime or zlib. Each tile takes the smallest of raw, solid, packed
      palette, plain RLE and palette RLE. One full 1024x768 frame of the test
      desktop at 32 bpp took 20 KB, against 104 KB for Hextile and 3.1 MB for
      Raw. The server takes the first of ZRLE, Hextile and Raw in the viewer's
      order.
    • CopyRect. When the foreground window turns up elsewhere at the same
      size, the viewer copies the pixels it already has, and the tile diff
      then sends only what the copy missed.
    • Clipboard both ways. Text only, as CF_TEXT with Latin-1 and CRLF on
      the Windows side. Changes are found by polling every 0.5 s and compared
      by hash, so text from the viewer is not echoed back. All clipboard calls
      run on the GDI worker, so the watchdog covers an owner that hangs; its
      wait now also handles window messages, because the clipboard owner window
      must answer them.
    • Optional VNC password ([vnc] password, at most 8 characters, a
      masked field in SETUP.EXE). Viewers then use VNC authentication; DES is
      in src\guest\vncdes.c and checked against FIPS known answers by the new
      host test. A wrong password is refused after 2 s. vnc status and
      v9x_vnc report PasswordSet, never the password.
    • Sampled capture. Reading the whole screen back was the bottleneck:
      about 780 ms per 1024x768 frame on an emulated S3 ViRGE at 16 bpp,
      against about 80 ms to diff and next to nothing to encode, which held a
      viewer to roughly one frame a second. Each poll now reads one pixel row
      per 16-row band of tiles, rotating which row through all 16. It then
      reads in full only the tiles whose sampled row changed, those that
      changed last frame, and the area under a drawn cursor, and the diff
      checks only those tiles. A full read still happens for a full-frame
      request and after a resize. In the same typing test, polls went from
      10-12 to 98 per 10 s (about 54 ms each), so the 100 ms poll interval is
      now the limit. perf-* lines in VNC.LOG (every 60 s and at session
      end) give capture, diff, encode and send times.
    • Windows 2000. On NT the server installs and runs as a Windows service
      (V9xRemoteVnc) as LocalSystem, instead of the Run key, because only
      SYSTEM can open the Winlogon (secure) desktop. The capture and input
      threads follow the input desktop with OpenInputDesktop /
      SetThreadDesktop, so a viewer sees and can drive the lock / "Unlock
      Computer" dialog and the logon screen, not just the user desktop. The
      capture thread owns no window (the clipboard moved to its own thread,
      which owns the hidden clipboard window) so SetThreadDesktop can move
      it. V9XVNC.EXE -install / -uninstall manage the service (SETUP.EXE
      does this on NT); -service is the SCM entry point. Sending Ctrl+Alt+Del
      from a viewer is not supported (no pre-Vista API); pair the VM with
      autologon. All of this is resolved at run time and is inert on Windows 9x
      and Windows 95. Service support: src\guest\vncsvc.c.
    • Command line. -stop and -status (exit 0 running, 1 not).
    • Test hook. While C:\V9XREMOTE\TEMP\VNCSTALL.DAT exists the capture
      worker behaves as if wedged, so tests\integration\vnc_stall.py can
      drive the watchdog on a healthy machine.
  • `v9xctl vnc status|...
Read more

V9x Remote Agent v0.9.1

Choose a tag to compare

@github-actions github-actions released this 10 Oct 09:13

Everything since v0.6.3, newest first.

0.9.1 (2026-10-09)

Changed

  • v9xctl update also upgrades agents too old to hot-update (before 0.6.0).
    It used to judge that by capability 0x0200, but 0.5.x advertises that bit
    too, so against 0.5.x it uploaded both binaries and then failed on
    UNSUPPORTED_OPERATION (exit 41). It now reads INFO AgentVersion first
    and, below 0.6.0, drives the staged route those agents support, with every
    guest path fixed and 8.3:
    copy the package to C:\V9XPKG, run its INSTALL.BAT there, check that
    V9XNEW.EXE is staged, reboot so UPDATE.BAT swaps it in from
    RunServices, confirm the agent now reports the package's version and
    nothing is left staged, then remove C:\V9XPKG. A session that improvised
    this by hand put the package in a long-named folder, and the boot-time step
    stopped at a prompt until someone pressed a key. The result carries
    Mode = Staged; failures exit 41 (staging) or 42 (did not apply, see
    C:\V9XREMOTE\UPDATE.LOG). It creates DOS VMs (INSTALL.BAT,
    UPDATE.BAT), which agents before 0.6.0 cannot avoid. Verified by staging
    0.5.2 to 0.9.1 on a Windows 98 SE guest. Host-side only; the agent is
    unchanged from 0.9.0 apart from its version.
  • The MCP v9x_update tool checks the agent version the same way and refuses
    an agent before 0.6.0 before uploading anything, pointing at
    v9xctl update.

0.9.0 (2026-10-09)

Changed

  • Idle connections are closed: a client that sends nothing for
    AGENT.INI [agent] idle_timeout seconds (default 300, 0 never) is
    dropped and logged as client-idle-timeout, except while it owns a running
    EXEC. Before, a client that vanished without a FIN or RST reaching the guest
    held one of the four connection slots, and kept the tray icon busy, until
    the agent restarted. Applies to Win9x and NT; uses select() only, so the
    import table is unchanged.

Added

  • Windows 2000 support for the Win32 agent and SETUP.EXE. Windows 98
    behaviour is unchanged: every difference is chosen at run time from
    GetVersion() (src/guest/platform.c), and the import table is the same.
    • Autostart: NT ignores RunServices, so on NT SETUP.EXE and the
      hot-update swapper register HKLM\...\CurrentVersion\Run instead. The
      agent then starts at logon, so pair it with autologon. Over an existing
      install, SETUP.EXE stages the update by pointing Run at
      V9XNEW.EXE -hotswap rather than UPDATE.BAT. SETUP.EXE /uninstall
      clears both keys on NT.
    • Reboot and shutdown enable SeShutdownPrivilege first, which NT requires
      before ExitWindowsEx (also for SETUP.EXE's "Restart Windows now"). If
      it cannot be enabled the request fails with ACCESS_DENIED instead of
      being accepted and then doing nothing. The token calls are resolved with
      GetProcAddress, so nothing new is imported.
    • Shutdown on NT drops EWX_POWEROFF when the machine has no soft-off
      (GetPwrCapabilities reports no SystemS5, as with the "Standard PC"
      HAL and APM off), because that HAL turns a power-off request into a
      reboot. Such a machine halts at "It is now safe to turn off your
      computer" and the agent logs shutdown-accepted-halt. Win9x still
      always powers off.
    • Shell-mode EXEC runs CMD.EXE /C on NT, where COMMAND.COM is the
      16-bit interpreter under NTVDM. The wire format is unchanged.
    • Hot update: NT lets a running EXE be renamed, so on NT the swapper also
      waits for the old agent's singleton mutex to disappear before swapping.
    • Sockets are not inheritable on NT. NT Winsock sockets are kernel handles
      that children inherit by default, so the hot-update swapper (and any
      EXEC child) held the listening socket and the relaunched agent looped on
      listen-failed forever. Every agent socket now has its inherit flag
      cleared (SetHandleInformation, resolved at run time), and on NT the
      swapper relaunches the agent with no inheritance, which also recovers an
      agent built without the fix. The swap log says run-set on NT.
    • INSTALL.BAT and UNINSTALL.BAT stop on NT and point at SETUP.EXE.
    • INVENTORY reads the NT locations: os from Windows NT\CurrentVersion
      (version_number like 5.0.2195, plus product_type), bios from
      HARDWARE\DESCRIPTION\System (plus video_version), and display
      bpp and refresh from EnumDisplaySettingsA. Devices are the present
      instances under SYSTEM\CurrentControlSet\Enum, with presence and
      problem codes from CFGMGR32 (meta devices=live); an instance with no
      Class value is classified through its ClassGUID. CFGMGR32 and
      EnumDisplaySettingsA are resolved at run time. The 9x records are
      unchanged, apart from REG_MULTI_SZ values now being read (joined with
      /) where before they were skipped.
    • allow_dos_vm=0 on NT refuses only a direct exec of a DOS or Win16
      program (anything that is not a PE image), since CMD.EXE and console
      EXEs start no VM there; EXEC_SHELL stays advertised. SETUP.EXE labels
      the option "Allow DOS and 16-bit Windows programs (NTVDM)" on NT.

0.8.1 (2026-10-07)

Changed

  • A guest download may fetch up to 2 GiB (V9X_MAX_DOWNLOAD_SIZE). It
    streams straight to the guest disk, so it no longer shares the 256 MiB
    put/get ceiling; a 256 MiB+ game archive failed with Win32 223
    (ERROR_FILE_TOO_LARGE) on 0.8.0. The wire format is unchanged.

Added

  • Relative mouse moves: INPUT opcode 7 (s32 dx, s32 dy), capability bit
    V9X_CAP_INPUT_RELATIVE (0x8000), moverel DX,DY in v9xctl input
    and relative=true on the MCP v9x_move tool. Games that read motion
    deltas and recentre the cursor (UT99's own menu cursor) never see an
    absolute move. Sent as mouse_event motion in steps of at most 4 pixels,
    below Windows' default acceleration threshold of 6.

0.8.0 (2026-10-01)

Added

  • SETUP.EXE, a graphical install wizard (Welcome, Options, Install,
    Finish), launched by AUTORUN.INF from the install CD. The Options page
    sets LAN discovery, DOS programs (allow_dos_vm), TCP port, listen address
    and allowed client, and shows the install ID. Over an existing install it
    keeps the settings and ID and stages the update for the next boot, exactly
    like INSTALL.BAT; run from C:\V9XREMOTE it only changes settings. It
    registers in Add/Remove Programs (SETUP.EXE /uninstall), and /S runs
    a silent install. INSTALL.BAT stays as the no-GUI path.
  • Install ID: an RFC 4122 v4 UUID in AGENT.INI [agent] install_id,
    generated by SETUP.EXE or on the agent's first start, so a machine can be
    recognised after its IP changes. Win9x and DOS agents both have it. It is
    reported in INVENTORY meta install_id, and in INFO when the host asks
    for it: capability INSTALL_ID (0x4000) means INFO accepts a u32 flags
    payload, and flag 1 appends the ID after allowed_client. Older hosts
    send no flags and see the old reply, so nothing breaks either way.
  • LAN discovery (Win9x agent): with discovery=1 the agent runs an
    mDNS/DNS-SD responder (RFC 6762/6763, Winsock 1.1 multicast, no Bonjour)
    advertising _v9xremote._tcp.local with SRV, TXT (id, ver, build,
    dos, os, name) and A records, re-announcing when its address
    changes and sending a goodbye before a hot update or reboot. INVENTORY
    meta discovery says off, on or unavailable. Off by default in
    AGENT.INI; SETUP.EXE ticks it.
  • v9xctl discover lists agents that answer, and a global -Id <install id>
    finds the target by discovery instead of -Host/-Port, and finds it again
    if it comes back from a reboot on a new address. -QueryAddress sends the
    query unicast to one address. The MCP server gains v9x_discover and
    --id, with the loopback guard applied to the address discovery returns.

0.7.1 (2026-09-26)

Fixed

  • INVENTORY lists only devices that are loaded now. It walks the
    Configuration Manager's live tree (HKEY_DYN_DATA\Config Manager\Enum)
    and follows each node's HardWareKey into HKLM\Enum, instead of reading
    every instance under Services\Class, which also keeps devices the machine
    had in the past. On a Velocity9x netbook 0.7.0 reported three display
    adapters, five disk drives and a dozen USB controllers, and named the
    stale "Standard PCI Graphics Adapter" as the display adapter; the display
    record now names the first working live Display node (the GMA 950).
    device records gain id (the hardware key, e.g. PCI\VEN_8086&...) and
    problem (the Configuration Manager problem code, when nonzero). A final
    meta record now always says devices=live, or devices=registry when
    there is no live tree and the old walk is used.

0.7.0 (2026-09-26)

Added

  • INVENTORY message (0x0070 -> 0x8070) and capability bit INVENTORY
    (0x2000): a hardware and software inventory read with calls that are safe
    on every 9x release. The reply is a list of typed records (os, cpu,
    memory, drive, display, net, bios, device, meta), each a set
    of string key/value fields, so fields can be added later without another
    protocol change. It covers the Windows product/build/sub-version, DirectX
    and IE versions, CPUID vendor/brand/family/model/stepping/features and a
    measured clock speed, physical and page-file memory, per-drive type, label,
    file system and size, the display adapter and resolution, host IP
    addresses, BIOS strings and the PnP device list (DriverDesc of the Display,
    Monitor, MEDIA, Net, disk controller, CD-ROM, modem, port, USB and input
    classes). Only local fixed disks are probed, with critical-error dialogs
    suppressed, so an empty floppy or CD drive or a dead network share never
    stalls or prompts. CPUs without CPUID (386/486) report vendor=unknown.
    The reply fits the 16 KiB response buffer; if it would not, the last
    record is meta truncated=1.
  • `v9xctl inve...
Read more

V9x Remote Agent v0.6.3

Choose a tag to compare

@github-actions github-actions released this 25 Sep 06:12

0.6.3 (2026-09-24)

Added

  • The notification-area icon now shows activity. Its body flashes lime while
    any client is connected or any execution is running, and a short hold keeps
    it flashing after the last frame so even a bare ping blinks once. The
    agent counts claimed connection slots and every frame it receives or sends;
    the tray worker samples those counters every 100 ms and swaps the icon at
    250 ms intervals. Nothing about the protocol changes.
  • The icon itself is now drawn by the agent as a 16-colour icon resource in
    memory (CreateIconFromResource) instead of borrowing IDI_APPLICATION:
    the agent may not import GDI32, so this is the only way to have two colour
    variants of one picture. If icon creation fails the agent logs
    tray-createicon-failed and falls back to the stock icon without flashing.
  • scripts\v9xctl.ps1 explains itself. Running it with no verb, an unknown
    verb, or help prints the verb list, the connection options (-Host,
    -Port, -ConnectTimeoutSeconds) and worked examples instead of an
    interactive prompt for Action; a verb missing its required parameter
    prints that verb's usage line and an example. Comment-based help backs
    Get-Help .\scripts\v9xctl.ps1 -Examples, verbs tab-complete, and a
    connection failure now names the target it tried and how to override it.

V9x Remote Agent v0.6.2

Choose a tag to compare

@github-actions github-actions released this 04 Sep 07:11

0.6.2 (2026-09-04)

Fixed

  • The notification-area icon, which had never appeared on any guest in any
    release that shipped it. v9x_tray_start passed a NULL lpThreadId to
    CreateThread, which Windows 9x rejects with ERROR_INVALID_PARAMETER
    (Windows NT accepts it, so no modern host ever flagged the call). The window
    and icon were created fine; the worker thread never existed, so
    Shell_NotifyIcon was never reached. Measured as tray-thread-failed gle=87
    on 98 SE. The owner window now also belongs to the worker thread and that
    thread pumps its own message queue, instead of the window being created on
    the main thread that blocks in accept() forever.
  • Tray failures are no longer silent. v9x_tray_start returned 0 for three
    different reasons behind one tray-icon-failed line, and a failing
    Shell_NotifyIcon was retried every second with its result discarded, so a
    wholly broken feature could not be diagnosed from the log. Each failure now
    names itself with its GetLastError (tray-window-failed,
    tray-loadicon-failed, tray-thread-failed, tray-notify-failed) and
    success logs tray-icon-added.

Added

  • scripts\set-autologon.ps1: report, enable or disable Windows 9x autologon
    on a guest, and clear a logon dialog that is already up (-Dismiss). Purely
    host-side, composed from existing verbs, no protocol change.
  • scripts\capture-emulator-window.ps1: capture the emulator's own window from
    the host. screenshot needs Explorer, so it returns exit 43 for precisely
    the states worth seeing - a logon dialog, a BIOS prompt, a shutdown hang.
    This reads the window instead and needs nothing from the guest.
  • Documented what the agent can and cannot do before anyone logs on.
    RunServices does start it before the logon dialog and everything except
    screenshot and wait-desktop works there, so a guest that prompts at boot
    stalls automation at its first wait-desktop while ping keeps succeeding.
    AGENTS.md now says to call info before concluding a guest is hung.

V9x Remote Agent v0.5.2

Choose a tag to compare

@github-actions github-actions released this 13 Aug 10:41

0.5.2 (2026-08-13)

Fixed

  • Screenshot capture and all GDI screen probing now run in the disposable
    V9XSHOT.EXE helper. A display-driver GPF or blocked GDI call can no longer
    directly crash the network agent; the agent terminates a helper that has not
    completed within 7.5 seconds and returns a normal screenshot error.
  • Capture is rejected unless Explorer's desktop is ready, and the helper
    refuses a mode whose dimensions change during a short stability sample.
  • V9XAGNT.EXE no longer imports GDI32. HELLO and INFO use USER screen metrics
    and report bits-per-pixel as unknown (0); successful screenshot responses
    still report the source colour depth measured by the helper.
  • The Win98 install and staged-update paths deploy the helper with the matching
    agent build.
  • INSTALL.BAT validates the complete package before modifying the guest,
    checks every required copy, and removes both staged binaries if either copy
    fails. UPDATE.BAT installs the helper before activating the matching agent.

Changed

  • Recovery guidance now forbids screenshots during fullscreen mode changes or
    suspected display wedges. Use INFO, direct trace-dump execution, and file
    download first, and capture only after desktop readiness is re-established.
  • BootCounter is documented as an agent-start counter, not independent reboot
    proof; reconnect, the persisted resume token, and desktop readiness are
    required before post-reboot GUI work.