【问题标题】:Release resources used on a background worker c# WPF释放后台工作人员使用的资源 c# WPF
【发布时间】:2015-03-31 23:21:57
【问题描述】:

好的,我正在开发一个游戏启动器。它检查更新,如果存在,它会被下载并解压缩。解压缩完成后,将新文件复制到需要的位置,然后删除 zip 和解压缩的文件。

问题是这样的:如果用户在解压缩时关闭了启动器,下次他们启动它时,我在解压缩时收到错误 - 文件已经存在。

所以我想做的是在退出时删除 Patch 文件夹。但是,如果后台工作人员正在运行,则无法删除该资源,因为它正在被另一个进程使用。

下载器类:

static void downloader_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker worker = sender as BackgroundWorker;

        if (!Directory.Exists(Path.Combine(BASE_DIR, "Patch")))
            Directory.CreateDirectory(Path.Combine(BASE_DIR, "Patch"));

        string sFilePathToWriteFileTo = Path.Combine(BASE_DIR, "Patch", "patch.zip").ToString();

        // first, we need to get the exact size (in bytes) of the file we are downloading
        Uri url = new Uri(sUrlToReadFileFrom);
        System.Net.HttpWebRequest request = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(url);
        System.Net.HttpWebResponse response = (System.Net.HttpWebResponse)request.GetResponse();
        response.Close();
        // gets the size of the file in bytes
        Int64 iSize = response.ContentLength;

        // keeps track of the total bytes downloaded so we can update the progress bar
        Int64 iRunningByteTotal = 0;

        // use the webclient object to download the file
        using (System.Net.WebClient client = new System.Net.WebClient())
        {
            // open the file at the remote URL for reading
            using (System.IO.Stream streamRemote = client.OpenRead(new Uri(sUrlToReadFileFrom)))
            {
                // using the FileStream object, we can write the downloaded bytes to the file system
                using (Stream streamLocal = new FileStream(sFilePathToWriteFileTo, FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    // loop the stream and get the file into the byte buffer
                    int iByteSize = 0;
                    byte[] byteBuffer = new byte[1024];
                    double dTotal = (double)iSize;
                    while ((iByteSize = streamRemote.Read(byteBuffer, 0, byteBuffer.Length)) > 0)
                    {
                        // write the bytes to the file system at the file path specified
                        streamLocal.Write(byteBuffer, 0, iByteSize);
                        iRunningByteTotal += iByteSize;

                        // calculate the progress out of a base "100"
                        double dIndex = (double)(iRunningByteTotal);
                        double dProgressPercentage = (dIndex / dTotal);
                        int iProgressPercentage = (int)(dProgressPercentage * 100);

                        // update the progress bar
                        worker.ReportProgress(iProgressPercentage);
                    }

                    // clean up the file stream
                    streamLocal.Close();
                }
                // close the connection to the remote server
                streamRemote.Close();
            }
        }

    }

然后是解压器:

private void decompresser_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker worker = sender as BackgroundWorker;
        #region Unzip files

        if (!Directory.Exists(decompressPath))
            Directory.CreateDirectory(decompressPath);

        using (ZipArchive archive = ZipFile.OpenRead(archiveName))
        {
            int iTotal = archive.Entries.Count();
            int curr = 0;
            foreach (ZipArchiveEntry entry in archive.Entries)
            {
                if (entry.FullName != entry.Name)
                {
                    if (entry.Name == string.Empty)
                    {
                        //create folder here
                        Directory.CreateDirectory(Path.Combine(decompressPath, entry.FullName));
                    }
                    else
                    {
                        //create folder and extract file into it
                        string dirToCreate = entry.FullName.Replace(entry.Name, "");
                        Directory.CreateDirectory(Path.Combine(decompressPath, dirToCreate));
                        entry.ExtractToFile(Path.Combine(decompressPath, entry.FullName));
                    }
                }
                else
                {
                    //just extract file
                    Console.WriteLine(Path.Combine(decompressPath, entry.FullName));
                    entry.ExtractToFile(Path.Combine(decompressPath, entry.FullName));
                }

                curr++;
                var progress = ((double)curr / (double)iTotal) * 60.0;
                worker.ReportProgress((int)progress);
            }
        }

        //delete zip file
        File.Delete(Path.Combine(BASE_DIR, "Patch", "patch.zip"));
        #endregion

        #region Copy files
        string sourceDirName = Path.Combine(BASE_DIR, "Patch");
        string destDirName = BASE_DIR;
        DirectoryInfo dir = new DirectoryInfo(sourceDirName);
        long maxbytes = 0;
        List<FileInfo> files = new List<FileInfo>();
        FileInfo[] folder = dir.GetFiles("*.*", SearchOption.AllDirectories);
        foreach (FileInfo file in folder)
        {
            if ((file.Attributes & FileAttributes.Directory) != 0) continue;
            files.Add(file);
            maxbytes += file.Length;
        }

        // Copy files
        long bytes = 0;
        foreach (FileInfo file in files)
        {
            try
            {
                //where to copy
                string copyPath = file.FullName.Replace("Patch\\", "").Replace(file.Name, "");
                if (!Directory.Exists(copyPath))
                    Directory.CreateDirectory(copyPath);
                File.Copy(file.FullName, Path.Combine(copyPath, file.Name), true);
                var progress = 60 + ((double)bytes / (double)maxbytes) * 30.0;
                worker.ReportProgress((int)progress);
            }
            catch (Exception ex)
            {
            }
            bytes += file.Length;
        }

        #endregion

        #region Clean Up

        foreach (FileInfo file in files)
        {
            try
            {
                var progress = 90 + ((double)(maxbytes - file.Length) / (double)maxbytes) * 9;

                file.Delete();
                worker.ReportProgress((int)progress);
            }
            catch (Exception ex) { }
        }
        try
        {
            string delPath = Path.Combine(BASE_DIR, "Patch");
            Directory.Delete(delPath, true);
        }
        catch (Exception ex) { }

        worker.ReportProgress(100);


        #endregion
    }

