2

作業中のファイルオーガナイザーアプリケーションがあります。ファイルは通常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呼び出しなどを使用せずにこれを行うための例を見つけることができないようです。私はここで何かが欠けている必要があるので、誰かが私をここで正しい軌道に乗せることができれば、私は本当に感謝しています。

前もって感謝します!

4

8 に答える 8

6

コピーを行うには、フォームでBackgroundWorkerを使用する必要があります。これにより、ファイルのコピーを別のスレッドで実行できるようになり、UIが応答しやすくなります。複雑さが増しますが、BackgroundWorkerが多くの配管を処理してくれます。しかし、あなたがやりたいことのはたくさんあります。

于 2009-05-10T01:22:12.463 に答える
3

これを行うには、 BackgroundWorkerThreadを使用する必要があります。これを行う方法の非常に良い例を次に示します。バックグラウンドワーカースレッドを使用してファイルをコピーする

于 2009-05-10T01:23:09.327 に答える
3

この機能を使いたいのですがCopyFileEx。その関数の類似物がマネージドフレームワークライブラリに存在しない場合は、とにかくそれを使用する方法についてGoogle:http ://www.thetechscene.com/2008/09/copyfileex-with-progress-callbackのような記事かもしれません-in-c-using-pinvoke /

私が使用したい理由CopyFileExは、O / Sカーネルに実装されており、ユーザーメモリ(管理メモリは言うまでもなく)を使用せずに、ファイルシステムドライバ内のあるファイルから別のファイルにデータがコピーされていると想定しているためです。

于 2009-05-10T01:37:13.053 に答える
1

Threading.ThreadPool.QueueUserWorkitemは、順調に進んでいるはずです。

于 2009-05-10T01:22:26.630 に答える
1

1つのアプローチは、別のスレッドでコピー操作を実行することです。スレッドがファイルをコピーする作業を実行している間、メインアプリケーションは引き続き正常に実行されます。もちろん、プログレスバーまたは同様のフィードバックメカニズムを更新できるように、スレッドとメインアプリケーション間の通信を追加する必要があります。

複数のスレッドを処理したくない場合、別のアプローチは、コピー操作の状態変数と、呼び出されるたびに特定のバイト数をコピーするためにメインアプリケーションから定期的に呼び出されるメンバー関数を含むクラスを作成することです。 。

于 2009-05-10T01:24:10.870 に答える
1

バックグラウンド スレッドの実行に加えて、512M-2G のデータを一度に1 バイトずつコピーしていることに注意してください。これは、ReadByte と WriteByte への最大 20 億回の呼び出しに変換されます。うまくいけば、これらの呼び出しがどこかにバッファリングされて、20 億のマネージからアンマネージへの遷移が発生しないようになります、それでも確実に加算されます。

メモリは無料ではありませんが、安いのは確かです。バッファー (おそらく 16K ~ 64K) を割り当て、チャンクでコピーします。いいえ、コードは、ブロック全体を読み取らない 1 つのケースを処理する必要があるほど単純ではありませんが、2G を介して 2G/64K メソッド呼び出しを行いたいと考えています。

于 2009-05-11T00:46:03.103 に答える
1

ここで 2 つの問題があります。1 つ目は、大きなファイルのコピー中に GUI スレッドが応答しないことです。他の人が示唆しているように、バックグラウンド スレッドを使用して解決する必要があります。

もう 1 つの問題は、現在のファイル コピー ルーチンが進捗コールバック関数をサポートしていないことです。以下の質問に対する受け入れられた回答には、独自のソリューションを作成するために必要な情報が含まれています。

.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
    }
}
于 2009-05-11T00:30:06.360 に答える
0

本当にやりたいのは、マルチスレッドアプリケーションを用意し、バックグラウンドスレッドでファイルのコピーを実行することです。そうすれば、メインスレッドが拘束されることはありません。

于 2009-05-10T01:17:43.647 に答える