How can I get the application's path in a .NET console application?

Viewed 1192079

How do I find the application's path in a console application?

In Windows Forms, I can use Application.StartupPath to find the current path, but this doesn't seem to be available in a console application.

30 Answers

System.Reflection.Assembly.GetExecutingAssembly().Location1

Combine that with System.IO.Path.GetDirectoryName if all you want is the directory.

1As per Mr.Mindor's comment:
System.Reflection.Assembly.GetExecutingAssembly().Location returns where the executing assembly is currently located, which may or may not be where the assembly is located when not executing. In the case of shadow copying assemblies, you will get a path in a temp directory. System.Reflection.Assembly.GetExecutingAssembly().CodeBase will return the 'permanent' path of the assembly.

You can use the following code to get the current application directory.

AppDomain.CurrentDomain.BaseDirectory

The answer above was 90% of what I needed, but returned a Uri instead of a regular path for me.

As explained in the MSDN forums post, How to convert URI path to normal filepath?, I used the following:

// Get normal filepath of this assembly's permanent directory
var path = new Uri(
    System.IO.Path.GetDirectoryName(
        System.Reflection.Assembly.GetExecutingAssembly().CodeBase)
    ).LocalPath;

If you are looking for a .NET Core compatible way, use

System.AppContext.BaseDirectory

This was introduced in .NET Framework 4.6 and .NET Core 1.0 (and .NET Standard 1.3). See: AppContext.BaseDirectory Property.

According to this page,

This is the prefered replacement for AppDomain.CurrentDomain.BaseDirectory in .NET Core

You may be looking to do this:

System.IO.Path.GetDirectoryName(
    System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase)

Assembly.GetEntryAssembly().Location or Assembly.GetExecutingAssembly().Location

Use in combination with System.IO.Path.GetDirectoryName() to get only the directory.

The paths from GetEntryAssembly() and GetExecutingAssembly() can be different, even though for most cases the directory will be the same.

With GetEntryAssembly() you have to be aware that this can return null if the entry module is unmanaged (ie C++ or VB6 executable). In those cases it is possible to use GetModuleFileName from the Win32 API:

[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern int GetModuleFileName(HandleRef hModule, StringBuilder buffer, int length);

I didn't see anyone convert the LocalPath provided by .Net Core reflection into a usable System.IO path so here's my version.

public static string GetApplicationRoot()
{
   var exePath = new Uri(System.Reflection.
   Assembly.GetExecutingAssembly().CodeBase).LocalPath;

   return new FileInfo(exePath).DirectoryName;
       
}

This will return the full C:\\xxx\\xxx formatted path to where your code is.

With .NET Core 3 and above you will get the .dll and not the .exe file. To get the .exe file path you can use.

var appExePath = Process.GetCurrentProcess().MainModule.FileName;

Try this simple line of code:

 string exePath = Path.GetDirectoryName( Application.ExecutablePath);

Path.GetDirectoryName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName) Is the only one that has worked for me in every case I have tried.

I use this for console + net 6

Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)

The techniques, and pitfalls, keep changing. The below assumes you're running a .NET 6 console app on linux (on win/mac the results will follow a similar pattern, just replace /usr/share/ and /home/username/ with the standard locations for your OS).

Demo:

Console.WriteLine("Path.GetDirectoryName(Process.GetCurrentProcess()?.MainModule?.FileName) = " + Path.GetDirectoryName(Process.GetCurrentProcess()?.MainModule?.FileName));
Console.WriteLine("Path.GetDirectoryName(Environment.ProcessPath)                           = " + Path.GetDirectoryName(Environment.ProcessPath));
Console.WriteLine("Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)          = " + Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
Console.WriteLine("typeof(SomeType).Assembly.Location                                       = " + typeof(SomeType).Assembly.Location);
Console.WriteLine("Path.GetDirectoryName(Environment.GetCommandLineArgs()[0])               = " + Path.GetDirectoryName(Environment.GetCommandLineArgs()[0]));
Console.WriteLine("AppDomain.CurrentDomain.BaseDirectory                                    = " + AppDomain.CurrentDomain.BaseDirectory);
Console.WriteLine("System.AppContext.BaseDirectory                                          = " + System.AppContext.BaseDirectory);

Results:

Path.GetDirectoryName(Process.GetCurrentProcess()?.MainModule?.FileName) = /usr/share/dotnet
Path.GetDirectoryName(Environment.ProcessPath)                           = /usr/share/dotnet
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)          = /home/username/myproject/bin/Debug/net6.0
typeof(SomeType).Assembly.Location                                       = /home/username/myproject/bin/Debug/net6.0
Path.GetDirectoryName(Environment.GetCommandLineArgs()[0])               = /home/username/myproject/bin/Debug/net6.0
AppDomain.CurrentDomain.BaseDirectory                                    = /home/username/myproject/bin/Debug/net6.0/
System.AppContext.BaseDirectory                                          = /home/username/myproject/bin/Debug/net6.0/

Each approach has its own pros and cons - see the other answers to learn in which uses cases to use which approach.

I run my .NET 6 console app with dotnet myapp, so what works (reliably) for me is either of:

typeof(SomeType).Assembly.Location
// or
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)

In .NET 6, my WPF app (<TargetFramework>net6.0-windows</TargetFramework>) returns the .dll file path for Assembly.GetEntryAssembly()!.Location instead of the .exe file. They introduced System.Environment.ProcessPath for this purpose:

var path = Environment.ProcessPath; // Note it may be null

Returns the path of the executable that started the currently executing process. Returns null when the path is not available.

See discussion for it here and here.

Related