Domanda

Ho un'applicazione Organizzatore di file su cui sto lavorando. I file sono regolarmente da 500 MB a 2 GB. Tutto funziona bene, ma è estremamente fastidioso che l'applicazione "smetta di rispondere". Quello che mi piacerebbe fare è un byte per byte o meg di meg copia con qualche applicazione.doevents () lì dopo ogni azione di lettura/scrittura. Qualcosa in questo senso, non so quali sarebbero le lezioni reali da usare, quindi ho intenzione di fare delle cose :)

private void CopyFile(string inFilename, string outFilename)
{
    FileReader inReader(inFilename);
    FileWriter outWriter(outFilename, FileMode.OpenOrCreate);

    byte theByte;
    while (theByte = inReader.ReadByte())
    {
        outWriter.WriteByte(theByte, WriteMode.Append);
        UpdateProgressBar();
        Application.DoEvents();
    }

    inReader.CloseFile();
    outWriter.CloseFile();
}

So che questo sembra che dovrebbe essere una cosa semplice da fare, ma per la vita di me non riesco a trovare alcun tipo di esempio per farlo senza usare chiamate dirette API o altro. Devo perdere qualcosa qui, quindi se qualcuno potesse portarmi sulla strada giusta qui lo apprezzerei davvero.

Grazie in anticipo!

È stato utile?

Soluzione

Dovresti usare un Lavoro di sfondo sul tuo modulo per fare la copia. Consentirà di eseguire le copie del file su un thread separato e consentirà l'interfaccia utente reattiva. C'è una certa complessità aggiunta, ma il lavoro di sfondo si occupa di un sacco di impianti idraulici per te. Ma ci sono molti esempi di cosa vuoi fare.

Altri suggerimenti

Devi usare un file BackgroundworkerThread per raggiungere questo obiettivo. Ecco un ottimo esempio di come farlo: Copia file utilizzando thread di lavoro in background

Vorrei usare il CopyFileEx funzione. Se un analogico a quella funzione non esiste nella libreria gestita Framework, allora Google su come usarlo comunque: forse un articolo come http://www.thetechscene.com/2008/09/copyfileex-with-progress-callback-in-c-using-pinvoke/

La mia ragione per voler usare CopyFileEx È che presumo che sia implementato nel kernel O/S, con i dati che vengono copiati da un file all'altro all'interno del driver del file system, senza utilizzare la memoria dell'utente (per non parlare della memoria gestita).

Threading.Threadpool.QueueUserWorkitem dovrebbe portarti bene.

Un approccio è eseguire l'operazione di copia in un thread separato. La tua applicazione principale continuerà a funzionare normalmente mentre il thread esegue il lavoro di copia del file. Naturalmente vorrai aggiungere una comunicazione tra il thread e l'applicazione principale in modo da poter aggiornare la barra di avanzamento o un meccanismo di feedback simile.

Se preferisci non affrontare più thread, un altro approccio è quello di creare una classe che contenga variabili di stato per l'operazione di copia e una funzione membro che si chiama periodicamente dalla tua applicazione principale per copiare un certo numero di byte ogni volta che si chiama .

Oltre a eseguire un thread di sfondo, dovresti notare che stai copiando 512 m-2G di dati Un byte alla volta. Questo si tradurrà in un massimo di 2 Miliardi chiama a readbyte e writebyte. Spero che quelle chiamate buffer da qualche parte in modo da non fare 2 Miliardi gestito a transizioni non gestite, ma anche per questo si sommerà sicuramente.

La memoria non è gratuita, ma è sicuro che diamine è economico. Allocare un buffer (forse 16K-64k) e copiare in blocchi. No, il codice non è così semplice come dovrai gestire un caso di non leggere l'intero blocco, ma preferirei invocazioni del metodo 2G/64K su 2G.

Hai due problemi qui. Il primo è che il thread GUI non è reattivo durante la copia di file di grandi dimensioni. Dovresti usare un thread di sfondo per risolverlo, come hanno suggerito altri.

