C# 将CopyFileEx转换为任务模式

C# 将CopyFileEx转换为任务模式,c#,asynchronous,task,file-copying,C#,Asynchronous,Task,File Copying,一直在阅读有关async和tasks的内容,并试图通过PInvoke将CopyFileEx方法转换为Task模式。我对进度部分有问题 CopyFileEx有一个名为CopyProgressRoutine的回调,它有一个名为lpData的参数,该参数接受一个指针。我想我可以用它来传递我的IProgress界面,这样我就可以报告进度了。然而,事实证明我必须使用结构,而不是类。你知道我该怎么做吗?还是我的方向完全错了 public class ProgressReportAsync { pub

一直在阅读有关async和tasks的内容,并试图通过PInvoke将CopyFileEx方法转换为Task模式。我对进度部分有问题

CopyFileEx有一个名为CopyProgressRoutine的回调,它有一个名为lpData的参数,该参数接受一个指针。我想我可以用它来传递我的IProgress界面,这样我就可以报告进度了。然而,事实证明我必须使用结构,而不是类。你知道我该怎么做吗?还是我的方向完全错了

public class ProgressReportAsync
{
    public int PercentDone { get; set; }
    public string InfoText { get; set; }

    public void setProgress(long _progress, long _total)
    {
        PercentDone = Convert.ToInt32((_progress * 100) / _total);
        InfoText = PercentDone + "% complete."; ;
    }
}

class FileCopyAsync
{
    class UserCallbackArg
    {
        public CancellationToken ct;
        public IProgress<ProgressReportAsync> prg;

        public UserCallbackArg(CancellationToken _ct, IProgress<ProgressReportAsync> _prg)
        {
            ct = _ct;
            prg = _prg;
        }

        public UserCallbackArg() { }
    }

