I Wish I Knew About Debugger.Launch Earlier

It was only a couple of years ago, that I learned about Debugger.Launch(), and since then, I’ve used it on many an occasion and thought “How did I ever live without this!”. It’s just such a little miracle tool when working with applications that have complex startup code that can’t be debugged easily.

Just the other day, while remembering this beauty of a function, I went back and looked at documentation for when this was released. After all, I probably went a good 5 or 6 years developing in .NET without ever using it.

My jaw almost hit the floor!

You’re telling me, that this has been in the .NET Framework since the dawn of time, and I’ve only just found out about it! UGH!

What Is Debugger.Launch?

Let me give a scenario for you. You are running an application (Such as a web application or windows service), that has startup methods you want to debug. Often this will be things like dependency injection setup, early config file reads or similar. For whatever reason, you can’t just hit F5 and start debugging. You need to run the application, then attach a debugger later. For web applications this is sometimes because you are using IIS even in development, and hitting a URL to test your application. And for things like Windows Services, you want to debug when it’s actually running as a Windows Service.

Now back in the day, I used to do this :

//Added in the startup code section
Thread.Sleep(10000); //Give myself 10 seconds to attach a debugger

Basically sleep the application for 10 seconds to allow myself time to attach a debugger. This kind of works. But it’s not exactly a strict science is it? If I attach early, then I’m left sitting there waiting out the remainder of the sleep time, and if I attach late, then I have to restart the entire process.

And that’s where Debugger.Launch() comes in :

//Added in the startup code section
System.Diagnostics.Debugger.Launch(); //Force the attachment of a debugger

You’re probably wondering how exactly does a debugger get “forced” to attach. Well consider the following console application :

using System;

Console.WriteLine("Debugger is attached!");

Imagine I build this application, and run it from the console (e.g. Not inside Visual Studio). I would then see the following popup :

Selecting Visual Studio, it will then open, and start debugging my application live! Again, this is invaluable for being able to attach a debugger at the perfect time in your start up code and I can’t believe I went so long in my career without using it.

How About Debugger.Break()?

I’ve also seen people use Debugger.Break(), and I’ve also used it, but with less success than Debugger.Launch().

The documentation states the following :

If no debugger is attached, users are asked if they want to attach a debugger. If users say yes, the debugger is started. If a debugger is attached, the debugger is signaled with a user breakpoint event, and the debugger suspends execution of the process just as if a debugger breakpoint had been hit.

But that first sentence is important because I find it less reliable than Launch. I generally have much less luck with this prompting a user to add a debugger. However! I do have luck with this forcing the code to break.

When a debugger is already attached (e.g. You attached a debugger at the right time or simply pressed F5 in Visual Studio), Debugger.Break forces the code to stop execution much like a breakpoint would. So in some ways, it’s like a breakpoint that can be used across developers on different machines rather than some wiki page saying “Place a breakpoint on line 22 to test startup code”.

It probably doesn’t sound that useful, except for the scenario I’m about to explain…

When Debugger.Launch() Doesn’t Work

In very rare cases, I’ve been stuck with Debugger.Launch not prompting the user to debug the code. Or, in some cases, me wanting to debug the code with an application not presented within the popup. There’s actually a simple solution, and it almost goes back to our Thread.Sleep() days.

Our solution looks like :

//Spin our wheels waiting for a debugger to be attached. 
while (!System.Diagnostics.Debugger.IsAttached)
    Thread.Sleep(100); //Or Task.Delay()

Console.WriteLine("Debugger is attached!");

It works like so :

  • If a debugger is not attached, then simply sleep for 100ms. And continue to do this until a debugger *is* present.
  • Once a debugger is attached, our loop will be broken, and we will continue execution.
  • The next call to Debugger.Break() immediately stops execution, and acts much like a breakpoint, allowing us to start stepping through code if we wish.

Now again, I much prefer to use Debugger.Launch, but sometimes you can’t help but do a hacky loop to get things working.

Another extension of this is to wrap the code an IF DEBUG statement like so :

//Spin our wheels waiting for a debugger to be attached. 
while (!System.Diagnostics.Debugger.IsAttached)
    Thread.Sleep(100); //Or Task.Delay()

Console.WriteLine("Debugger is attached!");

This means that should this code make it into production, it doesn’t just spin it’s wheels with no one able to work out why nothing is running. In my opinion however, any Debugger functions should not make it into checked in code.

Using these tools, you can now debug code that you once thought was impossible to do.

1 thought on “I Wish I Knew About Debugger.Launch Earlier”

  1. Debugger.Launch needs you to modify your code. This is fine if you can change the code, or use a magic command line switch like -Debugger to force this branch to execute. My favorite tool is still dnSpy (https://github.com/dnSpy/dnSpy) which lets you set breakpoints at any method without any pdbs because it decompiles the code on the fly. You need to turn off method inlining with specific .ini files to be able to see all local variables. This one lets you debug any method and step into every method if you have code for it or not which is invaluable if you need to debug some production problems. Unfortunately the github repo was archived. Lets hope development continues on this one.


Leave a Comment