L'altro problema è che la routine di copia del file corrente non supporta una funzione di callback di avanzamento. La risposta accettata alla domanda seguente ha le informazioni necessarie per scrivere la tua soluzione:

Posso mostrare i progressi della copia del file utilizzando fileInfo.Copyto () in .NET?

EDIT: ho appena trovato questo Classe wrapper per CopyFileex. L'ho provato e funziona benissimo!

using System;
using System.ComponentModel;
using System.IO;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;

namespace FileCopyTest {
    public sealed class FileRoutines {
        public static void CopyFile(FileInfo source, FileInfo destination) {
            CopyFile(source, destination, CopyFileOptions.None);
        }

        public static void CopyFile(FileInfo source, FileInfo destination,
            CopyFileOptions options) {
            CopyFile(source, destination, options, null);
        }

        public static void CopyFile(FileInfo source, FileInfo destination,
            CopyFileOptions options, CopyFileCallback callback) {
            CopyFile(source, destination, options, callback, null);
        }

        public static void CopyFile(FileInfo source, FileInfo destination,
            CopyFileOptions options, CopyFileCallback callback, object state) {
            if (source == null) throw new ArgumentNullException("source");
            if (destination == null)
                throw new ArgumentNullException("destination");
            if ((options & ~CopyFileOptions.All) != 0)
                throw new ArgumentOutOfRangeException("options");

            new FileIOPermission(
                FileIOPermissionAccess.Read, source.FullName).Demand();
            new FileIOPermission(
                FileIOPermissionAccess.Write, destination.FullName).Demand();

            CopyProgressRoutine cpr = callback == null ?
                null : new CopyProgressRoutine(new CopyProgressData(
                    source, destination, callback, state).CallbackHandler);

            bool cancel = false;
            if (!CopyFileEx(source.FullName, destination.FullName, cpr,
                IntPtr.Zero, ref cancel, (int)options)) {
                throw new IOException(new Win32Exception().Message);
            }
        }

        private class CopyProgressData {
            private FileInfo _source = null;
            private FileInfo _destination = null;
            private CopyFileCallback _callback = null;
            private object _state = null;

            public CopyProgressData(FileInfo source, FileInfo destination,
                CopyFileCallback callback, object state) {
                _source = source;
                _destination = destination;
                _callback = callback;
                _state = state;
            }

            public int CallbackHandler(
                long totalFileSize, long totalBytesTransferred,
                long streamSize, long streamBytesTransferred,
                int streamNumber, int callbackReason,
                IntPtr sourceFile, IntPtr destinationFile, IntPtr data) {
                return (int)_callback(_source, _destination, _state,
                    totalFileSize, totalBytesTransferred);
            }
        }

        private delegate int CopyProgressRoutine(
            long totalFileSize, long TotalBytesTransferred, long streamSize,
            long streamBytesTransferred, int streamNumber, int callbackReason,
            IntPtr sourceFile, IntPtr destinationFile, IntPtr data);

        [SuppressUnmanagedCodeSecurity]
        [DllImport("Kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool CopyFileEx(
            string lpExistingFileName, string lpNewFileName,
            CopyProgressRoutine lpProgressRoutine,
            IntPtr lpData, ref bool pbCancel, int dwCopyFlags);
    }

    public delegate CopyFileCallbackAction CopyFileCallback(
        FileInfo source, FileInfo destination, object state,
        long totalFileSize, long totalBytesTransferred);

    public enum CopyFileCallbackAction {
        Continue = 0,
        Cancel = 1,
        Stop = 2,
        Quiet = 3
    }

    [Flags]
    public enum CopyFileOptions {
        None = 0x0,
        FailIfDestinationExists = 0x1,
        Restartable = 0x2,
        AllowDecryptedDestination = 0x8,
        All = FailIfDestinationExists | Restartable | AllowDecryptedDestination
    }
}

Quello che vuoi veramente fare è avere un'applicazione multi-thread e fare la copia del file in un thread in background, in questo modo il thread principale non sarà legato.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top