2011-06-12 141 views
0

我正在编写一个类来移动和复制文件。当前文件进度和总进度发生变化时,我正在举办活动。当我在XP机器上测试代码时,它工作正常,但是当我在Windows 7 64位机器上运行它时,当前进度不会正确更新UI。当前进度ProgressBar只有一半,然后从下一个相同的文件开始。 ProgressBar总进度更新正常。任何想法为什么发生这种情况?从后台线程问题更新UI

编辑:Windows 7机器运行四核心和XP运行双核心。不知道这是否会有所作为。我只是一个业余爱好者所以请原谅我的无知:)

编辑:添加代码(背景)

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.IO; 
using System.Threading; 
using System.Timers; 
using Timer = System.Timers.Timer; 

namespace nGenSolutions.IO 
{ 
public class FileTransporter 
{ 
    #region Delegates 

    public delegate void CurrentFileChangedEventHandler(string fileName); 

    public delegate void CurrentProgressChangedEventHandler(int  percentComplete); 

    public delegate void CurrentWriteSpeedUpdatedEventHandler(long bytesPerSecond); 

    public delegate void TotalProgressChangedEventHandler(int percentComplete); 

    public delegate void TransportCompleteEventHandler(FileTransportResult result); 

    #endregion 

    private readonly List<string> _destinationFiles = new List<string>(); 
    private readonly List<string> _sourceFiles = new List<string>(); 

    private long _bytesCopiedSinceInterval; 
    private FileTransportResult _result; 

    private Timer _speedTimer; 
    private long _totalDataLength; 

    private BackgroundWorker _worker; 

    public bool TransportInProgress { get; private set; } 

    public event CurrentFileChangedEventHandler CurrentFileChanged; 

    public event CurrentProgressChangedEventHandler CurrentProgressChanged; 

    public event CurrentWriteSpeedUpdatedEventHandler CurrentWriteSpeedUpdated; 

    public event TotalProgressChangedEventHandler TotalProgressChanged; 

    public event TransportCompleteEventHandler TransportComplete; 

    public void AddFile(string sourceFile, string destinationFile) 
    { 
     if (!File.Exists(sourceFile)) 
      throw new FileNotFoundException("The specified file does not exist!", sourceFile); 

     var fileInfo = new FileInfo(sourceFile); 

     _totalDataLength += fileInfo.Length; 

     _sourceFiles.Add(sourceFile); 
     _destinationFiles.Add(destinationFile); 
    } 

    public void BeginTransport() 
    { 
     // update the write speed every 3 seconds 
     _speedTimer = new Timer {Interval = 3000}; 
     _speedTimer.Elapsed += SpeedTimerElapsed; 

     _worker = new BackgroundWorker(); 
     _worker.DoWork += DoTransport; 
     _worker.RunWorkerCompleted += WorkerCompleted; 

     _worker.RunWorkerAsync(); 
     _speedTimer.Start(); 

     TransportInProgress = true; 
    } 

    private void SpeedTimerElapsed(object sender, ElapsedEventArgs e) 
    { 
     InvokeCurrentSpeedUpdated(_bytesCopiedSinceInterval); 

     _bytesCopiedSinceInterval = 0; 
    } 

    private void WorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
    { 
     TransportInProgress = false; 
     InvokeTransportComplete(_result); 
    } 

    public void CancelTransport(bool rollbackChanges) 
    { 
     if (TransportInProgress == false) 
      throw new InvalidOperationException("You tried to stop the transport before you started it!"); 

     _result = FileTransportResult.Cancelled; 

     _worker.CancelAsync(); 

     while (_worker.IsBusy) 
     { 
      // wait for worker to die an 'orrible death 
     } 

     // TODO: rollback changes if requested 
    } 

    private void DoTransport(object sender, DoWorkEventArgs e) 
    { 
     long totalBytesCopied = 0; 
     int totalPercentComplete = 0; 

     for (int i = 0; i < _sourceFiles.Count; i++) 
     { 
      string sourceFile = _sourceFiles[i]; 
      string destinationFile = _destinationFiles[i]; 

      long currentFileLength = new FileInfo(sourceFile).Length; 

      InvokeCurrentFileChanged(sourceFile); 

      using (var sourceStream = new FileStream(sourceFile, FileMode.Open, FileAccess.Read)) 
      { 
       using (var destinationStream = new FileStream(destinationFile, FileMode.Create, FileAccess.Write)) 
       { 
        using (var reader = new BinaryReader(sourceStream)) 
        { 
         using (var writer = new BinaryWriter(destinationStream)) 
         { 
          int lastPercentComplete = 0; 

          for (int j = 0; j < currentFileLength; j++) 
          { 
           writer.Write(reader.ReadByte()); 

           totalBytesCopied += 1; 
           _bytesCopiedSinceInterval += 1; 

           int current = Convert.ToInt32((j/(double) currentFileLength)*100); 
           int total = Convert.ToInt32((totalBytesCopied/(double) _totalDataLength)*100); 

           // raise progress events every 3% 
           if (current%3 == 0) 
           { 
            // only raise the event if the progress has increased 
            if (current > lastPercentComplete) 
            { 
             lastPercentComplete = current; 
             InvokeCurrentProgressChanged(lastPercentComplete); 
            } 
           } 

           if (total%3 == 0) 
           { 
            // only raise the event if the progress has increased 
            if (total > totalPercentComplete) 
            { 
             totalPercentComplete = total; 
             InvokeTotalProgressChanged(totalPercentComplete); 
            } 
           } 
          } 
         } 

         InvokeCurrentProgressChanged(100); 
        } 
       } 
      } 
     } 

     InvokeTotalProgressChanged(100); 
    } 