我通过调用App.Current.Shutdown(); 方法终止应用程序。

【问题讨论】:

    标签: c# .net wpf


    【解决方案1】:

    如果我没有遗漏任何东西,那么有一个非常简单的解决方案。 您可以使用退出时的应用程序event 通过调用workerObject.RequestStop(); 来停止所有后台工作人员(请注意,它仅向工作人员中的代码发出信号,表明已请求停止,您需要在工作人员的工作循环中处理此问题以停止工作)。然后你就可以清理补丁文件夹了。

    【讨论】:

      【解决方案2】:

      为了避免这个问题,您能否在启动应用程序时、但在开始下载之前删除补丁文件夹?

      【讨论】:

        【解决方案3】:

        @GWLlosa - 如果需要,我确实考虑过在启动时删除文件,但如果找不到更好的东西,我会将其保存为最后的手段

        我尝试过这样的事情

            protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
                {
                    updater.CancelAsync();
                    while (updater.IsBusy)
                    {
                        Thread.Sleep(100);
                    }
        //run code to delete the files after the worked has stopped
        }
        

        但这只会让我陷入无限循环:)

        【讨论】:

          【解决方案4】:

          如果我这样做,我会在启动后立即查找并终止下载进程。

          Process[] p = Process.GetProcessesByName("processname");
          foreach (var proc in p)
          {
               proc.Kill();
          }
          

          【讨论】:

            【解决方案5】:

            对于您的问题,我有 2 个解决方案。

            1) 不要尝试在退出时删除补丁文件夹,只需触发并忘记 BackgroundWorker,如果用户取消补丁并退出,则只需退出应用程序。但是当用户启动您的应用程序时,请务必删除补丁文件夹,特别是在您调用解压缩之前。

            2) 如果您仍想在退出时删除补丁文件夹,请使用 CancelAsync() 函数取消您的 BackgroundWorker。例如

            private void btnStart_Click(object sender, EventArgs e)
            {
                if (btnStart.Text == "Start")
                {
                    ...
                }
                else
                {
                    m_bgWorker.CancelAsync();
                }
            }
            
            
            private void SearchFiles(string strPath, string strPattern)
            {
                string strMessage = "Parsing directory " + strPath;
                m_bgWorker.ReportProgress(0, strMessage);
                foreach (string strFileName in Directory.GetFiles(strPath, strPattern))
                {
                    if (m_bgWorker.CancellationPending)
                    {
                        return;
                    }
                    else
                    {
                        m_lstFiles.Add(strFileName);
                    }
                }
            
                foreach (string strDirName in Directory.GetDirectories(strPath))
                {
                    if (m_bgWorker.CancellationPending)
                    {
                        return;
                    }
                    else
                    {
                        SearchFiles(strDirName, strPattern);
                    }
                }
            }
            

            我从post 得到了代码。您可以从该示例中阅读更多内容,了解作者如何取消他的 BackgroundWorker。

            【讨论】:

            • 我之前写了一条评论,解释说我已经尝试过 :) 我捕获了关闭事件,停止了它,尝试取消异步,但它不起作用。据我所知,问题在于 using{} 块,因为它们在操作完成之前不会释放资产。
            • 如何将 CancelAsync() 转发到解压器 BackgroundWorker?如果使用 block 会导致问题,那就很奇怪了。因为如果您 CancelAsync() 解压缩操作,使用块 (ZipArchive archive = ZipFile.OpenRead(archiveName)) 现在超出范围,因此 ZipArchive 对象也应该被释放,并且所有解压缩过程也应该终止。
            • 显然这不会发生。我实际上在这里和那里使用了一些console.writeline,显然,在“使用”块内,没有收到工人取消事件。所以我的猜测是这个块创建了某种“单独的环境”,因此在这个范围之外更改变量不会影响 using 块。因此,在对这个问题进行了一段时间的抨击之后,我决定尝试一个更简单的方法...在开始时删除文件:)
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-12-23
            相关资源
            最近更新 更多