copy file w nowym wątku

0

Witajcie,

piszę program, w którym jedną z funkcji jest kopiowanie plików. W standardowym file.copy nie można swobodnie zmieniać wartości progress bar, ani anulować kopiowania. Wyczytałem, że kopiowanie należy umieścić w nowym wątku. Niestety jestem totalnym laikiem w C#, stąd proszę o pomoc.

Oto kod:

public void WatekProgressStart()
{
File.Copy(nazwaPliku, dirMP3Man + Path.GetFileName(nazwaPliku));
}

(...)

if (this.Dysk.SelectedIndices.Count > 0)
for (int i = 0; i < danePlikow.Count; i++)
{
s = (string)danePlikow[i];
nazwaPliku = s;
Thread watek = new Thread(new ThreadStart(WatekProgressStart));
watek.Start();
}

mam wrażenie, że tego jest za mało, że coś pominąłem...

druga rzecz, jak w nowym wątku odwołać się do formatki i jej elementów? wyskakuje błąd, że formę uruchomiono w innym wątku...

0

co do kodu, to tworzac watki for-em w ten sposób, zastępujesz ciagle zmienna watek. Nie wiem jak się zachowuje w takim wypadku program, czy wątki gdzies dalej żyją czy co... Ale nie będziesz mogł się odwołać do poprzednich wątków, tylko do ostatniego do którego przechowujesz jeszcze referencje w zmiennej 'watek'.

druga rzecz, jak w nowym wątku odwołać się do formatki i jej elementów? wyskakuje błąd, że formę uruchomiono w innym wątku...

poczytaj o delegatach i metodach Invoke / BeginInvoke

0

Napisałem kiedyś na swoje potrzeby klasę która kopiuje plik w nowym wątku. Może Ci się przyda :)

using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.IO;
using System.Runtime.Remoting.Messaging;

namespace CopyService
{
    class Copy
    {
        int BufferSize = 131072;
        byte[] buffer;
        FileStream reader;
        FileStream writer;
        long bytesTotal, bytesCopied;
        int readData, progressPercentage;
        private MyAsyncContext myTaskContext;

        public void CopyFileAsync(string Source, string Destination)
        {
            CopyFileDelegate worker = new CopyFileDelegate(CopyFile);
            AsyncCallback completedCallback = new AsyncCallback(CopyFileCompletedCallback);
            lock (_sync)
            {
                if (_myTaskIsRunning)
                    throw new InvalidOperationException("The control is currently busy.");

                AsyncOperation async = AsyncOperationManager.CreateOperation(null);
                MyAsyncContext context = new MyAsyncContext();
                bool cancelled;

                worker.BeginInvoke(Source, Destination, async, context, out cancelled, completedCallback, async);
                _myTaskIsRunning = true;
                myTaskContext = context;
            }
        }
        public event AsyncCompletedEventHandler CopyCompleted;
        public event CopyProgressChangedEventHandler CopyProgressChanged;

        protected virtual void OnCopyProgressChanged(object sender, CopyProgressChangedEventArgs e)
        {
            if (CopyProgressChanged != null)
            {
                CopyProgressChanged(this, e);
            }
        }
        protected virtual void OnCopyCompleted(AsyncCompletedEventArgs e)
        {
            if (CopyCompleted != null)
            {
                CopyCompleted(this, e);
            }
        }

        private readonly object _sync = new object();
        private bool _myTaskIsRunning = false;

        private delegate void CopyFileDelegate(string Source, string Destination, AsyncOperation async, MyAsyncContext asyncContext, out bool cancelled);
        private void CopyFile(string Source, string Destination, AsyncOperation async, MyAsyncContext asyncContext, out bool cancelled)
        {
            cancelled = false;
            try
            {
                buffer = new byte[BufferSize];
                reader = new FileStream(Source, FileMode.Open, FileAccess.Read);
                writer = new FileStream(Destination, FileMode.Append, FileAccess.Write);
                bytesTotal = reader.Length;
                int i = -1;
                while (reader.Position < bytesTotal)
                {
                    readData = reader.Read(buffer, 0, buffer.Length);
                    writer.Write(buffer, 0, readData);
                    writer.Flush();
                    i++;
                    bytesCopied = (long)(i * BufferSize) + (long)readData;
                    progressPercentage = Convert.ToInt32((decimal)bytesCopied / (decimal)bytesTotal * 100);
                    CopyProgressChangedEventArgs eArgs = new CopyProgressChangedEventArgs(bytesCopied, bytesTotal, progressPercentage, null);
                    async.Post(delegate(object e) { OnCopyProgressChanged(this, (CopyProgressChangedEventArgs)e); }, eArgs);

                    if (asyncContext.IsCancelling)
                    {
                        cancelled = true; 
                        return;
                    }
                }
            }
            catch (Exception exc)
            {
                AsyncCompletedEventArgs completedArgs = new AsyncCompletedEventArgs(exc, false, null);
                async.PostOperationCompleted(delegate(object e) { OnCopyCompleted((AsyncCompletedEventArgs)e); }, completedArgs);
            }
            finally
            {
                reader.Close();
                writer.Close();
            }
        }