    [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool CopyFileEx(string lpExistingFileName, string lpNewFileName, CopyProgressRoutine lpProgressRoutine, Object lpData, ref bool pbCancel, CopyFileFlags dwCopyFlags);

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

    [Flags]
    enum CopyFileFlags : uint
    {
        COPY_FILE_FAIL_IF_EXISTS = 0x00000001,
        COPY_FILE_RESTARTABLE = 0x00000002,
        COPY_FILE_OPEN_SOURCE_FOR_WRITE = 0x00000004,
        COPY_FILE_ALLOW_DECRYPTED_DESTINATION = 0x00000008,
        COPY_FILE_COPY_SYMLINK = 0x00000800,
        COPY_FILE_NO_BUFFERING = 0x00001000
    }

    enum CopyProgressResult : uint
    {
        PROGRESS_CONTINUE = 0,
        PROGRESS_CANCEL = 1,
        PROGRESS_STOP = 2,
        PROGRESS_QUIET = 3
    }

    enum CopyProgressCallbackReason : uint
    {
        CALLBACK_CHUNK_FINISHED = 0x00000000,
        CALLBACK_STREAM_SWITCH = 0x00000001
    }

    private static bool m_bCancel;

    private CopyProgressResult CopyProgressHandler(long total, long transferred, long streamSize, long StreamByteTrans, uint dwStreamNumber, CopyProgressCallbackReason reason, IntPtr hSourceFile, IntPtr hDestinationFile, IntPtr lpData)
    {
        switch (reason)
        {
            case CopyProgressCallbackReason.CALLBACK_CHUNK_FINISHED:

                UserCallbackArg ucarg = (UserCallbackArg)Marshal.PtrToStructure(lpData, typeof(UserCallbackArg));

                IProgress<ProgressReportAsync> prg = ucarg.prg;
                ProgressReportAsync prgReport = new ProgressReportAsync();

                prgReport.setProgress(transferred, total);
                prg.Report(prgReport);

                if (ucarg.ct.IsCancellationRequested)
                {
                    m_bCancel = true;
                }

                return m_bCancel ? CopyProgressResult.PROGRESS_CANCEL : CopyProgressResult.PROGRESS_CONTINUE;

            default:
                return CopyProgressResult.PROGRESS_CONTINUE;
        }
    }

    public FileCopyAsync() { }

    public Task DoWorkAsync(string _from, string _to, CancellationToken ct, IProgress<ProgressReportAsync> prg)
    {
        return TaskEx.Run(() =>
        {
            bool copyResult;

            if (File.Exists(_to))
            {
                //throw new Exception("File already exists: " + _to);
            }

            if (!File.Exists(_from))
            {
                throw new FileNotFoundException(_from);
            }

            FileInfo fi = new FileInfo(_from);

            m_bCancel = false;

            UserCallbackArg ucarg = new UserCallbackArg(ct, prg);
            GCHandle handle = GCHandle.Alloc(ucarg, GCHandleType.Pinned);
            IntPtr ptr = handle.AddrOfPinnedObject();


            if (fi.Length > (1024 * 1024 * 100))
            {
                //Greater than 100mb then no buffer flag added
                copyResult = CopyFileEx(_from, _to, new CopyProgressRoutine(CopyProgressHandler), ptr, ref m_bCancel, (CopyFileFlags.COPY_FILE_RESTARTABLE & CopyFileFlags.COPY_FILE_FAIL_IF_EXISTS & CopyFileFlags.COPY_FILE_NO_BUFFERING));
            }
            else
            {
                copyResult = CopyFileEx(_from, _to, new CopyProgressRoutine(CopyProgressHandler), ptr, ref m_bCancel, (CopyFileFlags.COPY_FILE_RESTARTABLE & CopyFileFlags.COPY_FILE_FAIL_IF_EXISTS));
            }
            if (!copyResult)
            {
                int error = Marshal.GetLastWin32Error();

                if (m_bCancel && (error == 1235))
                {
                    return;
                }
                else
                {
                    Win32Exception ex = new Win32Exception(error);
                    throw new Win32Exception(error);
                }
            }
        });
    }
}
公共类ProgressReportAsync
{
public int PercentDone{get;set;}
公共字符串InfoText{get;set;}
公共进度(长进度、长总计)
{
完成百分比=转换为32((\u进度*100)/\u总数);
InfoText=PercentDone+%complete;
}
}
类FileCopyAsync
{
类UserCallbackArg
{
公开取消;
公共IProgress prg;
public UserCallbackArg(CancellationToken\u ct,IProgress\u prg)
{
ct=\u-ct;
prg=_prg;
}
public UserCallbackArg(){}
}
[DllImport(“kernel32.dll”,SetLastError=true,CharSet=CharSet.Auto)]
[返回:Marshallas(UnmanagedType.Bool)]
静态外部bool CopyFileEx(字符串lpExistingFileName、字符串lpNewFileName、CopyProgressRoutine lpProgressRoutine、对象lpData、ref bool pbCancel、CopyFileFlags dwCopyFlags);
私有委托CopyProgressResult CopyProgressRoute(长TotalFileSize、长TotalByTestTransferred、,
long StreamSize、long StreamByTestTransferred、uint dwStreamNumber、CopyProgressCallbackReason dwCallbackReason、,
IntPtr hSourceFile、IntPtr hDestinationFile、IntPtr lpData);
[旗帜]
枚举CopyFileFlags:uint
{
复制\u文件\u失败\u如果\u存在=0x00000001,
复制\u文件\u可重新启动=0x00000002,
复制\u文件\u打开\u源\u以便写入=0x00000004,
复制\u文件\u允许\u解密\u目的地=0x00000008,
复制\u文件\u复制\u符号链接=0x0000080,
复制\u文件\u无\u缓冲=0x00001000
}
枚举CopyProgressResult:uint
{
继续的进度=0,
进度\取消=1,
进度停止=2,
进度=3
}
枚举CopyProgressCallback原因:uint
{
回调\u CHUNK\u FINISHED=0x00000000,
回调\流\开关=0x00000001
}
私有静态布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔布尔;
私有CopyProgressResult CopyProgressHandler(长总计、长传输、长流大小、长流字节数、uint dwStreamNumber、CopyProgressCallbackReason、IntPtr hSourceFile、IntPtr hDestinationFile、IntPtr lpData)
{
切换(原因)
{
案例CopyProgressCallbackReason.CALLBACK\u CHUNK\u已完成:
UserCallbackArg ucarg=(UserCallbackArg)Marshal.PtrToStructure(lpData,typeof(UserCallbackArg));
i进程prg=ucarg.prg;
ProgressReportAsync prgReport=新的ProgressReportAsync();
prgReport.setProgress(已转移,总计);
项目报告(项目报告);
如果(ucarg.ct.IsCancellationRequested)
{
m_bCancel=真;
}
返回m_bCancel?CopyProgressResult.PROGRESS_CANCEL:CopyProgressResult.PROGRESS_CONTINUE;
违约:
返回CopyProgressResult.PROGRESS\u CONTINUE;
}
}
公共文件CopyAsync(){}
公共任务DoWorkAsync(字符串_-from、字符串_-to、CancellationToken ct、IProgress prg)
{
返回TaskEx.Run(()=>
{
bool复制结果;
if(File.Exists(_to))
{
//抛出新异常(“文件已存在:”+\u to);
}
如果(!File.Exists(_from))
{
抛出新的FileNotFoundException(\u from);
}
FileInfo fi=新的FileInfo(\u from);
m_bCancel=假;
UserCallbackArg ucarg=新的UserCallbackArg(ct,prg);
GCHandle=GCHandle.Alloc(ucarg,GCHandleType.pinted);
IntPtr ptr=handle.addrofPindedObject();
如果(fi.Length>(1024*1024*100))
{
//大于100mb,则未添加缓冲区标志
copyResult=CopyFileEx(_-from,_-to,new-CopyProgressRoutine(CopyProgressHandler),ptr,ref m_-Cancel,(CopyFileFlags.COPY_-FILE_-RESTARTABLE和CopyFileFlags.COPY_-FILE_-FAIL,如果存在和CopyFileFlags.COPY_-FILE_-NO_-BUFFERING));
}
其他的
{
copyResult=CopyFileEx(_-from,_-to,new-CopyProgressRoutine(CopyProgressHandler),ptr,ref m_-Cancel,(CopyFileFlags.COPY_-FILE_-RESTARTABLE&CopyFileFlags.COPY_-FILE_-FAIL,如果存在));
}
如果(!copyResult)
{
int error=Marshal.GetLastWin32Error();
如果(m_bCancel&(error==1235))
{
返回;
}
其他的
{
Win32Exception ex=新的Win32Exception(错误);
抛出新的Win32Exception(错误);
}
}
});
}
}

我认为最简单的解决方案是将CopyProgressHandler回调移动到用户参数类。在这种情况下,您可以使用ucarg.CopyProgressHandler作为您的CopyProgress例程,并调用存储在用户参数类中的IProgress引用上的方法。也许您也可以将m_bCancel标志移动到该类


使用这种方法可以避免数据编组。

您应该使DoWorkAsync同步。仅仅围绕同步操作包装线程是一种反模式。打电话的人可以自己做。