Get your app's name in C#
Recently I've found myself diving back into C#. It's a great language, and discovering all the additions and improvements has been genuinely fun! The last time I dug into the dotnet side of things was about 10 years ago for Datto's sales and marketing division, so it has been a real journey catching up.
At Datto, we needed to build a few utilities to help the lead generation team verify the quality of "leads," or prospective business partners. These tools targeted Windows desktops, so at the time C# was a great choice. This was before Electron was a twinkle in GitHub's eyes, so many of the tools leaned on CefSharp for a combination of web technology and native operating system integration.
Other tools were more mundane. We had a project to collect lead data from a few public websites, for instance. These were console applications that integrated with our home-grown Customer Relationship Manager via a backing MySQL database. Herein lies todays post: how the heck do you get a dotnet application's name, from within the application?
This might seem like an odd request. If you've done any level of shell-fu, such as using git
, you may have seen what is known as a "usage message." Here's a portion of what you see when you run git from the command line with no arguments:
usage: git [-v | --version] [-h | --help] [-C <path>] [-c <name>=<value>]
[--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
[-p | --paginate | -P | --no-pager] [--no-replace-objects] [--bare]
[--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
[--super-prefix=<path>] [--config-env=<name>=<envvar>]
<command> [<args>]
Now, you could hardcode this string of information. In most circumstances, that should work just fine. Suppose someone renames the program though? Perhaps they want two different versions of the same program. This usage message, if hardcoded, could be misleading. It's a contrived example, granted, but there is value in detecting the running name of the binary file. Consider busybox for a more practical example. In embedded systems leveraging busybox, the common linux commands are all symbolic links to the busybox
command. Those links determine which command busybox actually executes.
So how do we achieve this with C#? Recall a typical entrypoint looks something like the following:
internal static class Program
{
public static void Main(string[] args)
{
// do something here..
}
}
The args
array happens to contain all of the arguments passed to our program, but unlike in a C or C++ program, we don't have the program's name as the first item! Instead, there are a few approaches.
NB: Since C# 10 you don't actually need to explicitly define the Program class nor the Main method thanks to top-level statements. For quick and dirty fiddling this is a great feature. I prefer the consistency defining these elements brings.
AppDomain.CurrentDomain.FriendlyName
internal static class Program
{
public static void Main(string[] args)
{
Console.WriteLine($"Usage: {AppDomain.CurrentDomain.FriendlyName}");
}
}
This retrieves the "friendly name" of the program. However, this does not reflect the running name of the program if you rename it!
Process.GetCurrentProcess().ProcessName
using System.Diagnostics;
internal static class Program
{
public static void Main(string[] args)
{
Console.WriteLine($"Usage: {Diagnostics.Process.GetCurrentProcess().ProcessName}");
}
}
This retrieves the name of the process, which for the current process should be the running name of the program. However, on a mac with .NET 8 the name was truncated!
If you go this route, don't forget to include that import of System.Diagnostics via using System.Diagnostics
of course.
Path.GetFileName(Environment.GetCommandLineArgs()[0])
internal static class Program
{
public static void Main(string[] args)
{
Console.WriteLine($"Usage: {Path.GetFileName(Environment.GetCommandLineArgs()[0])}");
}
}
Now we're talking! Environment.GetCommandLineArgs() returns an array of strings similar to argv in C or C++. The first element is the fully-qualified path to the running program. To get a more concise version for display, simply pass it into Path.GetFileName
and presto! A consistent usage string for your command line programs.