12

I have a .exe assembly and a set of referenced assemblies that I would like to be able to deploy updated code for. I don't need to actually change the running code, but the next time I launch the executable I would want it to pick up the updated code. Currently when I attempt to copy over the running file I get an error about the .exe being used by another process.

To summarize; how can I update the code while the .exe is in use?

Michael Hale
  • 1,367
  • 13
  • 16
  • There seems to be a project on Codeplex aiming on hot deploy for .NET. I haven't tried it though: http://hotdeploy.codeplex.com/ – Dirk Vollmar Feb 25 '10 at 16:48

9 Answers9

21

It is easy to do. You can rename the file, Windows has a lock on the handle, not the directory entry for the file. Now you can just copy the update without problems. All that's left to do is to get rid of the renamed file after your app starts up again. If necessary.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • 1
    In addition to your answer, you may want to note that the user might need to detach from the debugging process first before renaming the file, otherwise the IDE (i.e. Visual Studio) won't allow the source to be edited. – Andrew Mao Mar 18 '14 at 18:00
  • it is bad solution because you will be getting damaged shortcuts after renaming. You should use helper application instead. – l0pan Feb 28 '17 at 22:52
  • Hmm, no. Please upvote the "I don't think this is possible" answer, everybody understands that one. – Hans Passant Feb 28 '17 at 22:55
  • 1
    I upvoted here http://stackoverflow.com/a/18079191/2954142 I spent several hours debugging the issue caused by updating by renaming. Windows 10 pinned shortcut keeps referring to the old renamed application while new version of the application already existed. – l0pan Feb 28 '17 at 23:07
  • You completely missed the point, it is not renamed. The goal here is to get a file *with the same name* in the same directory, so shortcuts don't break, even though it is in use. You do so by renaming the old one so you can get copy the new one, *with the same name*. – Hans Passant Feb 28 '17 at 23:17
  • I see. But the problem is that Windows may automatically update shortcuts after you rename their target. The shortcuts are damaged once you “get rid of the renamed file” or their target linked to the old version if you keep renamed file. I saw this in production and I couldn’t investigate deeper how it could happen. – l0pan Feb 28 '17 at 23:29
  • The auto-updating shortcut event occurs when opening the *.lnk file. If the original filename no longer exists, then windows will try to figure out what happened to it and update the *.lnk to link to the new location. However, if you do the renames quickly enough, then the new *.exe will be in place before anyone tries to open the *.lnk and there will be no issue. Just don't do any processing between the renaming of the old file out of the way and the renaming of the new file into place. – Rhaokiel Mar 19 '21 at 12:12
3

I don't think this is possible. For example, when deploying asp.net applications with zero downtime, best practice is to have a load balancer so you can take down one instance, update it, then take down the other for update.

Joel Martinez
  • 46,929
  • 26
  • 130
  • 185
2

You can't update the assembly when it's in use. The best option for this type of situation is to make a small executable which does a shadow copy of your assemblies, and launches them from a new location.

This way, when the user launches the program, you can shadow copy (locally) from the deployment site, which can always be overwritten.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
2

ClickOnce gives you some options. There is an update strategy of "update after application startup."

For more complex scenarios, there is the System.Deployment namespace. For example, you could periodically poll for updates in your application.

Austin Salonen
  • 49,173
  • 15
  • 109
  • 139
2

This class will rename the currently running executable, if it completes without exception, you can simply write the new executable, then relaunch, eg:

Ourself.Rename();
// Download or copy new version
File.Copy(newVersion, Ourself.FileName());
// Launch new version
System.Diagnostics.Process.Start(Ourself.FileName());
// Close current version
Close(); // Exit();

Easy enough?

class Ourself
{
    public static string FileName() {
        Assembly _objParentAssembly;

        if (Assembly.GetEntryAssembly() == null)
            _objParentAssembly = Assembly.GetCallingAssembly();
        else
            _objParentAssembly = Assembly.GetEntryAssembly();

        if (_objParentAssembly.CodeBase.StartsWith("http://"))
            throw new IOException("Deployed from URL");

        if (File.Exists(_objParentAssembly.Location))
            return _objParentAssembly.Location;
        if (File.Exists(System.AppDomain.CurrentDomain.BaseDirectory + System.AppDomain.CurrentDomain.FriendlyName))
            return System.AppDomain.CurrentDomain.BaseDirectory + System.AppDomain.CurrentDomain.FriendlyName;
        if (File.Exists(Assembly.GetExecutingAssembly().Location))
            return Assembly.GetExecutingAssembly().Location;

        throw new IOException("Assembly not found");
    }

    public static bool Rename()
    {
        string currentName = FileName();
        string newName = FileName() + ".ori";
        if (File.Exists(newName))
        {
            File.Delete(newName);
        }
        File.Move(currentName, newName);
        return true;
    }
}
Orwellophile
  • 13,235
  • 3
  • 69
  • 45
1

Just an idea: Try MEF and [Import["http://someRemoteResource"]]

Robert
  • 1,466
  • 10
  • 25
1

What about the following:

  1. Deploy the exe to an update folder.
  2. Whenever the app starts up, it would check the update folder.
  3. If its not empty, execute a copy program
  4. The copy program would then replace the existing exe with the one in the update folder
  5. Delete anything in the update folder
  6. Then relaunch the exe
FallenAvatar
  • 4,079
  • 1
  • 21
  • 24
1

The best idea would be one of the other answers already suggested... like using recomposition with MEF and or ClickOnce. However, those solutions won't help you for "this deploy". They require you to make changes to the exe and or create a boot strap executable, which will only help you for the next deploy.

For this deploy you can try doing this (I've never done this before, but theoretically it could work):

  1. Copy your new dll's to a subfolder somewhere
  2. Add a command line xcopy command to the RunOnce registry key to copy the new dll from the subfolder to the final exe folder where you want it to go.
  3. Reboot.

The RunOnce key contains command line commands which are Run Once on reboot, and then removed from the registry so they don't run again. This is how InstallShield allows you to overwrite certain dll's while they are in use by other applications.

Nick
  • 5,875
  • 1
  • 27
  • 38
0

It is possible if you want to replace an assembly you have updated before. I have an application where the user is able to download a new HtCore.dll. After download, I rename it to HtCore.dll_new. On application start, the libraries get replaced, see below:

Modify the constructor of your App.cs

public App()
{
    try
    {
        var htCore = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "HtCore.dll");
        if (File.Exists($"{htCore}_new"))
        {
            File.Copy(htCore, $"{htCore}_backup", true);
            File.Delete(htCore);
            File.Move($"{htCore}_new", htCore);
            File.Delete($"{htCore}_new");
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}
Dominic Jonas
  • 4,717
  • 1
  • 34
  • 77