NDepend Blog

Improve your .NET code quality with NDepend

Debugging a .NET App on Linux from Windows Visual Studio with WSL

September 15, 2021 6 minutes read

Debugging a .NET App on Linux from Windows Visual Studio with WSL2

NDepend analysis, reporting, API and Power-Tools will run on Linux and MacOS with the next version 2021.2. To achieve that, a major refactoring session has been achieved to isolate code that can compile and run upon .NET Standard 2.0 and .NET 5.0. When everything worked fine on .NET 5.0 on Windows, it was time to debug the whole thing on .NET 5.0 on Linux until it worked completely.

There are various options to run and debug a .NET app on Linux:

  1. The first option to debug a .NET 5.0 program on Linux would have been to use a .NET IDE that supports Linux like Visual Studio Code, Rider or even MonoDevelop. However doing so would have come with a learning curve, we’re quite used to Visual Studio on Windows. Also going back and forth between Windows and Linux dual boot or working on two machines is not ideal. Perhaps we could have use a Linux Virtual Machine like VMware Workstation or Oracle VirtualBox.
  2. The second option is to debug remotely through SSH a .NET 5.0 program running on Linux through Visual Studio or VS Code as explained here. This is a great facility but an even more productive third option exists.
  3. Nowadays Microsoft loves Linux and provides Windows Subsystem for Linux (WSL) for a few years. WSL is now in version 2 and is mature enough to work with. It was surprisingly easy to install and use. But the real strength is the ability to debug a .NET 5.0 program running on Linux/WSL2 from a Visual Studio 2019 session on Windows. This possibility was really a productivity-boost to make our product run on Linux.

Let’s go through our experience with WSL.

Installing WSL2 on Windows

I won’t go through the step-by-step install because it is already described well on various tutorials. We followed the tutorial provided on the Ubuntu site and it was a matter of 10 minutes. Microsoft also provides a tutorial. Every install step worked seamlessly. Also you can choose your preferred Linux distribution, Ubuntu, Suse, Debian…

Once WSL2 installed, Ubuntu became a Windows application that you can start, pin in the Task panel or in the Taskbar. This is quite magic but wait, there is much more!


Exploring Linux folders from Windows

File access interacts nicely between the two OS. You can explore the Linux directory from Windows Explorer through the UNC path \\wsl$\Ubuntu . For example the /home/pat/test  directory in Linux can be explored from Windows from \\wsl$\Ubuntu\home\pat\test.


Notice the different directory separator char, '\' on Windows and '/' on Linux. Linux is picky and won’t recognize the Windows char '\' while Windows accepts both characters as separator. If needed you can get the separator character programmatically from System.IO.Path.DirectorySeparatorChar.

Exploring Windows folders from Linux

The Windows C:/ drive is mounted on the Linux volume/mnt/c. It means that you can access your entire file system from Linux. For example I prepared this small .NET 5.0 application and made it run on both OS.


I didn’t have to copy or transfer WhichOperatingSystem.dll somehow, both Linux and Windows runs the exact same DLL file. Both these cd commands refer to the same directory.

Installing .NET 5.0 on WSL

In the section above I assumed that .NET 5.0 was already installed on the WSL2 Linux. But you have to actually install it yourselves. You can install the .NET 5.0 SDK as explained on this official Microsoft page.

Alternatively you can download .NET 5.0 SDK for Linux from the official Microsoft page https://dotnet.microsoft.com/download/dotnet/5.0 and follow this tutorial.

Debug .NET Apps in WSL2 with Visual Studio

We already explained above how to execute our .NET 5.0 application WhichOperatingSystem.dll from both Windows and Linux. Actually Visual Studio 2019 is automatically aware of your WSL2 install and you can select the WSL2 configuration to run and debug.


Here is debugging on WSL2 in action. I added string location = Assembly.GetExecutingAssembly().Location to highlight the fact that the DLL is loaded from the /mnt/c mounted directory.


Debugging step-by-step (F10) is a bit slow (even on a beefy machine) compare to what we are used to. Hopefully the performance will be improved in the near future. Compared to WSL, WSL2 already brings some significant performance gains: Craig Loewen, Windows Developer Platform program manager, wrote, “WSL 2 delivers full system call compatibility with a real Linux kernel and is 3-6x faster compared to earlier versions of WSL”.

The launchSettings.json file

When you select WSL2 in the launch menu, Visual Studio adds a Properties\launchSettings.json file to your solution.

WSL2 launchSettings.json file

It is possible to define several WSL2 launch configurations, each with various command line arguments if needed. For example it can look like that. Notice that we don’t precise the dotnet command but that the name of the DLL is considered as the first command line argument:

WSL2 Multiple Launch Configurations

The <DebugType>portable</DebugType> .csproj setting

This all works fine because we run a brand new .NET 5.0 project with the default settings. In the .csproj if no value is provided for the <DebugType> setting, it takes the default value portable. As its name suggest portable PDBs can be debugged on Windows, Linux and MacOS while the full value only works on Windows. If – like us – you migrate a .NET Framework application to .NET 5.0, your .csproj files certainly contain the value full, and you’ll have to make sure to change it to portable to debug your application on Linux.


You are now ready to debug your .NET 5.0 or .NET 6.0 applications on Linux from Visual Studio. WSL2 and its integration with Visual Studio is an awesome productivity features. Having a Linux box right inside your Windows OS saves a lot of time.

The only WSL2 issue we found is the impossibility to open the HTML + javacsript report generated by NDepend in a browser. On a regular Linux install doing so work Process.Start("xdg-open", "url"). But on WSL2 we get the message “InvalidOperationException This command cannot be run due to the error: The system cannot find the file specified”.

Notice that Jetbrains seems to have planned WSL2 support in Rider but at this time (September 2021) they are not there yet. See the WSL2 support feature request page here.

When porting NDepend to Linux we found a few .NET Framework APIs unsupported on Linux. I already wrote some posts about Migrating Delegate.BeginInvoke Calls to .NET Core, .NET 5 and .NET 6 and On replacing Thread.Abort() in .NET 6, .NET 5 and .NET Core.

In a future post I’ll explain in details how we refactored the entire application to maximize the number of classes that only needs to refer to .NET Standard 2.0. For that we dog-food NDepend that helped us quickly and precisely assess our .NET code compliance with .NET Standard. Almost all our non-UI code is now in some .NET Standard 2.0 assemblies and can be executed from .NET Framework 4.7.2 or 4.8 on Windows and from .NET 5.0 on Windows, Linux and MacOS. As a consequence the next NDepend version will be delivered as a single lightweight and multi-platform redistributable. The advantages are manifold:

  • This simplifies our development process and our build system.
  • This will avoid some headache to our users.
  • Whatever will the .NET trend be in the long term, .NET X.0, Blazor or something unpredictable today, most of our code is already capable to run on it.


  1. Lol. “Linux is picky.” More accurately, “Windows is a snowflake in regards to paths” due to a pact they made with IBM in the early 90s. It’s be nice if windows would get on the same page with everyone else in regards to mount points and path specs.

  2. What an exquisite article! Your post is very helpful right now. Thank you for sharing this informative one

Comments are closed.