views:

785

answers:

3

I have some UI application that lives in the user's task bar that is written in C#. The EXE for the tool is checked in to our source control system on a number of projects that use it so we are able to update the version they run with by checking in updated EXE.

The problem is that when the users get the latest revision of the exe, the program is often running, and the sync fails on their machine. I want to fix it so the program doesn't lock the exe and any dependent DLL's when it runs so they can sync without having to shut down the program.

Currently, I have a program that takes an executable as a parameter and will launch it from memory by reading the assembly contents into memory ahead of time. Unfortunetly, this totally fails when it comes to the DLL's that the program requires.

The code I have right now looks something like this:

public class ExecuteFromMemory
{
 public static void Main(string[] args)
 {
  //Figure out the name of the EXE to launch and the arguments to forward to it
  string fileName = args[0];
  string[] realArgs = new string[args.Length - 1];
  Array.Copy(args, 1, realArgs, 0, args.Length - 1);

  //Read the assembly from the disk
  byte[] binary = File.ReadAllBytes(fileName);

  //Execute the loaded assembly using reflection
  Assembly memoryAssembly = null;
  try
  {
   memoryAssembly = Assembly.Load(binary);
  }
  catch (Exception ex)
  {
   //Print error message and exit
  }

  MethodInfo method = memoryAssembly.EntryPoint;
  if (method != null && method.IsStatic)
  {
   try
   {
    method.Invoke(null, new object[] { realArgs });
   }
   catch(Exception ex)
   {
    //Print error message and exit
   }
  }
  else
  {
   //Print error message and exit
  }
 }
}

My question is, am I doing something totally stupid? Is there a better way to handle this? If not, what should I do to support handling external dependencies?

For example, the above code fails to load any dependent files if you try to run 'Foo.exe' that uses functions from 'Bar.dll', the 'Foo.exe' will be overwriteable, but 'Bar.dll' is still locked and can't be overwritten.

I tried getting the list of referenced assemblies from the 'GetReferencedAssemblies()' method on the loaded assmebly, but that doesn't seem to give any indication where the assemblies should be loaded from... Do I need to search for them myself? If so, what's the best way to do this?

It seems like other people might have come across this before, and I don't want to re-invent the wheel.

- Update: The EXE is checked in because thats how we distribute our in-house tools to the teams that use them. Its not optimal for this use-case, but I don't have the opportunity to change that policy.

A: 

Does the program need to keep running while updating?

Typically to update a program which is running you would copy over any of the files that are to be replaced to a temporary folder. Then shut down the old instance, delete it and move the new files over to the correct locations then re-launch it.

This allows for minimal down time of the application since the longest part is usually the copy and the file move is very fast if the temporary folder is on the same logical drive.

Ben S
+3  A: 

Disclaimer: I don't use Windows, though I am familiar with its strange way of locking things.

In order to update your application while it is running, you'll likely need to have two processes: The executable itself, and an update “helper” application that will finish the update process. Let's say that your application is ProcessA.exe and your update helper is Updater.exe. Your main program will download a new copy of the executable, saving it with a random name. Then you run your updater program, which watches for the termination of your current process. When your process terminates, it displays a quick window showing the status of the update, moving the new executable into the place of the old one, and then restarting that program.

It'd be more elegant to be able to emulate POSIX filesystem semantics and be able to delete the currently-running process disk image and replace it with a new file, but I don't know if that is even possible on Windows. On a POSIX system, you can delete an in-use file and it won't actually be deleted until any remaining file handles are closed, though you can then reuse the filename.

You might want to check out an article written at CodeProject that talks about this. It also has a follow-up article.

Good luck!

Michael Trausch
A: 

Although Michael's answer is one way of doing this, there are tools out there that are explicitly for managing what's installed on the desktop.

What you are doing with the exe being checked into source control is not normal. If you have a windows domain controller, you can use Group Policy to push programs down to the client. Alternatively, you could use something like Altiris to handle it.

If you must continue the way you are going then you have two options. One, using a helper / loader app which does a version check on launch. This is similar to how firefox works.

The second way is to build a helper service that sits in memory and polls every so often for updates. This is how Google Chrome, Adobe, etc work.

Chris Lively