سؤال

لدي تطبيق منظم الملفات الذي أعمل عليه. الملفات بانتظام من 500 ميجابايت إلى 2 جيجابايت. كل شيء يعمل بشكل جيد ، لكن من المزعج للغاية أن يكون التطبيق "يتوقف عن الاستجابة". ما أود القيام به هو بايت بواسطة Byte أو Meg بواسطة Meg Copy مع بعض التطبيقات. شيء ما على هذا المنوال ، لا أعرف ما هي الفصول الفعلية التي يجب استخدامها ، لذلك سأقوم فقط بتجميع بعض الأشياء :)

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

أعلم أن هذا يبدو أنه يجب أن يكون شيئًا بسيطًا ، لكنني لا يبدو أني لحياة لي يجد أي نوع من الأمثلة للقيام بذلك دون استخدام مكالمات واجهة برمجة التطبيقات المباشرة أو أي شيء آخر. يجب أن أفتقد شيئًا هنا ، لذا إذا كان بإمكان أي شخص أن يحصل على المسار الصحيح هنا ، فسأقدر ذلك حقًا.

شكرا مقدما!

هل كانت مفيدة؟

المحلول

يجب عليك استخدام أ عمال الخلفية في النموذج الخاص بك للقيام بالنسخ. سيسمح بنسخ الملف على مؤشر ترابط منفصل وترك واجهة المستخدم الخاصة بك تستجيب. هناك بعض التعقيد المضافة ، لكن عامل الخلفية يعتني بالكثير من السباكة لك. لكن هناك الكثير من أمثلة من ماذا تريد ان تفعل.

نصائح أخرى

تحتاج إلى استخدام أ خلفية عامل لانجاز هذا. إليك مثال جيد جدًا على كيفية القيام بذلك: نسخ الملف باستخدام مؤشرات ترابط عامل الخلفية

أريد استخدام CopyFileEx وظيفة. إذا لم يكن هناك تمثيلي لتلك الوظيفة في مكتبة الإطار المدارة ، فعندئذٍ Google حول كيفية استخدامها على أي حال: ربما مثل مقال مثل http://www.thetechscene.com/2008/09/copyfilex-with-progress-callback-in-c-using-pinvoke/

السبب في الرغبة في الاستخدام CopyFileEx هو أنني أفترض أنه يتم تنفيذه في kernel o/s ، مع وجود نسخ البيانات من ملف إلى آخر داخل برنامج تشغيل نظام الملفات ، دون استخدام ذاكرة المستخدم (ناهيك عن الذاكرة المدارة).

Througing.ThreadPool.QueueUserWorkItem يجب أن تحصل على ما يرام في طريقك.

طريقة واحدة هي تنفيذ عملية النسخ في موضوع منفصل. سيستمر تطبيقك الرئيسي بشكل طبيعي أثناء قيام مؤشر الترابط بعمل نسخ الملف. ستحتاج بالطبع إلى إضافة اتصال بين الخيط والتطبيق الرئيسي حتى تتمكن من تحديث شريط التقدم أو آلية ردود الفعل المماثلة.

إذا كنت تفضل عدم التعامل مع عدة مؤشرات ترابط ، فإن هناك طريقة أخرى تتمثل في إنشاء فئة تحتوي على متغيرات الحالة لعملية النسخ ودالة الأعضاء التي تسمى بشكل دوري من تطبيقك الرئيسي من أجل نسخ عدد معين من البايتات في كل مرة يطلق عليها .

بالإضافة إلى تشغيل مؤشر ترابط الخلفية ، يجب أن تلاحظ أنك تقوم بنسخ البيانات من 512 م 2 جم بايت واحد في كل مرة. هذا سوف يترجم إلى ما يصل إلى 2 مليار دعوات إلى readbyte و writebyte. نأمل أن تكون هذه المكالمات عازلة في مكان ما حتى لا تصنع 2 مليار تمكنت من التحولات غير المدارة ، ولكن حتى ذلك سوف يضيف بالتأكيد.

الذاكرة ليست مجانية ، ولكن من المؤكد أن هيك رخيصة. تخصيص المخزن المؤقت (ربما 16 كيلو باد 64 كيلو) ونسخ في قطع. لا ، الرمز ليس بسيطًا كما ستحتاج إلى التعامل مع حالة واحدة من عدم قراءة الكتلة بأكملها ، لكنني أفضل أن تتجاوز 2G/64K طريقة أكثر من 2G.

لديك مشكلتان هنا. الأول هو أن مؤشر ترابط واجهة المستخدم الرسومية لا يستجيب أثناء نسخ الملفات الكبيرة. يجب عليك استخدام موضوع خلفية لحل ذلك ، كما اقترح آخرون.

المشكلة الأخرى هي أن روتين نسخ الملف الحالي لا يدعم وظيفة رد الاتصال بالتقدم. الإجابة المقبولة على السؤال أدناه لديها المعلومات التي تحتاجها لكتابة الحل الخاص بك:

هل يمكنني إظهار تقدم نسخ الملف باستخدام fileInfo.copyto () في .NET؟

تحرير: لقد وجدت هذا للتو فئة Wrapper لـ 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