Quick Answer
Set DBG_CONTROL_C to DBG_REPLY_LATER in your debugger's exception handler, or run your app outside the debugger to test Ctrl+C behavior.
What's Going On?
You're debugging something — maybe a console app in Visual Studio or a process in WinDbg — and you press Ctrl+C to send a break signal. Instead of your app catching it, you get error 0x000002B5 (ERROR_DBG_CONTROL_C). The debugger intercepted the signal before your code ever saw it.
This happens because Windows debuggers automatically trap Ctrl+C and convert it into a debug event. That's actually by design: the debugger wants to give you a chance to break into the debugger itself. But if your app relies on handling SetConsoleCtrlHandler or similar, that interception kills your logic.
The exact trigger varies. I've seen it most often in Visual Studio 2019/2022 when running a C++ console app that uses signal(SIGINT, handler), or in WinDbg when you're analyzing a process that expects to handle Ctrl+C gracefully. The debugger catches it first, the error code gets thrown, and you're stuck thinking your app broke.
Step-by-Step Fix
- Identify the debugger in use.
If you're in Visual Studio, check the Debug menu. In WinDbg, you'll see the command window. If you're using a third-party debugger like IDA Pro, the steps differ slightly — but the pattern holds. - For Visual Studio:
Open Debug → Windows → Exception Settings. Look for "Win32 Exceptions" and find0x80000003(STATUS_BREAKPOINT) or just search "control". You'll see an entry forDBG_CONTROL_C. By default, it's set to "Break when thrown". Change that to "Continue" — that tells the debugger to pass the signal to your app. After you change it, click OK. Test by pressing Ctrl+C in your running app. You should see your handler fire instead of the debugger. - For WinDbg:
Use the command.ignore_cpp_exceptions cpp_exceptions— but that's too broad. Better: set a filter forDBG_CONTROL_Cspecifically. Typesxe -c "gn" ctrlcin the command line. That tells WinDbg to catch the event but pass it to the process (the "gn" means "go with exception not handled"). After that, rungto go. Now Ctrl+C should reach your app. - Test outside the debugger.
If you just want to confirm your Ctrl+C handler code works, run the compiled .exe directly, not from within the debugger. Open a command prompt, navigate to your build output, and launch it. Press Ctrl+C. If your handler fires, you know the debugger was the problem, not your code.
Alternative Fixes
If the main fix didn't work — maybe you're in a managed debugger like the one in VS for C# — try these:
- Run without the debugger attached.
In Visual Studio, press Ctrl+F5 instead of F5. That starts the app without debugging. The debugger won't intercept anything. You lose breakpoints, but you can test Ctrl+C behavior cleanly. - Change the console mode.
CallSetConsoleModeto disableENABLE_PROCESSED_INPUT. That tells Windows not to send Ctrl+C events to the console at all. Your app won't receive them, but the debugger won't either. This is a hack, not a fix — only use it if you're stuck. - Use a different debugger.
Some lightweight debuggers likegdb(on Windows via MinGW) handle signals differently. If you really need to test signal handling, trygdbwithhandle SIGINT pass nostop noprint.
Prevention Tips
Once you fix the immediate error, you can stop it from coming back. Here's how:
- Set up your debugger's exception handling once and save the settings as a default. In Visual Studio, you can export your exception settings via Debug → Windows → Exception Settings → the gear icon → Export. Load that into future projects.
- Write debug-aware code — use
IsDebuggerPresent()to check if a debugger is attached. If it is, skip the Ctrl+C handler or log a warning. That way you don't get surprised. - Test signal handling in a release build outside the debugger as part of your CI pipeline. Don't rely solely on debug sessions to verify final behavior.
One last thing: if you're working with console apps that need to clean up on Ctrl+C (like saving state or closing files), you're better off handling it in your main loop with a flag rather than relying on the signal handler. Debuggers mess with signals, and your users won't have a debugger attached — so test without one.