5

We have a client application with the following architecture: a manager process manages a couple of worker processes (reader and writer) and periodically queries the server for version updates. If a version update is available, the manager downloads it to the client computer, shuts down the worker threads, starts an updater process to handle the update and exits. The updater, on startup, receives the manager PID and the update file location; it then waits for the manager to exit, backs up all files of the manager and workers, recreates their directories and spreads the new version files to the new directories.

When going through this process as described, the first call to Directory.Move(string, string) – which serves to back up the manager directory – throws the IOException. The strange thing is, if I let the manager shut down without starting the updater and then start the updater executable myself, the exception is not thrown.

Manager code for managing worker threads:

public void Run()
{
   _config = GetConfiguration();
   Process reader, writer;

   //Start reader and writer with appropriate arguments

   //Keep reader and writer alive

   reader.Kill();
   writer.Kill();

   reader.WaitForExit();
   writer.WaitForExit();

   reader.Dispose();
   writer.Dispose();
}

Manager code for querying the database:

EndpointAddress endpoint;
BasicHttpBinding httpBinding = new BasicHttpBinding();
httpBinding.MaxReceivedMessageSize = 2000000000;
ChannelFactory<IService> chanFactory = new ChannelFactory<IService>(httpBinding);
IService service;

try
{
   endpoint = new EndpointAddress(ConfigurationManager.AppSettings["Service URL"]);

   service = chanFactory.CreateChannel(endpoint);

   UpdateInstructions instructions = service.GetUpdateInstructions(_config.SiteID,     Assembly.GetExecutingAssembly().GetName().Version.ToString(), _config.Version);

   HandleUpdateInstructions(instructions); //Downloads files and starts the updater process
}
catch (Exception ex)
{
   //Report exception
}
finally
{
    if (chanFactory.State != CommunicationState.Faulted)
    chanFactory.Close();
}

Manager code for starting the updater process:

private void StartUpdater(string updateFilePath, string configFilePath)
{
   ProcessStartInfo updaterStartInfo = new ProcessStartInfo(_config.UpdaterExePath, string.Format("{0} \"{1}\" \"{2}\"", Process.GetCurrentProcess().Id, updateFilePath, configFilePath));
   Process updater = Process.Start(updaterStartInfo);
   updater.Dispose();
}

Updater code for waiting for the manager to close:

bool isManagerUp = true;

while (isManagerUp)
{
  try
  {
     Process managerProcess = Process.GetProcessById(bDoxForceManagerPID);

     managerProcess.WaitForExit();

     managerProcess.Dispose();

     isManagerUp = false;
  }
  catch
  {
     isManagerUp = false;
  }
}

Updater code for updating a module:

//updateDirectory is the directory of the new files to be inserted, moduleDirectory is the working directory of the module that will be updated, in this case the manager
private void UpdateModule(DirectoryInfo updateDirectory, DirectoryInfo moduleDirectory)           
{
   string backupDirectory = MakeBackupDirectoryFullPath(moduleDirectory.Parent.FullName);

   Directory.Move(moduleDirectory.FullName, backupDirectory); // IOException as described above.
   Directory.CreateDirectory(moduleDirectory.FullName);

   foreach (FileInfo updateFile in updateDirectory.EnumerateFiles())
   {
       string newFilePath = moduleDirectory.FullName + "\\" + updateFile.Name;

       File.Copy(updateFile.FullName, newFilePath);
   }

   Directory.Delete(updateDirectory.FullName, true);
}
Mr Mush
  • 1,538
  • 3
  • 25
  • 38
  • 1
    Show MakeBackupDirectoryFullPath method code too plz – Arsen Mkrtchyan Jan 16 '12 at 11:35
  • 3
    @MrMush: swallowing exceptions with empty `catch` blocks makes it harder to debug. For example, in your Updater code, `isManagerUp` variable is completely redundant, since you set it to false no matter what happens. – vgru Jan 16 '12 at 11:40
  • 1
    1 - When are you calling StartUpdater ? After the while loop ? Anyways, trust the message : there is still a lock access to your directory. Which means that the manager directory is still in use for sure by your previous process. My guess is either that managerProcess is not done yet when you call StartUpdater, or the directory is still in use in some way by another process. You should debug this carefully, using locks. – Mehdi LAMRANI Jan 16 '12 at 12:00
  • 1
    2 - As of The directory still being in use in some way by another process you mentioned using worker threads, I bet that they are not finished yet when you are already starting your updater. A simple check is to put a Thread.Sleep(10000) before calling the updater, Chances are it might work – Mehdi LAMRANI Jan 16 '12 at 12:02
  • 1
    @Groo: This is merely a simplified version of my code, and quite long as it is, I didn't want to bore you with unnecessary code. – Mr Mush Jan 16 '12 at 13:02
  • 1
    any final source code working? – Kiquenet Aug 20 '13 at 18:49
  • 1
    @Kiquenet: As you can see in my answer, it was a development environment issue, not a code problem. Solved by turning the processes to Windows services. – Mr Mush Aug 21 '13 at 07:26

2 Answers2

4

Thank to Adam Caviness answer we were able to figure it out.

Our processes were Console applications, they created a .vshost files that kept on working after the processes were order to terminate. Attempting to move the directory with the running .vshost files caused the problem.

Turning the processes into Windows services didn't create a .vshost files and solved this issue.

Community
  • 1
  • 1
Mr Mush
  • 1,538
  • 3
  • 25
  • 38
3

I suggest you use MS (formally SysInternals) Process Monitor to track this down and thus first rule out any anti-virus/anti-malware/heuristics (should you not be going av commando like we devs do). The clue to that makes me point you in this direction is that you can start the updater yourself and the exception is not thrown. Just this year already I've ran into this issue and had to add an AV directory exclusion.

Adam Caviness
  • 3,424
  • 33
  • 37