亲爱的熟练。我正在开发一个实体,允许用户以异步方式复制多个文件,并具有取消功能(以及报告进度)。显然,复制过程在另一个线程中运行,与调用CopyAsync的线程不同。
我的第一个实现使用FileStream.BeginRead / BeginWrite和缓冲区,并根据该缓冲区的使用次数报告进度。
后来,出于教育目的,我试图通过Win32 CopyFileEx函数实现相同的东西。最后,我偶然发现了以下内容:此函数采用指向bool值的指针,该值被视为取消指示符。根据MSDN,在复制操作期间,Win32将多次检查该值。当用户将此值设置为“false”时,复制操作将被取消。
对我来说真正的问题是如何创建一个布尔值,将其传递给Win32并使外部用户可以访问该值,以使其能够取消复制操作。显然用户会调用CancelAsync(object taskId),所以我的问题是如何在我的CancelAsync实现的另一个线程中访问该布尔值。
我的第一次尝试是使用Dictionary,其中key是异步操作的标识符,值是为布尔值内存槽分配的值。当用户调用“CancelAsync(object taskId)”方法时,我的类从字典中检索指向该已分配内存的指针,并在那里写入“1”。
昨天我开发了另一个解决方案,它基于在我的复制方法中创建一个bool局部变量,并在字典中保存该值的地址,直到复制操作完成。这种方法可以在以下代码行中描述(非常简单和粗略,只是为了说明一个想法):
class Program
{
// dictionary for storing operaitons identifiers
public Dictionary<string, IntPtr> dict = new Dictionary<string,IntPtr>();
static void Main(string[] args)
{
Program p = new Program();
p.StartTheThread(); // start the copying operation, in my
// implementation it will be a thread pool thread
}
ManualResetEvent mre;
public void StartTheThread()
{
Thread t = new Thread(ThreadTask);
mre = new ManualResetEvent(false);
t.Start(null);
GC.Collect(); // just to ensure that such solution works :)
GC.Collect();
mre.WaitOne();
unsafe // cancel the copying operation
{
IntPtr ptr = dict["one"];
bool* boolPtr = (bool*)ptr; // obtaining a reference
// to local variable in another thread
(*boolPtr) = false;
}
}
public void ThreadTask(object state)
{
// In this thread Win32 call to CopyFileEx will be
bool var = true;
unsafe
{
dict["one"] = (IntPtr)(&var); // fill a dictionary
// with cancellation identifier
}
mre.Set();
// Actually Win32 CopyFileEx call will be here
while(true)
{
Console.WriteLine("Dict:{0}", dict["one"]);
Console.WriteLine("Var:{0}", var);
Console.WriteLine("============");
Thread.Sleep(1000);
}
}
}
实际上我对P / Invoke和所有不安全的东西都有点新意,因此犹豫了后一种方法,即在字典中保存对本地值的引用并将此值暴露给另一个线程。
关于如何将指针暴露给布尔值以支持取消复制操作的任何其他想法?
答案 0 :(得分:1)
啊,这就是其他主题的内容。有一个更好的方法来实现这一点,CopyFileEx()也支持进度回调。该回调允许您更新UI以显示进度。它允许您取消副本,只需从回调中返回PROGRESS_CANCEL。
访问pinvoke.net以获取您需要的回调委托声明。
答案 1 :(得分:0)
如果您的目标是支持取消正在进行的文件复制操作,我建议您使用CopyProgressRoutine。这在复制期间会定期调用,并允许您使用返回代码取消操作。它将允许您异步取消操作,而无需直接处理指针。
private class FileCopy
{
private bool cancel = false;
public void Copy(string existingFile, string newFile)
{
if (!CopyFileEx(existingFile, newFile,
CancelableCopyProgressRoutine, IntPtr.Zero, IntPtr.Zero, 0))
{
throw new Win32Exception();
}
}
public void Abort()
{
cancel = true;
}
private CopyProgressResult CancelableCopyProgressRoutine(
long TotalFileSize,
long TotalBytesTransferred,
long StreamSize,
long StreamBytesTransferred,
uint dwStreamNumber,
CopyProgressCallbackReason dwCallbackReason,
IntPtr hSourceFile,
IntPtr hDestinationFile,
IntPtr lpData)
{
return cancel ? CopyProgressResult.PROGRESS_CANCEL :
CopyProgressResult.PROGRESS_CONTINUE;
}
// Include p/inovke definitions from
// http://www.pinvoke.net/default.aspx/kernel32.copyfileex here
}
如果您确实想使用pbCancel参数,那么手动分配非托管内存可能是最安全的方法。获取局部变量的地址有点危险,因为一旦变量超出范围,指针将不再有效。
您还可以在对象中使用布尔字段而不是布尔局部变量,但是您需要将其固定在内存中以防止垃圾收集器移动它。您可以使用fixed语句或使用GCHandle.Alloc来执行此操作。
答案 2 :(得分:0)
也许我错过了什么,但为什么你不能只使用defn已经@ http://pinvoke.net/default.aspx/kernel32/CopyFileEx.html然后在取消时将ref int(pbCancel)设置为1?