From 87a8ac161da95a8c68540af1b207c8325edfee02 Mon Sep 17 00:00:00 2001 From: nevse Date: Wed, 12 Aug 2026 23:13:22 +0400 Subject: [PATCH] Stop the process before terminating it ICorDebugProcess::Terminate needs the process synchronized. On a running debuggee it fails with CORDBG_E_PROCESS_NOT_SYNCHRONIZED, the exception is caught and logged, and the request answers success while the process keeps running. Stopping first is what Disconnect already does for the non-terminating path. Terminate disposes whether or not the terminate worked, so a client that sends terminate and then disconnect disposes twice and the second one throws ChannelClosedException out of Dispose, surfaced as "Failed to disconnect". TryComplete makes the second dispose harmless. --- src/SharpDbg.Infrastructure/Debugger/ManagedDebugger.cs | 5 ++++- .../Debugger/ManagedDebugger_RequestHandlers.cs | 9 +++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/SharpDbg.Infrastructure/Debugger/ManagedDebugger.cs b/src/SharpDbg.Infrastructure/Debugger/ManagedDebugger.cs index a5b1c7f..4f6dde1 100644 --- a/src/SharpDbg.Infrastructure/Debugger/ManagedDebugger.cs +++ b/src/SharpDbg.Infrastructure/Debugger/ManagedDebugger.cs @@ -478,7 +478,10 @@ private void Dispose() // Unsubscribe from callbacks to avoid any further event dispatch _callbacks.OnAnyEvent -= QueueEvent; - _runtimeEventChannel.Writer.Complete(); + // TryComplete because this can run twice: Terminate disposes whether or not it succeeded, so a + // client that sends terminate and then disconnect would get a ChannelClosedException out of the + // second one + _runtimeEventChannel.Writer.TryComplete(); // ProcessRuntimeEventQueue is blocked on DapRequestAndRuntimeEventLock (which we hold) and would // never complete if we waited on it here — that is the deadlock. Read and discard remaining events ourselves, // then let the processor exit once the lock is released. diff --git a/src/SharpDbg.Infrastructure/Debugger/ManagedDebugger_RequestHandlers.cs b/src/SharpDbg.Infrastructure/Debugger/ManagedDebugger_RequestHandlers.cs index d07d7f8..bb1f2fc 100644 --- a/src/SharpDbg.Infrastructure/Debugger/ManagedDebugger_RequestHandlers.cs +++ b/src/SharpDbg.Infrastructure/Debugger/ManagedDebugger_RequestHandlers.cs @@ -648,6 +648,15 @@ public void Terminate() { try { + // Terminate needs the process synchronized. On a running one it fails with + // CORDBG_E_PROCESS_NOT_SYNCHRONIZED and leaves the debuggee running. + if (_process.TryIsRunning(out var isRunning) is Cor.S_OK && isRunning) + { + var stopHResult = _process.TryStop(0); + if (stopHResult is not (Cor.S_OK or Cor.CORDBG_E_PROCESS_TERMINATED)) + _logger?.Invoke($"Error stopping process before terminating it: {stopHResult}"); + } + _process.Terminate(0); } catch (Exception ex)