Приложение форм «не отвечает» при копировании больших файлов?

StackOverflow https://stackoverflow.com/questions/844482

Вопрос

У меня есть приложение для организатора файлов, над которым я работаю. Файлы регулярно составляют от 500 до 2 ГБ. Все работает нормально, но чрезвычайно раздражает, что приложение «перестает отвечать». Что я хотел бы сделать, так это байт по байту или MEG от Meg Copy с какой -то Application.doevents () там после каждого действия чтения/записи. Что -то в этом направлении, я не знаю, какими будут настоящие классы для использования, поэтому я просто собираюсь сделать что -нибудь :)

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();
}

Я знаю, что это, кажется, это должно быть простой вещью, но я на всю жизнь не могу найти какой -либо пример для выполнения этого без использования прямых вызовов API или чего -то еще. Я должен что -то упустить здесь, поэтому, если кто -то смог меня на правильном пути, я бы очень признателен.

Заранее спасибо!

Это было полезно?

Решение

Вы должны использовать Фоновый работник в вашей форме, чтобы сделать копирование. Это позволит выполнять копии файла в отдельном потоке и позволить вашему пользовательскому интерфейсу быть отзывчивыми. Есть некоторая дополнительная сложность, но фоновый работник заботится о большей части сантехники для вас. Но есть много Примеры из что ты хочешь делать.

Другие советы

Вам нужно использовать Фоновая работа Чтобы сделать это. Вот очень хороший пример того, как это сделать: Скопировать файл с использованием фоновых потоков

Я бы хотел использовать CopyFileEx функция Если аналог этой функции не существует в библиотеке управляемых фреймворков, то Google для ее использования в любом случае: может быть, такая статья, как http://www.thetechscene.com/2008/09/copyfileex-with-progress-callback-in-c-using-pinvoke/

Моя причина желания использовать CopyFileEx Я предполагаю, что он реализован в ядре O/S, при этом данные копируют из одного файла в другой в драйвере файловой системы, без использования памяти пользователя (не говоря уже о управляемой памяти).

Threading.Threadpool.QueueUserWorkItem должен хорошо вас в пути.

Один подход - выполнить операцию копирования в отдельном потоке. Ваше основное приложение будет продолжать работать нормально, в то время как поток выполняет работу по копированию файла. Вы, конечно, захотите добавить связь между потоком и основным приложением, чтобы вы могли обновить свою панель прогресса или аналогичный механизм обратной связи.

Если вы не хотите иметь дело с несколькими потоками, другой подход состоит в том, чтобы создать класс, который содержит переменные состояния для операции копирования и функцию участника, которая периодически называется из вашего основного приложения, чтобы копировать определенное количество байтов каждый раз, когда она называется Анкет

В дополнение к запуску фонового потока, вы должны отметить, что вы копируете 512M-2G данных один байт за раз. Анкет Это приведет к 2 до 2 Миллиард Звонки в Ридбейт и написатьбит. Надеюсь, эти звонки где -то буферизируют, чтобы вы не делали 2 Миллиард удалось неуправляемые переходы, но даже в этом случае это наверняка сложится.

Память не бесплатна, но это уверен, так как, черт возьми, дешево. Выделите буфер (может быть, 16K-64K) и копируйте куски. Нет, код не так простой, как вам нужно будет обработать один случай, когда я не читаю весь блок, но я бы предпочел, чтобы вызовы метода 2G/64K за 2G.

У вас есть две проблемы здесь. Во -первых, поток GUI не отзывчив при копировании больших файлов. Вы должны использовать фоновый поток, чтобы решить это, как предложили другие.

Другая проблема заключается в том, что ваша текущая процедура копирования файла не поддерживает функцию обратного вызова прогресса. Принятый ответ на вопрос ниже имеет информацию, необходимую для написания собственного решения:

Могу ли я показать прогресс копии файла, используя fileinfo.copyto () в .net?

РЕДАКТИРОВАТЬ: Я только что нашел это Класс обертки для CopyFileEx. Анкет Я проверил это, и это прекрасно работает!

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
    }
}

То, что вы действительно хотите сделать, это иметь многопоточное приложение и сделать копию файла в фоновом потоке, таким образом, ваш основной поток не будет связан.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top