While we’re all benefiting from the push towards “One .NET”, the reality is that there will always be differences in operating systems. Windows, macOS, and Linux are, in fact, different operating systems and have their advantages and disadvantages. The difference is a truism easily overlooked when writing C# code and compiling our apps to an intermediate language. In general, everything works, but sometimes we get a rude reminder. One of those reminders is the Debugger
class. This post will show you an admittedly janky way of achieving the same result as Debugger.Launch
across operating systems.
What Is Debugger.Launch?
.NET started life as a Windows-only platform. Therefore, the .NET team could make assumptions about the environment and add functionality specific to the Windows operating system. For example, when calling Debugger.Launch
from .NET code within a Windows environment, the .NET framework will make a system-level call to pause the current process and then launch and attach the debugger. In most cases, that debugger is typically Visual Studio or JetBrains Rider. Developers usually use this method to isolate an issue that is hard to reproduce, but they have a general idea of when or where it might appear.
Sounds helpful, right? Well, only for Windows users. Let’s take a look at the Launch
method.
For those unfamiliar with native APIs, the attribute of DllImport
calls down to native code compiled to the current operating system. On Windows, the operating system implements these APIs as you would expect. However, on macOS and Linux, the Windows native method is replaced by a no-op call, which, as you may have guessed, does absolutely nothing.
Well, what can folks on macOS and Linux do? I have a solution for you, but you may not like it.
Janky Debugger.Launch for macOS and Linux
While Debugger.Launch
doesn’t work, the Debugger.IsAttached
property does. When you attach the debugger, the property value will change. We can write a method that waits for the debugger to connect to the currently paused process.
Now, you can call the WaitForDebugger
method in place of Debugger.Launch
.
To avoid an infinite loop, I decided to add a configurable timeout period through the method’s arguments. We can see the debugger waiting in the console output when running our app. The technique also outputs the process id for user-friendliness. Let’s see an unsuccessful attempt first.
Now let’s see a successful attempt to attach the debugger.
Note, we stop waiting as soon as the debugger is attached, so there is no need to worry about wasting time waiting for the total time limit to elapse.
I hope you found this post helpful, and as always, thanks for reading and sharing. If you have a different approach, please let me know on Twitter @buhakmeh.