表格应用程序“未响应” 复制大文件时?

发布于 2024-07-20 05:49:26 字数 726 浏览 2 评论 0原文

我正在开发一个文件管理器应用程序。 文件大小通常为 500MB 到 2GB。 一切工作正常,但应用程序“停止响应”是非常烦人的。 我想做的是在每次读/写操作之后逐字节或逐兆复制一些 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 调用或其他方式来执行此操作的示例。 我必须在这里遗漏一些东西,所以如果有人能让我走上正确的轨道,我将非常感激。

提前致谢!

I've got a file organizer application I'm working on. The files are regularly 500MB to 2GB. Everything works fine, but it is extremely annoying that the application "Stops Responding." What I'd like to do is a byte by byte or meg by meg copy with some Application.DoEvents() in there after each read/write action. Something along these lines, I don't know what the actual classes to use would be so I'm just going to make some stuff up :)

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

I know this seems like it should be a simple thing to do but I for the life of me can't seem to find any kind of example for doing this without using direct API calls or whatever. I've got to be missing something here so if anyone could get me on the right track here I'd really appreciate it.

Thanks in advance!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

じ违心 2024-07-27 05:49:27

我想使用 CopyFileEx 函数。 如果托管框架库中不存在与该功能类似的功能,那么请 Google 来了解如何使用它:也许是一篇类似 http://www.thetechscene.com/2008/09/copyfileex-with-progress-callback-in-c-using- pinvoke/

我想要使用 CopyFileEx 的原因是我假设它是在操作系统内核中实现的,数据在文件系统驱动程序中从一个文件复制到另一个文件,而无需使用用户内存(更不用说托管内存)。

I'd want to use the CopyFileEx function. If an analog to that function doesn't exist in the managed framework library, then Google for how to use it anyway: maybe an article like http://www.thetechscene.com/2008/09/copyfileex-with-progress-callback-in-c-using-pinvoke/

My reason for wanting to use CopyFileEx is that I assume it's implemented in the O/S kernel, with the data being copy from one file to another within the file system driver, without using user memory (let alone managed memory).

北座城市 2024-07-27 05:49:27

Threading.ThreadPool.QueueUserWorkitem 应该可以帮助您顺利进行。

Threading.ThreadPool.QueueUserWorkitem should get you well on your way.

巷雨优美回忆 2024-07-27 05:49:27

一种方法是在单独的线程中执行复制操作。 当线程执行复制文件的工作时,您的主应用程序将继续正常运行。 您当然需要在线程和主应用程序之间添加通信,以便可以更新进度栏或类似的反馈机制。

如果您不想处理多个线程,另一种方法是创建一个类,其中包含用于复制操作的状态变量和一个从主应用程序定期调用的成员函数,以便每次调用时复制一定数量的字节。

One approach is to perform the copy operation in a separate thread. Your main application will continue to run normally while the thread performs the work of copying the file. You'll of course want to add communication between the thread and the main application so you can update your progress bar or similar feedback mechanism.

If you'd rather not deal with multiple threads, another approach is to create a class that contains state variables for the copy operation and a member function that's called periodically from your main application in order to copy a certain number of bytes each time it's called.

喜你已久 2024-07-27 05:49:27

除了运行后台线程之外,您还应该注意,您正在复制 512M-2G 的数据一次一个字节。 这将转化为最多 2 个对 ReadByte 和 WriteByte 的BILLION 调用。 希望这些调用在某个地方进行缓冲,这样您就不会进行 2 BILLION 托管到非托管的转换,但即便如此,这肯定会增加。

内存不是免费的,但它确实很便宜。 分配一个缓冲区(可能是 16K-64K)并分块复制。 不,代码并不像您需要处理不读取整个块的情况那么简单,但我宁愿 2G/64K 方法调用超过 2G。

In addition to running a background thread, you should note that you're copying 512M-2G of data one byte at a time. This will translate into up to 2 BILLION calls to ReadByte and WriteByte. Hopefully those calls buffer somewhere so you don't make 2 BILLION managed to unmanaged transitions, but even so that will surely add up.

Memory isn't free, but it's sure as heck is cheap. Allocate a buffer (maybe 16K-64K) and copy in chunks. No, the code isn't as simple as you will need to handle one case of not reading the entire block, but I'd rather to 2G/64K method invocations over 2G.

┈┾☆殇 2024-07-27 05:49:27

你这里有两个问题。 首先是 GUI 线程在复制大文件时没有响应。 正如其他人所建议的那样,您应该使用后台线程来解决这个问题。

另一个问题是您当前的文件复制例程不支持进度回调函数。 以下问题的接受答案包含编写自己的解决方案所需的信息:

我可以在 .NET 中使用 FileInfo.CopyTo() 显示文件复制进度吗?

编辑:
我刚刚找到了这个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
    }
}

You have two problems here. The first is that the GUI thread is not responsive while copying large files. You should use a background thread to solve that, as others have suggested.

The other problem is that your current file copying routine does not support a progress callback function. The accepted answer to the question below has the information you need to write your own solution:

Can I show file copy progress using FileInfo.CopyTo() in .NET?

EDIT:
I just found this wrapper class for CopyFileEx. I tested it and it works great!

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
    }
}
桃酥萝莉 2024-07-27 05:49:27

您真正想做的是拥有一个多线程应用程序并在后台线程中进行文件复制,这样您的主线程就不会被占用。

What you really want to do is have a multi-threaded application and do the file copy in a background thread, that way your main thread will not be tied up.

请止步禁区 2024-07-27 05:49:26

您应该在表单上使用 BackgroundWorker 来执行以下操作复制。 它将允许在单独的线程上完成文件复制,并让您的 UI 具有响应能力。 虽然增加了一些复杂性,但 BackgroundWorker 会为您处理很多管道工作。 但是,有很多示例 您想要执行的操作

You should use a BackgroundWorker on your form to do the copying. It will allow the file copies to be done on a separate thread and let your UI be responsive. There is some added complexity, but the BackgroundWorker takes care of a lot of the plumbing for you. But, there are plenty of examples of what you want to do.

安稳善良 2024-07-27 05:49:26

您需要使用 BackgroundWorkerThread 来完成此操作。 这是如何执行此操作的一个很好的示例: 使用后台工作线程复制文件

You need to use a BackgroundWorkerThread to accomplish this. Here's a very good example of how to do that: Copy File Using Background Worker Threads

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文