CopyFileEx可以从辅助线程调用吗?

7

在一个线程中调用CopyFileEx和CopyCallback/ProgressRoutine函数(进度条位置将同步)是否可行和正确?

我能否在一个线程中声明CopyCallback/ProgressRoutine函数?我在CopyFileEx上得到错误:“需要变量”@ProgressRoutine。

1个回答

13
当然可以。回调函数将在调用CopyFileEx的线程上下文中被调用。如果你需要同步一些UI命令,请使用Delphi的常规TThread.Synchronize或者其他你想要使用的线程间同步技术。
回调函数不能是线程类的一个方法。它需要匹配API所规定的签名,因此必须是一个独立的函数。当你正确声明它时,在将其传递给CopyFileEx时就不需要使用@运算符了。
function CopyProgressRoutine(TotalFileSize, TotalBytesTransferred: Int64;
  StreamSize, StreamBytesTransferred: Int64;
  dwStreamNumber, dwCallbackReason: DWord;
  hSourceFile, hDestinationFile: THandle;
  lpData: Pointer): DWord; stdcall;

您可以使用lpData参数让回调函数访问相关联的线程对象。在调用CopyFileEx时,将对该参数传递线程对象的引用:

procedure TCopyThread.Execute;
begin
  ...
  CopyResult := CopyFileEx(CurrentName, NewName, CopyProgressRoutine, Self,
    @Cancel, CopyFlags);
  ...
end;

有了对线程对象的访问,您可以调用该对象的方法,包括其自己的进度例程,因此以下内容可以构成独立函数的全部内容。它可以将其他所有内容委托给您对象的一个方法。在这里,我假设该方法具有与独立函数完全相同的参数,除了省略了lpData参数,因为它将作为Self参数隐式传递。

function CopyProgressRoutine;
var
  CopyThread: TCopyThread;
begin
  CopyThread := lpData;
  Result := CopyThread.ProgressRoutine(TotalSize, TotalBytesTransferred,
    StreamSize, StreamBytesTransferred, dwStreamNumber,
    dwCallbackReason, hSourceFile, hDestinationFile);
end;

在我看来,设置 TProgressBar.Position 不需要使用 TThread.SynchronizeTProgressBar.SetPosition 方法从不分配控件的句柄,并通过 SendMessage 调用自身切换线程上下文。 - kludg
7
技术上讲,@Serg,存在竞争条件。TProgressBar在读取Handle属性之前会检查HandleAllocated。如果句柄已经被分配,但在读取Handle之前被销毁,那么句柄将在错误的线程中重新分配。尽管这种情况不太可能发生,但TProgressBar可能是安全的。总的来说,UI更新应该与UI线程同步。 - Rob Kennedy
我能在一个线程中声明CopyCallback/ProgressRoutine函数吗?我在CopyFileEx上使用@ProgressRoutine时会收到“需要变量”的错误提示。 - maxfax
你可以使用队列而不是完全锁定同步TThread.Synchronize。 - Warren P

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接