Publishing A Single EXE File In .NET Core 3.0

Say I have a simple “Hello World” console application that I want to send to a friend to run. The friend doesn’t have .NET Core installed so I know I need to build a self contained application for him. Easy, I just run the following command in my project directory :

Pretty self explanatory :

  • Publish the project from the current directory
  • Build the project to run on Windows 64 bit machines
  • Build in release configuration mode
  • Publish everything as “self-contained” so that everything required to run the app is packaged up with our executable

So this works right, we end up with a folder that has our exe and everything that is required to run it, but the issue is that there is a tonne required to run even a HelloWorld console app.

All up it’s a little over 200 files. Crazy – but it makes sense. It’s essentially having to package the .NET Core runtime just to run Hello World.

So functionally, this works. But optics wise, it looks like a mess. I’ve sent folders like this to clients and had to say “uhh.. So I’m going to send you a folder with hundreds of files in it… But… Can you just find the one titled MyApplication.exe and run that and don’t worry about the rest?”. When people are used to having an icon on their desktop they double click and things just.. work… This just doesn’t cut it.

You Need .NET Core 3.0 (Preview 5+)

I need to put that in bold because this will not work if you don’t have .NET Core 3.0. And if you are an early adopter (At the time of this post .NET Core 3.0 is not GA yet), you will need atleast preview 5 upwards. If you’re not one to mess around in preview’s, then you can just wait a few months till .NET Core 3.0 is released!

The PublishSingleFile Flag

All that intro and it literally comes down to a single command flag :

All this does is runs our publish command but tells it to package it within a single file. You’ll notice that we no longer specify the self-contained flag. That’s because it’s assumed that if you are packaging as a single exe, that you will want all it’s dependencies along with it. Makes sense.

And the output :

A single tidy exe! When this is executed, the dependencies are extracted to a temporary directory and then everything is ran from there. It’s essentially a zip of our previous publish folder! I’ve had a few plays around with it and honestly, it just works. There is nothing more to say about it. It just works.

Helpful Tip : Make sure you clean your publish directory or just outright delete your bin folder. It doesn’t break anything to not do so, but all those old DLL’s just hang around until you do so your nice single EXE is hard to spot. 

File Size And Startup Cost

Keen eyes will notice something about the above screenshot. The file size. It’s over 70MB! That’s crazy for an application that does nothing but print Hello World to the screen! This is solved in Preview 6 of .NET Core 3.0 with a feature called IL Linker or Publish trimmer that omits DLL’s that aren’t used. You can read more about that here!

The other issue you may find is that there is a slight startup cost when running the self contained executable for the first time. Because it needs to essentially unzip all dependencies to a temporary directory on first run, that’s going to take a little bit of time to complete. It’s not crazy (5 seconds or so), but it’s noticeable. Luckily on subsequent runs it uses this already unzipped temp folder and so startup is immediate.

ENJOY THIS POST?
Join over 3.000 subscribers who are receiving our weekly post digest, a roundup of this weeks blog posts.
We hate spam. Your email address will not be sold or shared with anyone else.

7 comments

  1. Wade,
    Do you know where does the temp directory get created? We have been running into problems of missing dependencies and unnecessarily modified or removed files from the installation folder because customers didn’t know. This approach will help us considerably. However if the temp directory location is locked down, which it is via group policies on Enterprise networks, then this would become another battle. Similar to ClickOnce application. Anyway, I was just curious about it after reading your post. Very informative post.

    1. Honestly. No. And I did have a quick search for it both on my PC and in all documentation. It says “A” Temp directory but not “THE” Temp directory which made me think it will be something in your user profile or similar. I’ll keep looking and let you know.

    2. The easiest way to figure out these kind of details is by using procmon from sysinternals (basically dtrace for Windows)

      Run it as admin, then start the program and check the file open kernel calls.

  2. Thanks for writing this up. Super helpful. I struggled for hours with dotnet-warp and fody, presumably due to compatibility issues because I’m on the preview dotnetcore sdk. This just worked, first shot. Yeah, the binary is enormous, but that too will be solved. This bodes well for the future of .net core on the desktop.

  3. Hi Wade

    Thanks for writing the article. This is very very useful. However, when I try to run the exe I get the following error: “The application to execute does not exist: myapp.dll”. Did you encountered this as well? And if so, how you solved it?

Leave a Reply

Your email address will not be published. Required fields are marked *