        private void CopyFileCompletedCallback(IAsyncResult ar)
        {
            CopyFileDelegate worker = (CopyFileDelegate)((AsyncResult)ar).AsyncDelegate;
            AsyncOperation async = (AsyncOperation)ar.AsyncState;
            bool cancelled;
            worker.EndInvoke(out cancelled, ar);
            lock (_sync)
            {
                _myTaskIsRunning = false;
                myTaskContext = null;
            }
            AsyncCompletedEventArgs completedArgs = new AsyncCompletedEventArgs(null, cancelled, null);
            async.PostOperationCompleted(delegate(object e) { OnCopyCompleted((AsyncCompletedEventArgs)e); }, completedArgs);

        }

        public bool IsBusy
        {
            get { return _myTaskIsRunning; }
        }

        public void CancelAsync()
        {
            lock (_sync)
            {
                if (myTaskContext != null)
                    myTaskContext.Cancel();
            }
        }
    }
    public delegate void CopyProgressChangedEventHandler(object sender, CopyProgressChangedEventArgs e);
    public class CopyProgressChangedEventArgs : EventArgs
    {
        private long bytesCopied;
        private long totalBytesToCopy;
        private int progressPercentage;
        private object userState;

        public long BytesCopied
        {
            get { return bytesCopied; }
        }

        public long TotalBytesToCopy
        {
            get { return totalBytesToCopy; }
        }

        public int ProgressPercentage
        {
            get { return progressPercentage; }
        }

        public object UserState
        {
            get { return userState; }
        }
        
        public CopyProgressChangedEventArgs(long bytesCopied, long totalBytesToCopy, int progressPercentage, object userState)
        {
            this.bytesCopied = bytesCopied;
            this.totalBytesToCopy = totalBytesToCopy;
            this.progressPercentage = progressPercentage;
            this.userState = UserState;
        }
    }
    internal class MyAsyncContext
    {
        private readonly object _sync = new object();
        private bool _isCancelling = false;

        public bool IsCancelling
        {
            get
            {
                lock (_sync) { return _isCancelling; }
            }
        }

        public void Cancel()
        {
            lock (_sync) { _isCancelling = true; }
        }
    }
}
0

dark_astray: rzeczywiście, powinienem zrobić coś na kształt tablicy tych wątków... poczytam o Invoke, dzięki!

Fr33q: bardzo Ci dziękuję za kod! Zaimplementowałem go, użyłem:
Copy kopiowanie = new Copy();
kopiowanie.CopyFileAsync(s, dirMP3Man + Path.GetFileName(s));
ale nadal po uruchomieniu kontrolki typu button są nieklikalne, tzn. gdy kopiuje plik całe okno wciąż się przywiesza, a właśnie tego chciałbym uniknąć. A może źle użyłem kodu?

0

Musiałeś coś źle zrobić. Przykład użycia:

private button1_Click(object sender, EventArgs e)
{
        Copy copy = new Copy();
        copy.CopyProgressChanged += new CopyProgressChangedEventHandler(copy_CopyProgressChanged);
        copy.CopyCompleted += new AsyncCompletedEventHandler(copy_CopyCompleted);
        copy.CopyFileAsync(source, dest);
}

void copy_CopyProgressChanged(object sender, CopyProgressChangedEventArgs e)
{
        progressBar1.Value = e.ProgressPercentage;
        label1.Text = e.BytesCopied + " of " + e.TotalBytesToCopy;
}

void copy_CopyCompleted(object sender, AsyncCompletedEventArgs e)
{
        if (e.Error != null)
        {
                label1.Text = "Error: " + e.Error.Message;
        }
        else if (e.Cancelled)
        {
                label1.Text = "Cancelled";
        }
        else
        {
                label1.Text = "Done!";
        }
}
0

Dziękuję. Kod działa świetnie, kopiuje jak należy, ale dopóki nie skończy kopiować, nie da się nacisnąć żadnego przycisku (a chciałbym by była możliwość anulowania kopiowania)...

0

A nie da się tego prosto łatwo zrobić BackgroundWorkerem?

0
Abery napisał(a)

Dziękuję. Kod działa świetnie, kopiuje jak należy, ale dopóki nie skończy kopiować, nie da się nacisnąć żadnego przycisku (a chciałbym by była możliwość anulowania kopiowania)...

Spróbuj poeksperymentować z rozmiarem bufora (zmienna BufferSize). Możliwe że Twój komputer podczas kopiowania jet zbyt obciążony i dlatego zamraża formatkę. Ta klasa jest tak napisana, że wykonuje się w osobnym wątku nie blokując jednocześnie wątku wywołującego (w tym przypadku GUI) i u mnie działa świetnie ;-)

1 użytkowników online, w tym zalogowanych: 0, gości: 1