Skip to content

fix(ui): enumerate codebase-memory-mcp processes on Windows - #955

Open
EightDoor wants to merge 1 commit into
DeusData:mainfrom
EightDoor:fix/windows-process-enumeration
Open

fix(ui): enumerate codebase-memory-mcp processes on Windows#955
EightDoor wants to merge 1 commit into
DeusData:mainfrom
EightDoor:fix/windows-process-enumeration

Conversation

@EightDoor

@EightDoor EightDoor commented Jul 8, 2026

Copy link
Copy Markdown

Problem

On Windows, the Control Panel's Active Processes tab always shows empty because handle_processes in src/ui/http_server.c only queries the current process via GetProcessMemoryInfo/GetProcessTimes and returns "processes":[]. The POSIX path uses popen("ps") to enumerate all codebase-memory-mcp instances.

Closes #950

Solution

Added Windows process enumeration via CreateToolhelp32Snapshot + Process32First/Process32Next to find all codebase-memory-mcp.exe instances. For each matching process:

  • Open via OpenProcess with PROCESS_QUERY_INFORMATION | PROCESS_VM_READ
  • Read CPU time via GetProcessTimes (user + system)
  • Read memory via GetProcessMemoryInfo (WorkingSetSize)
  • Calculate elapsed time from creation timestamp
  • Output in matching JSON format with is_self flag

Changes

  • src/ui/http_server.c: Added #include <tlhelp32.h> and ~70 lines of process enumeration logic

Edge Cases Handled

  • OpenProcess failure (skips process)
  • GetProcessMemoryInfo/GetProcessTimes failure (zeros fallback)
  • CreateToolhelp32Snapshot failure (returns empty array)
  • Buffer overflow protection consistent with POSIX path

Test Plan

  • CI: build-windows / build-windows-arm64 jobs compile successfully
  • Manual: Open Control Panel on Windows with multiple codebase-memory-mcp instances, verify Active Processes tab shows all instances with CPU/memory/elapsed values

@EightDoor
EightDoor requested a review from DeusData as a code owner July 8, 2026 10:17
@DeusData DeusData added bug Something isn't working windows Windows-specific issues ux/behavior Display bugs, docs, adoption UX priority/normal Standard review queue; useful PR with ordinary maintainer urgency. labels Jul 8, 2026
@DeusData

DeusData commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Thanks for taking the Windows UI process-list gap. Triage: Windows local-UI bug for #950, normal priority. Review focus is access-denied handling, stable JSON shape, and avoiding exposure of more process detail than the UI needs.

@DeusData DeusData added this to the 0.9.1-rc milestone Jul 8, 2026
@DeusData

DeusData commented Jul 9, 2026

Copy link
Copy Markdown
Owner

