【问题标题】:Process cannot access the file because it is being used by another process on Directory.Move()进程无法访问该文件,因为它正被 Directory.Move() 上的另一个进程使用
【发布时间】:2012-02-11 08:36:13
【问题描述】:

我们有一个具有以下架构的客户端应用程序:一个管理器进程管理几个工作进程(读取器和写入器)并定期查询服务器以获取版本更新。如果版本更新可用,则管理器将其下载到客户端计算机,关闭工作线程,启动更新程序进程以处理更新并退出。更新程序在启动时接收管理器 PID 和更新文件位置;然后等待 manager 退出,备份 manager 和 workers 的所有文件,重新创建他们的目录并将新版本文件传播到新目录。

当按照描述执行此过程时,第一次调用 Directory.Move(string, string)(用于备份管理器目录)会引发 IOException。奇怪的是,如果我让管理器在不启动更新程序的情况下关闭,然后自己启动更新程序可执行文件,则不会抛出异常。

用于管理工作线程的管理器代码:

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();
}

查询数据库的管理器代码:

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();
}

用于启动更新进程的管理器代码:

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();
}

等待管理器关闭的更新代码:

bool isManagerUp = true;

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

     managerProcess.WaitForExit();

     managerProcess.Dispose();

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

更新模块的更新代码:

//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);
}

【问题讨论】:

  • 请显示 MakeBackupDirectoryFullPath 方法代码
  • @MrMush:吞下带有空 catch 块的异常会使调试变得更加困难。例如,在您的 Updater 代码中,isManagerUp 变量是完全多余的,因为无论发生什么都将其设置为 false。
  • 1 - 您何时调用 StartUpdater ?在while循环之后?无论如何,请相信消息:您的目录仍然有锁定访问权限。这意味着您之前的进程肯定仍在使用管理器目录。我的猜测是,当您调用 StartUpdater 时 managerProcess 尚未完成,或者该目录仍在以某种方式被另一个进程使用。您应该使用锁仔细调试。
  • 2 - 截至您提到的使用工作线程的另一个进程仍在以某种方式使用该目录,我敢打赌,当您已经启动更新程序时,它们还没有完成。一个简单的检查是在调用更新程序之前放置一个 Thread.Sleep(10000),它可能会起作用
  • @Groo:这只是我的代码的简化版本,虽然它很长,但我不想用不必要的代码让你厌烦。

标签: c# .net wcf process ioexception


【解决方案1】:

感谢Adam Caviness 的回答,我们能够弄清楚。

我们的进程是控制台应用程序,他们创建了一个 .vshost 文件,这些文件在进程被命令终止后继续工作。 尝试使用正在运行的 .vshost 文件移动目录会导致问题。

将进程转换为 Windows 服务并没有创建 .vshost 文件并解决了这个问题。

【讨论】:

    【解决方案2】:

    我建议您使用 MS(正式为 SysInternals)Process Monitor 来追踪此问题,因此首先排除任何反病毒/反恶意软件/启发式(如果您不像我们的开发人员那样去突击队)。让我指出这个方向的线索是,您可以自己启动更新程序并且不会引发异常。就在今年,我已经遇到了这个问题,不得不添加一个 AV 目录排除。

    【讨论】:

    • +1 感谢您的回答,我们找到了问题所在。
    猜你喜欢
    • 2010-12-10
    相关资源
    最近更新 更多