Many applications nowadays automatically update themselves to the latest version when you launch them. Sometimes they ask (like Minecraft) and sometimes they don't (like Google Chrome). By doing this, you don't have the problem of having a large portion of your users sporting a version that is horribly ancient/insecure/not-supported/et-cetera and there's nothing you can do about it (like Internet Explorer).
So how do you make such an application? In .NET, you have the ability to dynamically load assemblies. Most applications have a main method where execution begins. But instead, consider if you have a small launcher application, a DLL file containing your actual application, and a text file containing the version number of the DLL. Over on your web server, you have three files. The launcher application (that users will download), a page that shows just the version number of the latest available version (this URL is used by the launcher), and you have the latest DLL available (which the launcher is also aware of).
The launcher will open the version number file to see which version is present on your computer. Then it'll go to your web server to check the latest available version number. If the versions don't match, then that means the binaries are out of date and a newer version ought to be downloaded.
After downloading the newer assembly, the older assembly is replaced and the launcher app loads the assembly present in the current directory (which is now the new one). And all is well.
Why not use System.Reflection to check the DLL directly for the version number?
Because in order to check the version number, the assembly must be loaded. If it turns out that the assembly is not the latest version and needs to be replaced, then you won't be able to replace the file after downloading the newer version because the CLR instance is holding on to a reference to that file. That's why a text file is used to save the version number. An alternate approach would be to use a registry key. However, if the user deletes your program and then downloads it and runs it later, then you'll have the problem of a stale registry making it think the latest version is already downloaded. A way to get around this is to check the registry for the latest version AND to check to see if a file is present. Both are valid approaches. There is a way to unload an assembly and get around this whole mess but that requires using multiple AppDomain instances and that's a lengthy exercise for another day.
What if I need multiple files to be downloaded?
You can extend this methodology any way you want. For example, instead of a page that shows the latest version number, perhaps it shows a page with the version number and a manifest of files to be downloaded. Or you can have it download a zip file instead of a DLL. Or use embedded resources. This is just a simple example illustrating the general approach.
I've created an example Project that uses this infrastructure. In fact, the launcher is generic enough that you can just use this as is for your own project.
This is the launcher application. It does all the upgrade-checking and assembly-loading logic.
This is where your actual application resides. In the example app, it's just a window that shows some simple text. The output after building is the DLL you want to upload as your latest available version.
When you're developing your app and running/debugging locally, you don't want the auto-updater to run. This project references the Binaries project directly and launches it. Set this to your default starting project.