CopyFileEx不会在大文件上写入元数据(延迟写入失败)

时间:2015-06-23 18:10:57

标签: c#

我在c#中使用CopyFileEx pinvoke将小型和大型文件从辅助驱动器复制到闪存驱动器。有问题的文件是MP4文件。较小的文件可以复制,但较大的文件大约1GB或更多,复制文件的大小,但不包括使MP4不可播放的元数据。是什么原因可以导致我做到这一点?

关闭程序后,Windows会告诉我写入失败。

public class XCopy
{
    #region DLL Imports
    [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool CopyFileEx(string lpExistingFileName, string lpNewFileName, CopyProgressRoutine lpProgressRoutine, IntPtr lpData, ref int pbCancel, CopyFileFlags dwCopyFlags);
    #endregion


    #region Delegates
    private delegate CopyProgressResult CopyProgressRoutine(long TotalFileSize, long TotalBytesTransferred, long StreamSize, 
        long StreamBytesTransferred, uint dwStreamNumber, CopyProgressCallbackReason dwCallbackReason, IntPtr hSourceFile, IntPtr hDestinationFile, IntPtr lpData);
    #endregion


    #region Fields
    private int isCancelled;
    private int filePercentCompleted;
    private static bool finished = true;
    private static bool errorOccurred;
    private static License license;
    #endregion


    #region Properties
    /// <summary>
    /// Gets the flag indicating that copying has finished.
    /// </summary>
    public static bool Finished => finished;

    /// <summary>
    /// Gets the flag indicating that an error occurred while copying.
    /// </summary>
    public static bool ErrorOccurred => errorOccurred;
    #endregion


    #region Event Handlers
    private event EventHandler<ProgressChangedEventArgs> ProgressChanged;
    #endregion


    #region Enumerations
    /// <summary>
    /// Progress result.
    /// </summary>
    private enum CopyProgressResult : uint
    {
        PROGRESS_CONTINUE = 0
    }

    /// <summary>
    /// Callback reason.
    /// </summary>
    private enum CopyProgressCallbackReason : uint
    {
        CALLBACK_CHUNK_FINISHED = 0x00000000
    }

    /// <summary>
    /// File flags.
    /// </summary>
    [Flags]
    private enum CopyFileFlags : uint
    {
        COPY_FILE_FAIL_IF_EXISTS = 0x00000001,
        COPY_FILE_NO_BUFFERING = 0x00001000,
        COPY_FILE_RESTARTABLE = 0x00000002
    }
    #endregion


    #region Constructors
    /// <summary>
    /// Constructor that is internal.
    /// </summary>
    private XCopy()
    {
        license = LicenseManager.Validate(typeof(XCopy), this);
        isCancelled = 0;
    }

    /// <summary>
    /// Constructor.
    /// </summary>
    static XCopy()
    {
        license = LicenseManager.Validate(typeof(XCopy), IntPtr.Zero);
    }
    #endregion


    #region Internal Members
    /// <summary>
    /// Copy source file to destination.
    /// </summary>
    /// <param name="source"></param>
    /// <param name="destination"></param>
    /// <param name="overwrite"></param>
    /// <param name="nobuffering"></param>
    /// <param name="handler"></param>
    private void CopyInternal(string source, string destination, bool overwrite, bool nobuffering, EventHandler<ProgressChangedEventArgs> handler)
    {
        errorOccurred = false;
        finished = false;

        try
        {
            var copyFileFlags = CopyFileFlags.COPY_FILE_RESTARTABLE;

            if (!overwrite)
            {
                copyFileFlags |= CopyFileFlags.COPY_FILE_FAIL_IF_EXISTS;
            }

            if (nobuffering)
            {
                copyFileFlags |= CopyFileFlags.COPY_FILE_NO_BUFFERING;
            }

            if (handler != null)
            {
                ProgressChanged += handler;
            }

            var result = CopyFileEx(source, destination, CopyProgressHandler, IntPtr.Zero, ref isCancelled, copyFileFlags);

            if (!result)
            {
                throw new Win32Exception(Marshal.GetLastWin32Error());
            }
        }
        catch (Exception e)
        {
            LogManager.Log(LogType.ERROR, "Copy: " + e.Message);

            if (handler != null)
            {
                ProgressChanged -= handler;
                errorOccurred = true;
            }

            finished = true;
        }
    }

    /// <summary>
    /// Progress handler.
    /// </summary>
    /// <param name="total"></param>
    /// <param name="transferred"></param>
    /// <param name="streamSize"></param>
    /// <param name="streamByteTrans"></param>
    /// <param name="dwStreamNumber"></param>
    /// <param name="reason"></param>
    /// <param name="hSourceFile"></param>
    /// <param name="hDestinationFile"></param>
    /// <param name="lpData"></param>
    /// <returns></returns>
    private CopyProgressResult CopyProgressHandler(long total, long transferred, long streamSize, long streamByteTrans, uint dwStreamNumber, 
        CopyProgressCallbackReason reason, IntPtr hSourceFile, IntPtr hDestinationFile, IntPtr lpData)
    {
        if (reason == CopyProgressCallbackReason.CALLBACK_CHUNK_FINISHED)
        {
            OnProgressChanged((transferred / (double)total) * 100.0);
        }

        if (transferred >= total)
        {
            finished = true;
        }

        return CopyProgressResult.PROGRESS_CONTINUE;
    }
    #endregion


    #region External Members
    /// <summary>
    /// Copy using an overwrite and buffer flag.
    /// </summary>
    /// <param name="source"></param>
    /// <param name="destination"></param>
    /// <param name="overwrite"></param>
    /// <param name="nobuffering"></param>
    public static void Copy(string source, string destination, bool overwrite, bool nobuffering)
    {
        Task.Factory.StartNew(() =>
        {
            try
            {
                new XCopy().CopyInternal(source, destination, overwrite, nobuffering, null);
            }
            catch (AggregateException e)
            {
                foreach (var error in e.InnerExceptions)
                {
                    LogManager.Log(LogType.DEBUG, error.Message);
                }
            }
        });
    }

    /// <summary>
    /// Copy using an overwrite and buffer flag along with a progress event handler.
    /// </summary>
    /// <param name="source"></param>
    /// <param name="destination"></param>
    /// <param name="overwrite"></param>
    /// <param name="nobuffering"></param>
    /// <param name="handler"></param>
    public static void Copy(string source, string destination, bool overwrite, bool nobuffering, EventHandler<ProgressChangedEventArgs> handler)
    {
        Task.Factory.StartNew(() =>
        {
            try
            {
                new XCopy().CopyInternal(source, destination, overwrite, nobuffering, handler);
            }
            catch (AggregateException e)
            {
                foreach (var error in e.InnerExceptions)
                {
                    LogManager.Log(LogType.DEBUG, error.Message);
                }
            }
        });
    }

    /// <summary>
    /// Dispose of the license.
    /// </summary>
    public void Dispose()
    {
        if (license != null)
        {
            license.Dispose();
            license = null;
        }
    }
    #endregion


    #region Events
    /// <summary>
    /// Handle changing progress.
    /// </summary>
    /// <param name="percent"></param>
    private void OnProgressChanged(double percent)
    {
        if ((int)percent > filePercentCompleted)
        {
            filePercentCompleted = (int)percent;
            ProgressChanged?.Invoke(this, new ProgressChangedEventArgs(filePercentCompleted, null));
        }
    }
    #endregion
}

0 个答案:

没有答案