The fix itself looks right — CreateToolhelp32Snapshot + Process32First/Next filtered on our exe name, with OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ) per match, mirrors what the POSIX ps path exposes (localhost-only UI, own-binary processes — no new information surface). Closes a long-standing gap (#50) and CI is otherwise green.

One blocker: DCO fails — the commit is missing its Signed-off-by trailer. git commit --amend -s (or git rebase --signoff) and a push will turn it green; happy to merge right after.

@DeusData

Copy link
Copy Markdown
Owner

Reviewed — this is a clean, well-built fix. Using the Win32 toolhelp snapshot (CreateToolhelp32Snapshot/Process32First) instead of shelling out to tasklist/wmic is exactly right (no command-injection surface, no PATH reliance), OpenProcess is read-only, and handle cleanup is correct (CloseHandle per iteration + on the snapshot). It mirrors the existing POSIX ps enumeration and adds no new surface.

One correction needed before merge: the PR body and commit both say Closes #50, but #50 is an unrelated, already-closed issue. The bug this actually fixes is #950 ("/api/processes returns empty array on Windows"). Could you update the reference to Closes #950 so it links and auto-closes the right issue? Once that's fixed I'll merge (with a merge commit, to keep your DCO sign-off). Thanks @EightDoor!

@EightDoor
EightDoor force-pushed the fix/windows-process-enumeration branch from 2d68535 to 09a4eb1 Compare July 18, 2026 14:47
Windows handle_processes previously returned an empty processes array
because it only queried the current process via GetProcessMemoryInfo/
GetProcessTimes. POSIX uses popen("ps") to enumerate all instances.

Add process enumeration via CreateToolhelp32Snapshot + Process32First/
Process32Next to find all codebase-memory-mcp.exe processes, query per-
process CPU/memory/elapsed time via OpenProcess + GetProcessTimes +
GetProcessMemoryInfo, and return them in the JSON response.

Closes DeusData#950

Signed-off-by: 周凯 <851708184@qq.com>
@EightDoor
EightDoor force-pushed the fix/windows-process-enumeration branch from 09a4eb1 to c5ebab6 Compare July 18, 2026 14:50
@EightDoor

EightDoor commented Jul 18, 2026

Copy link
Copy Markdown
Author

I've already updated the DCO, and the bug fix for #950 has also been implemented. I hope this can be merged as soon as possible. Thanks for your help. Thank you @DeusData

@DeusData

Copy link
Copy Markdown
Owner

Thanks for following up, and sorry this sat after you completed the requested work. The DCO correction and #950 reference are present on current head c5ebab6a15b4a6aa557c9c6deead7b09afa57d81, and the check set is green. You have no remaining triage action; this is back in exact-head maintainer review.

@DeusData

Copy link
Copy Markdown
Owner

Thank you for this, and sorry for the long wait — you fixed the DCO promptly and then the branch sat. That was on us.

Reviewed at your current head, and the work is good. One small thing stands between it and a merge.

What checks out. Using CreateToolhelp32Snapshot + Process32First/Next rather than shelling out to tasklist or wmic was the right instinct: it removes any command-injection or PATH-hijack surface entirely, and it is faster and more robust than parsing tool output. It is genuinely safer than the POSIX path we already ship, which runs popen("ps … | grep").

The matching is exact rather than loose — _stricmp against codebase-memory-mcp.exe, case-insensitive, which is correct for Windows and tighter than the POSIX substring grep. I traced the caller before worrying about that: /api/processes is display-only (ControlTab.tsx renders cards and gauges), and there is no kill or terminate path anywhere in the server or the frontend, so the enumeration cannot act on anything.

Handle hygiene is correct throughout: every successful OpenProcess is paired with a CloseHandle, the snapshot handle is closed after the loop, there are no early returns inside the loop, and an OpenProcess failure skips that PID and keeps scanning instead of aborting. pe.dwSize is set before Process32First. Access rights are read-only. And every added line — including the tlhelp32.h include — sits inside the existing #ifdef _WIN32 branches, so the POSIX code is byte-identical and Linux/macOS cannot change.

The one blocker: an uninitialized read.

FILETIME ftc, fte, ftk, ftu;

These are uninitialized, and the elapsed-time block reads ftc unconditionally. If GetProcessTimes fails, that is an uninitialized read — undefined behaviour — producing a garbage elapsed string. The PR describes a "zeros fallback", but that only covers the CPU and RSS values, not this.

In practice it is close to unreachable: a handle with PROCESS_QUERY_INFORMATION will essentially always satisfy GetProcessTimes. But "essentially always" is not a guarantee, and we do not knowingly merge UB — it is the kind of thing that surfaces years later on someone else's machine under a sanitizer.

Either of these closes it:

FILETIME ftc = {0}, fte = {0}, ftk = {0}, ftu = {0};

or, cleaner, move the elapsed computation inside the GetProcessTimes success branch.

Push that and this merges.

Two optional notes, neither blocking:

  • A now > start guard on the elapsed subtraction would harden against clock skew — if creation time is marginally in the future, the unsigned subtraction wraps to something enormous.
  • PROCESS_QUERY_LIMITED_INFORMATION with a fallback (the pattern this codebase already uses in src/daemon/ipc.c) would also list elevated instances that PROCESS_VM_READ cannot open. Current behaviour silently omits them, which is safe but slightly incomplete.

Thanks again — mirroring the POSIX JSON shape exactly, including is_self, meant the frontend needed no changes at all. That parity work is why this review was straightforward.

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

Labels

bug Something isn't working priority/normal Standard review queue; useful PR with ordinary maintainer urgency. ux/behavior Display bugs, docs, adoption UX windows Windows-specific issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug] /api/processes returns empty array on Windows — Control Panel "Active Processes" shows nothing

2 participants