    private void InvokeCurrentFileChanged(string fileName) 
    { 
     CurrentFileChangedEventHandler handler = CurrentFileChanged; 

     if (handler == null) return; 

     handler(fileName); 
    } 

    private void InvokeCurrentProgressChanged(int percentComplete) 
    { 
     CurrentProgressChangedEventHandler handler = CurrentProgressChanged; 

     if (handler == null) return; 

     handler(percentComplete); 
    } 

    private void InvokeCurrentSpeedUpdated(long bytesPerSecond) 
    { 
     CurrentWriteSpeedUpdatedEventHandler handler = CurrentWriteSpeedUpdated; 

     if (handler == null) return; 

     handler(bytesPerSecond); 
    } 

    private void InvokeTotalProgressChanged(int percentComplete) 
    { 
     TotalProgressChangedEventHandler handler = TotalProgressChanged; 

     if (handler == null) return; 

     handler(percentComplete); 
    } 

    private void InvokeTransportComplete(FileTransportResult result) 
    { 
     TransportCompleteEventHandler handler = TransportComplete; 

     if (handler == null) return; 

     handler(result); 
    } 
} 

}

编辑:添加代码(GUI)

using System; 
using System.IO; 
using System.Windows.Forms; 
using ExtensionMethods; 
using nGenSolutions.IO; 

namespace TestApplication 
{ 
public partial class ProgressForm : Form 
{ 
    public ProgressForm() 
    { 
     InitializeComponent(); 
    } 

    private void ProgressForm_Load(object sender, EventArgs e) 
    { 
     var transporter = new FileTransporter(); 
     foreach (string fileName in Directory.GetFiles("C:\\Temp\\")) 
     { 
      transporter.AddFile(fileName, "C:\\" + Path.GetFileName(fileName)); 
     } 

     transporter.CurrentFileChanged += transporter_CurrentFileChanged; 
     transporter.CurrentProgressChanged += transporter_CurrentProgressChanged; 
     transporter.TotalProgressChanged += transporter_TotalProgressChanged; 
     transporter.CurrentWriteSpeedUpdated += transporter_CurrentWriteSpeedUpdated; 
     transporter.TransportComplete += transporter_TransportComplete; 

     transporter.BeginTransport(); 
    } 

    void transporter_TransportComplete(FileTransportResult result) 
    { 
     Close(); 
    } 

    void transporter_CurrentWriteSpeedUpdated(long bytesPerSecond) 
    { 
     double megaBytesPerSecond = (double)bytesPerSecond/1024000; 

     currentSpeedLabel.SafeInvoke(x=> x.Text = string.Format("Transfer speed: {0:0.0} MB/s", megaBytesPerSecond)); 
    } 

    private void transporter_TotalProgressChanged(int percentComplete) 
    { 
     totalProgressBar.SafeInvoke(x => x.Value = percentComplete); 
    } 

    private void transporter_CurrentProgressChanged(int percentComplete) 
    { 
     currentProgressBar.SafeInvoke(x => x.Value = percentComplete); 
    } 

    private void transporter_CurrentFileChanged(string fileName) 
    { 
     this.SafeInvoke(x => x.Text = string.Format("Current file: {0}", fileName)); 
    } 
} 

}

编辑:加入SafeInvoke代码

public static void SafeInvoke<T>(this T @this, Action<T> action) where T : Control 
    { 
     if (@this.InvokeRequired) 
     { 
      @this.Invoke(action, new object[] {@this}); 
     } 
     else 
     { 
      if ([email protected]) return; 

      if (@this.IsDisposed) 
       throw new ObjectDisposedException("@this is disposed."); 

      action(@this); 
     } 
    } 
+1

我很好奇它为什么在XP上正确运行,但在W7上失败。你可以发布代码吗? – Vlad 2011-06-12 13:57:47

+0

当你更新它时,你是否检查进度条的InvokeRequired属性? – Eranga 2011-06-12 14:00:59

+0

嗯,也许你可以做一个最小的例子,表现错误的方式?坦率地说,它太多的代码。该代码看起来是正确的,尽管过于复杂。我目前看到的唯一问题是不安全的调用Close。 (顺便说一句,你的代码是Windows窗体,对不对?) – Vlad 2011-06-12 14:13:42

回答

1

那么,如果transporter_CurrentProgressChanged得到正确的值,程序就能正常工作。当进度值为100%时,您可以尝试向InvokeCurrentProgressChanged(可能带有0参数)添加一些最小的Thread.Sleep调用,以获得UI自身更新的机会,但在这种情况下,您会降低程序性能。可能会更好地保持程序不变,因为它按预期工作,并且主进度栏被更新。

+0

我试过Thread.Sleep(0),但它没有什么不同。不得不去Thread.Sleep(150)看起来不错,但显然这是一个没有去表现明智。我想我必须保持现状。谢谢你的帮助。 – woodstock 2011-06-12 15:35:06