3

I am creating a software application that is able to update itself. After the start, the application checks if there are updates avaiable, download those files (assemblies), and then proceeds loading them.

However, the problem is when the application is run by multiple users at the same time. This happens when running on a terminal server. The application can't replace those old files, since windows keeps them locked.

Is there a simple way to solve this problem? It's a legacy application, and I don't have the time to alter big parts of the application or the update mechanic.

Greg B
  • 14,597
  • 18
  • 87
  • 141

4 Answers4

3

Well, Windows does allow you to rename the respective files even though they are in use. So you could rename to updated files, replace them with the new version and restart the application.

I guess you won't be able to solve this without changing the update mechanism.

Thorsten Dittmar
  • 55,956
  • 8
  • 91
  • 139
  • I can't just 'restart the application'. The files are locked due to other users running the application at the same time on a terminal server, like a said in my question. –  Jun 08 '12 at 08:15
  • 1
    Yes you can. Updates are being installed as the user runs the application, right? So there are X instances running the old version and one being updated by renaming the existing files and copying the new versions to the installation folder. If the application then calls `Application.Restart()` the one user uses the new version of the application and the DLLs, while the other users still use the old version until they restart the application. – Thorsten Dittmar Jun 08 '12 at 08:36
3

Most of updater have a bootstrapper executable that performs the update of the main application.

The idea is to run the bootstrapper instead of your application. Then the bootstrapper apply any update if required, before launching the actual application.

Another technique (I never tried, it's only a clue) is to use shadow assemblies. The concept is to "duplicate" the assembly on the file in order to avoid file in use locking.

Finally, you can take a look at clickonce which can easily create self updating applications, if you accepts the drawbacks of this mechanism.

Steve B
  • 36,818
  • 21
  • 101
  • 174
  • I can't use some kind of bootstrapper, since the user has to log in first to get a connection to the database to check of updates. And even if this would be possible, the files are still locked by other users running the application at the same time. –  Jun 08 '12 at 08:17
  • Maybe clickonce can solve the problem, as the application is installed in the user profile. If not, shadowing assemblies in the only way to go I think – Steve B Jun 08 '12 at 08:19
  • I'll have a look at ClickOnce, but I am afraid you have to install the application for every user instead of only once? The problem is, the application is a legacy application, already being run by several dozen customers, so there's no room for big changes like this... –  Jun 08 '12 at 08:24
1

A simple solution would be to use Shadow Copying.

Simple example:

class Program
{
    static void Main(string[] args)
    {
        var x = AppDomain.CreateDomain("TestAssembly", null, new AppDomainSetup() { 
                                                            ShadowCopyFiles = "true",
                                                            CachePath = @"c:\tmp", 
                                                            ApplicationName = "ShadowCopyTest"
                                                       });
        var a = x.Load("TestAssembly"); // Load Assembly and run...
    }
}

You could create an executable that would load your application (your executable users are starting right now) into a new Application Domain using Shadow Copying. CachePath has to be user specific, e.g. the users temp directory.

This way, each user would create a copy of all assemblies loaded by your application. But you have to take care of cleaning up the copied files yourself.

All you have to do then is to ensure the application gets started by your new wrapper executable.

Community
  • 1
  • 1
sloth
  • 99,095
  • 21
  • 171
  • 219
  • I did a quick check, and it seems to work. I will need to test further, but Shadow Copying seems promising... –  Jun 08 '12 at 08:25
  • Thanks, I think that will be a sensible approach! –  Jun 08 '12 at 08:33
0

I guess you are downloading assemblies directly in place of the old assemblies. Lets say you have an assembly myprogram.dll . First download the new updated dll with a different name ( _myprogram.dll for example) . Fire an event after the download is competed that will replace the myprogram.dll with _myprogram.dll. This event should declare all running processes to stop at first then replace the assemblies. replacement should take at a moment. You cannot avoid the service cut on this moment.

EDİT:

There should be a process that will always running and checking for updates. Send the name of the files to this process first. The process will now it will download for example 5 files. The process should download the files in a predefined name format(Concatenating it with underscore for example). After the process finishes downloading 5th file the process should kill all other processes ( or preferably only the processes related to the downloaded assemblies), then replace the assemblies with the newer ones. Than start the processes again.

Ozgur Dogus
  • 911
  • 3
  • 14
  • 38
  • How should such an Event look like? So basically you are suggesting to run the application under an administrator account all the time, and when there's an update, just kill the processes of all the other users? I don't think that is anywhere near an possible option... –  Jun 08 '12 at 08:14
  • Yeah, I will then tell our customers that we solved our updating issue on terminal servers by randomly killing processes will users are working with the application... –  Jun 08 '12 at 08:32
  • You cannot update an assembly without killing it. Your application will stop only as long as replacing two files through your code takes. – Ozgur Dogus Jun 08 '12 at 08:35