Home > database >  C# console application not running stand-alone after being published
C# console application not running stand-alone after being published

Time:01-05

I'm rather new to C# and Visual Studio.

The problem I have is after I publish my C# console app in Visual Studio. The console app is not handling the augments correctly and is throwing an error.

I have setup a few Launch Profiles to test out how the console app will behave with different arguments. They all pass and give me the expected results when I run the Debug and Release Profiles.

However, when I go to publish and I select self-contained and select Produce single file. (Published Profile Settings.) The app no longer works as expected.

I get the following error:

Generic Exception Handler: System.ArgumentNullException: Value cannot be null. (Parameter 'paths')
   at System.IO.Path.Combine(String[] )
   at AMP_New_Project.AMP_New_Project_Folder.Main(String[] args) in C:\Users\eriskedahl\Documents\GitHub\JMS\NewAMP_Proj_Console\AMP_New_Proj.cs:line 135
PS C:\Users\erisk\Documents\GitHub\JMS\NewAMP_Proj_Console\Releases>

Now the error itself is fine and looks like my the exception handling is working, but I have a debug profile to check this and the path is allowed to be null. There is an if statement to handle what happens when a path argument is not supplied.

if (strWorkPath == null || strWorkPath.Length == 0)
{
    strWorkPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)!;
}

It seems the console app is not self-contained and is missing some references but not sure where else to check. Why would the app behave differently after being published?

When I run debug, and no path is given, the the variable strWorkPath is null and then gets set to the console app's current working directory.

As mentioned this works completely fine and as expected when I run the app through the debug in Visual Studio, I only get this error when I run the published version. If I open a Powershell or cmd window and go to the Bin/Release folder and run the application from there I also get the expected results. The App fails once I copy the exe file to a different folder.

CodePudding user response:

When publishing as a single file, the build system bundles all of the assemblies which make up your application together.

The Assembly.Location docs say:

In .NET 5 and later versions, for bundled assemblies, the value returned is an empty string.

So we're passing an empty string to Path.GetDirectoryName, which says:

Directory information for path, or null if path denotes a root directory or is null.

So that's probably what's happening: Assembly.Location returns an empty string, because the assembly has been bundled together with a load of other assemblies as part of the publish, and Path.GetDirectoryName turns that into null.

To get the application's location in a way which supports bundling, use AppDomain.CurrentDomain.BaseDirectory.

Note that this is not the current working directory. The CWD is the directory that the user is in when they run your application. It's normally assumed that if the user passes a relative path to your application (e.g. as a command-line argument), the application should interpret that as relative to the user's CWD.

CodePudding user response:

Problem is not that you are using self-contained deployment but that you also using single-file deployment (one of the checkboxes).

Docs for single-file deployment and executable mention that some Assembly APIs will not work in single-file deployment mode, including the Location, which will return an empty string:

API Note
Assembly.CodeBase Throws System.PlatformNotSupportedException.
Assembly.EscapedCodeBase Throws System.PlatformNotSupportedException.
Assembly.GetFile Throws System.IO.IOException.
Assembly.GetFiles Throws System.IO.IOException.
Assembly.Location Returns an empty string.
AssemblyName.CodeBase Returns null.
AssemblyName.EscapedCodeBase Returns null.
Module.FullyQualifiedName Returns a string with the value of <Unknown> or throws an exception.
Marshal.GetHINSTANCE Returns -1.
Module.Name Returns a string with the value of <Unknown>.

Which leads to Path.GetDirectoryName returning null.

There are some workarounds mentioned:

  • To access files next to the executable, use System.AppContext.BaseDirectory

  • To find the file name of the executable, use the first element of System.Environment.GetCommandLineArgs, or starting with .NET 6, use the file name from System.Environment.ProcessPath.

  • To avoid shipping loose files entirely, consider using embedded resources.

So you can use System.AppContext.BaseDirectory to determine the directory.

  • Related