File-New-Project/EarTrumpet · error · ZombieProcessException

Process is a zombie: {processId}

Error message

Process is a zombie: {processId}

What it means

ModernAppInfo.GetAppUserModelIdByPid resolves the AUMID for a packaged (UWP/Store) audio process. Unlike the desktop path there is no NtQuery fallback: if Kernel32.OpenProcess returns a zero handle for the PID, it immediately throws ZombieProcessException because without a process handle it cannot call GetApplicationUserModelId.

Source

Thrown at EarTrumpet/DataModel/AppInformation/Internal/ModernAppInfo.cs:143

                        Kernel32.OpenPackageInfoByFullName(packageFullName, 0, out IntPtr packageInfoReference);

                        int bufferLength = 0;
                        Kernel32.GetPackageApplicationIds(packageInfoReference, ref bufferLength, IntPtr.Zero, out int appIdCount);

                        buffer = Marshal.AllocHGlobal(bufferLength);
                        Kernel32.GetPackageApplicationIds(packageInfoReference, ref bufferLength, buffer, out appIdCount);

                        appUserModelId = Marshal.PtrToStringUni(Marshal.ReadIntPtr(buffer));
                        Marshal.FreeHGlobal(buffer);

                        Kernel32.ClosePackageInfo(packageInfoReference);
                    }
                }
            }
            else
            {
                throw new ZombieProcessException(processId);
            }

            return appUserModelId;
        }

        private static bool CanResolveAppByApplicationUserModelId(string aumid)
        {
            try
            {
                Shell32.SHCreateItemInKnownFolder(FolderIds.AppsFolder, Shell32.KF_FLAG_DONT_VERIFY, aumid, typeof(IShellItem2).GUID);
                return true;
            }
            catch (Exception ex)
            {
                Trace.WriteLine($"{ex}");
                return false;
            }
        }

View on GitHub (pinned to aa894e51c2)

Solutions

  1. Wrap ModernAppInfo construction in try/catch (ZombieProcessException) and drop the stale session.
  2. Pre-check the process with Process.GetProcessById or a WaitForSingleObject probe before constructing.
  3. Do not retry indefinitely; a zero handle for a packaged process almost always means it is gone.
  4. Confirm the PID still maps to a packaged process via PackageManager if you need to distinguish dead-vs-access-denied.

Example fix

// before
var info = new ModernAppInfo(pid, trackProcess: true);

// after
try { info = new ModernAppInfo(pid, trackProcess: true); }
catch (ZombieProcessException) { return; } // packaged process gone
Defensive patterns

Strategy: try-catch

Validate before calling

bool Alive(int pid)
{
    try { using (var p = Process.GetProcessById(pid)) { return !p.HasExited; } }
    catch (ArgumentException) { return false; }
    catch (InvalidOperationException) { return false; }
}

Try / catch

try { info = new ModernAppInfo(pid, trackProcess); } catch (ZombieProcessException ex) { Trace.WriteLine($"Dead packaged process {pid}: {ex}"); info = null; }

Prevention

When it happens

Trigger: Constructing ModernAppInfo for a PID whose packaged process cannot be opened (terminated, suspended-then-terminated, or access denied). OpenProcess returns IntPtr.Zero and the else branch throws.

Common situations: A UWP app was suspended/terminated by the OS but its audio session lingers; the packaged process crashed; querying a PID after it exited; insufficient rights to open the packaged process.

Related errors


AI-assisted analysis of File-New-Project/EarTrumpet@aa894e51c2 (2026-08-13). Data as JSON: /api/errors/48ba274333f5e4ce. Report an issue: GitHub.