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
- Wrap ModernAppInfo construction in try/catch (ZombieProcessException) and drop the stale session.
- Pre-check the process with Process.GetProcessById or a WaitForSingleObject probe before constructing.
- Do not retry indefinitely; a zero handle for a packaged process almost always means it is gone.
- 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
- ModernAppInfo has no fallback path — a zero OpenProcess handle is fatal; always catch.
- Pre-check the PID when you suspect a suspended/terminated UWP process.
- Suppress stale packaged-app sessions from the UI on this exception.
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.