{"record":{"id":"11068227775b2c05","repo":"File-New-Project/EarTrumpet","slug":"process-is-a-zombie-processid","errorCode":null,"errorMessage":"Process is a zombie: {processId}","messagePattern":"Process is a zombie: (.+?)","errorType":"exception","errorClass":"ZombieProcessException","httpStatus":null,"severity":"error","filePath":"EarTrumpet/DataModel/AppInformation/Internal/DesktopAppInfo.cs","lineNumber":63,"sourceCode":"                }\r\n                finally\r\n                {\r\n                    Kernel32.CloseHandle(handle);\r\n                }\r\n            }\r\n            else\r\n            {\r\n                trackProcess = false;\r\n\r\n                if (TryGetExecutableNameViaNtByPid(processId, out var imageName))\r\n                {\r\n                    ExeName = Path.GetFileNameWithoutExtension(imageName);\r\n                    SmallLogoPath = imageName;\r\n                    PackageInstallPath = imageName;\r\n                }\r\n                else\r\n                {\r\n                    throw new ZombieProcessException(processId);\r\n                }\r\n            }\r\n\r\n            // Display Name priority:\r\n            // - AppsFolder\r\n            // - A window caption\r\n            // - Exe Name\r\n\r\n            try\r\n            {\r\n                var appResolver = (IApplicationResolver)new ApplicationResolver();\r\n                appResolver.GetAppIDForProcess((uint)processId, out string appId, out _, out _, out _);\r\n                Marshal.ReleaseComObject(appResolver);\r\n\r\n                var shellItem = Shell32.SHCreateItemInKnownFolder(FolderIds.AppsFolder, Shell32.KF_FLAG_DONT_VERIFY, appId, typeof(IShellItem2).GUID);\r\n                DisplayName = shellItem.GetString(ref PropertyKeys.PKEY_ItemNameDisplay);\r\n            }\r\n            catch (COMException ex)\r","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/File-New-Project/EarTrumpet/blob/aa894e51c22f5f9a939b31b224c4d2d3e163416e/EarTrumpet/DataModel/AppInformation/Internal/DesktopAppInfo.cs#L45-L81","documentation":"DesktopAppInfo constructs display info for a Win32 audio process. It first tries Kernel32.OpenProcess; if that returns a zero handle it falls back to an NtQuerySystemInformation scan to recover the image name. Only when BOTH fail (cannot open the process AND cannot find the PID in the system process list) does it throw ZombieProcessException. The process is effectively gone or inaccessible, so no metadata can be gathered.","triggerScenarios":"An audio session still references a processId whose process has terminated (or that the current user cannot open) by the time DesktopAppInfo is constructed: OpenProcess returns IntPtr.Zero and TryGetExecutableNameViaNtByPid finds no matching UniqueProcessId.","commonSituations":"A short-lived audio-playing process exits while the EarTrumpet flyout is open; the audio host crashed; access denied opening a higher-privilege process; PID reused between session creation and info lookup.","solutions":["Catch ZombieProcessException at the call site that constructs DesktopAppInfo and skip / hide the corresponding session rather than crashing.","Before constructing, liveness-check the PID with Process.GetProcessById (catch ArgumentException) or WaitForSingleObject on a opened handle.","Treat the exception as transient only if you can confirm the PID is still allocated to a live process; otherwise discard the stale session.","If access-denied is the cause, ensure EarTrumpet runs with sufficient privilege or ignore inaccessible processes."],"exampleFix":"// before\nvar info = new DesktopAppInfo(pid, trackProcess: true);\n\n// after\ntry { info = new DesktopAppInfo(pid, trackProcess: true); }\ncatch (ZombieProcessException) { /* session's process is dead; drop it */ return; }","handlingStrategy":"try-catch","validationCode":"// pre-check process liveness before constructing DesktopAppInfo\nbool Alive(int pid)\n{\n    try { using (var p = Process.GetProcessById(pid)) { return !p.HasExited; } }\n    catch (ArgumentException) { return false; }\n    catch (InvalidOperationException) { return false; }\n}","typeGuard":null,"tryCatchPattern":"try { info = new DesktopAppInfo(pid, trackProcess); } catch (ZombieProcessException ex) { Trace.WriteLine($\"Dead process {pid}: {ex}\"); info = null; }","preventionTips":["Always wrap DesktopAppInfo construction in try/catch(ZombieProcessException) at the session-display boundary.","Drop the session gracefully instead of crashing the flyout.","Do not retry the same dead PID; it will keep failing."],"tags":["process","win32","zombie","appinfo","desktop-app","interop"],"backgroundTag":null,"analyzedSha":"aa894e51c22f5f9a939b31b224c4d2d3e163416e","analyzedAt":"2026-08-13T18:51:30.564Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}