进程无法访问该文件,因为它正在被 Directory.Move() 上的另一个进程使用

发布于 2024-12-27 17:13:56 字数 3100 浏览 4 评论 0原文

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

当按照描述执行此过程时,第一次调用 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);
}

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

疏忽 2025-01-03 17:13:56

感谢 Adam Caviness 的回答,我们才得以弄清楚。

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

将进程转换为 Windows 服务不会创建 .vshost 文件并解决了此问题。

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.

梦魇绽荼蘼 2025-01-03 17:13:56

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文