在C#中随机抛出的IO异常

3
我一直在为我所工作的公司开发一个程序,用于存档旧客户数据。该程序将数据从工作服务器复制到本地机器,创建所有数据的.zip文件,然后将其复制到存档服务器。
完成上述步骤后,程序会删除原始文件和本地副本。偶尔,程序会出错,因为它无法删除我的计算机上的本地副本。它不会在每个不同的文件夹压缩之后出错。它会在处理300个文件夹或5个文件夹后出错。它会抛出以下三种错误之一:“目录不为空”,“文件正在被另一个进程使用”或“拒绝访问文件”。我尝试过将文件属性设置为正常,使用强制垃圾回收,并手动结束WinZip进程。
我真的不明白为什么有时会这样。我是计算机管理员,应该能够删除这些文件。我认为还有其他东西在使用它,但除了Visual Studio中的程序外,我的机器上不应该有其他东西在使用它。谢谢。
以下是清理方法和压缩文件的方法。
[MethodImplAttribute(MethodImplOptions.NoInlining)]
    static void CleanUp(SqlConnection Connection, string jNumber, DirectoryInfo dir, bool error, string prefix)
    {
        if (!error | (!error & emptyFolder))
        {
            try
            {
                SqlCommand updateJob = new SqlCommand(string.Format("update job set archived = 1 where job = {0}", jNumber), sdiConnection);
                updateJob.ExecuteNonQuery();
            }
            catch
            {
                WriteToLog("SQL Error: " + jNumber, "There was an error changing the archive bit to 1 after the job has been archived");
            }

            try
            {
                GC.Collect();
                GC.WaitForPendingFinalizers();
            }
            catch
            {
                WriteToLog("Error cleaning up after processing job", "There was an error garbage collecting.");
            }


            try
            {
                //path of the temporary folder created by the program
                string tempDir = Path.Combine(Path.Combine(System.Environment.CurrentDirectory, "Temp"), jNumber);
                //path of the destination folder
                string destDir = Path.Combine(dir.ToString(), jNumber);
                //SetFileAttributes(tempDir);


                try
                {
                    File.Delete(tempDir + ".zip");
                }
                catch (System.IO.IOException)
                {
                    File.Delete(tempDir + ".zip");
                }
                try
                {
                    Directory.Delete(destDir, true);
                }
                catch (System.IO.IOException)
                {
                    Directory.Delete(destDir, true);
                }
                try
                {
                    Directory.Delete(tempDir, true);
                }
                catch (System.IO.IOException)
                {
                    Directory.Delete(tempDir, true);
                }

            }
            catch
            {

                WriteToLog("File Error: " + jNumber, "There was an error removing files and/or folders after the job has been archived. Please check the source server and destination server to make sure everything copied correctly. The archive bit for this job was set.");
                Directory.Delete(Path.Combine(System.Environment.CurrentDirectory, "Temp"), true);
                Directory.CreateDirectory(Path.Combine(System.Environment.CurrentDirectory, "Temp"));
            }
        }


static bool ZipJobFolder(string jNumber, string jPath)
    {
        try
        {
            string CommandStr = @"L:\ZipFiles\winzip32.exe";
            string parameters = "-min -a -r \"" + jNumber + "\" \"" + jPath + "\"";

            ProcessStartInfo starter = new ProcessStartInfo(CommandStr, parameters);
            starter.CreateNoWindow = true;
            starter.RedirectStandardOutput = false;
            starter.UseShellExecute = false;

            Process process = new Process();
            process.StartInfo = starter;
            Console.WriteLine("Creating .zip file");
            process.Start();
            process.WaitForExit();

            Process[] processes;
            string procName = "winzip32.exe";
            processes = Process.GetProcessesByName(procName);
            foreach (Process proc in processes)
            {
                Console.WriteLine("Closing WinZip Process...");
                proc.Kill();
            }

        }
        catch
        {
            WriteToLog(jNumber, "There was error zipping the files of this job");
            return false;
        }
        return true;
    }

3
如果我不得不猜的话...是反病毒软件在持续扫描吗? - Marc Gravell
1
通常情况下,一般性的 catch 语句是一个不好的主意。如果你已经捕获了你知道的特定异常,为什么还需要额外的层级呢? - ChrisF
@Marc Gravell - 我不知道我是否会直接去找反病毒软件,但我的猜测是相同的:由于某种原因,文件系统没有释放要删除的文件/文件夹。 - AllenG
1
另外,关于 "MethodImplOptions.NoInlining" - 需要滚动条的东西似乎不太可能被内联... - Marc Gravell
我同意其他人的看法 - 有些东西正在索引文件或者其他什么?Tortoise SVN、杀毒软件等。如果你能在它发生时抓住它,你可以使用Sysinternals工具来找出是什么锁定了它。 - Rob Goodwin
@ChrisF 我只是想捕捉任何发生的事情。我碰巧注意到我得到的所有异常都是IO异常。@Marc Gravell 反病毒软件可能是一个可能性。趋势科技确实进行了主动扫描,但我不知道它是否在复制文件时扫描文件。此外,“MethodImplOptions.NoInlining”这个东西来自一个教程,我试图解决问题。@Rob Goodwin 目前唯一可能阻止它运行的程序是趋势科技。我会尝试禁用它,看看情况是否有所改善。 - johns4
4个回答

1

我注意到在使用Windows资源管理器删除大量文件和子文件夹时会出现这种情况。但是等待一段时间后再次尝试删除,似乎就可以正常工作了。

因此,我一直认为这是操作系统的不稳定行为。

虽然这不是解决方案,但您可以尝试让应用程序在尝试删除这些文件之前暂停一小段时间,看看是否仍会出现错误。

如果错误消失了,那么似乎与某些时间问题有关。但我自己想知道问题的根源。

评论者指出可能是反病毒程序导致的。如果是这样,那么您需要编写一些代码,在尝试删除之前检查文件是否被锁定。如果被锁定,则暂停一段时间,然后再次检查,直到它不再被锁定,然后才能继续删除。

您只需要小心不要陷入无限竞争状态。

编辑: 有一个相关的问题如何最好地等待文件锁定释放,可以查看一下以获取更多想法。

编辑2: 这里是另一个可能的解决方案在 .Net 中等待文件解锁


谢谢你的建议。我已经尝试在WinZip完成后让线程休眠。我让它休眠了5秒钟,但似乎没有什么效果。 - johns4
@johns4你试过完全禁用你的杀毒软件来排除这种可能性吗? - 7wp
这就是我现在正在处理的事情。它受到密码保护,我正在尝试找到它。 - johns4
看看我的修改,可能更容易的方式就是等待文件不再锁定,然后获取文件的句柄。由于无法预测什么会锁定该文件,可能是防病毒软件、Windows索引器、Windows资源管理器、其他进程等等。 - 7wp

1

很可能您遇到了共享冲突 - 删除无法获取独占文件句柄。其中一种情况是AV软件被触发并且在删除调用之前扫描未完成。还可能是WinZip进程尚未完全停止。

处理它的几种方法: 1)失败时,休眠几秒钟后再尝试。 2)我可能不会使用WinZip,而是使用ZipStorer(http://zipstorer.codeplex.com/)。它将在过程中压缩文件,您无需执行“kill”步骤,而且可以更细粒度地控制。您还可以通过启动多个线程并行执行压缩。


我试图让线程休眠几秒钟,但是没有任何变化。谢谢提供链接。我使用WinZip的唯一原因是我的一个同事已经有了它的代码,并建议我尝试一下。 - johns4

0

我发现有一个方法可以帮助解决问题,那就是不要试图在单个File.Move或File.Copy调用中创建临时文件和目录。相反,手动创建父目录,从最高级别开始向下工作。最后,在所有父目录存在时,移动或复制文件。


0

杀毒软件可能会成为一个问题,因为如果杀毒软件正在读取您的文件,则会导致该文件无法访问。说实话,当我使用.NET框架时,我经常看到这种弹窗,我只是将处理放在循环中,并尝试执行所需的任何文件操作,直到不再抛出异常为止。 如果正在复制文件或正在注册内核缓冲区中的文件(如果实现了某种观察器),也会发生这种情况。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接