Можно ли вызвать CopyFileEx из вторичного потока?

Можно ли и правильно ли вызывать функции CopyFileEx и CopyCallback/ProgressRoutine (ProgressBar.Position будет синхронизирован) из потока?

Могу ли я объявить функцию CopyCallback/ProgressRoutine в потоке? Я получаю сообщение об ошибке: «Требуется переменная» в CopyFileEx на @ProgressRoutine.


person maxfax    schedule 16.06.2011    source источник


Ответы (1)


Конечно, это возможно. Функция обратного вызова будет вызываться в контексте потока, вызывающего CopyFileEx. Если вам нужно синхронизировать некоторые команды пользовательского интерфейса, используйте обычную TThread.Synchronize Delphi или любую другую технику синхронизации между потоками, которую вы хотите.

Функция обратного вызова не может быть методом класса потока. Он должен соответствовать подписи, продиктованной 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;
person Rob Kennedy    schedule 16.06.2011
comment
Настройка TProgressBar.Position не требует TThread.Synchronize IMO. Метод TProgressBar.SetPosition никогда не выделяет дескриптор элемента управления и работает через вызов SendMessage, который сам переключает контекст потока. - person kludg; 16.06.2011
comment
Технически, @Serg, есть состояние гонки. TProgressBar проверяет HandleAllocated перед чтением свойства Handle. Если дескриптор уже был выделен, но был уничтожен до чтения Handle, то дескриптор будет повторно выделен в неправильном потоке. Это маловероятно, так что TProgressBar, вероятно, в безопасности. Однако в целом обновления пользовательского интерфейса должны синхронизироваться с потоком пользовательского интерфейса. - person Rob Kennedy; 16.06.2011
comment
Могу ли я объявить функцию CopyCallback/ProgressRoutine в потоке? Я получаю сообщение об ошибке: требуется переменная в CopyFileEx на @ProgressRoutine. - person maxfax; 16.06.2011
comment
Однако вы можете поставить в очередь вместо полностью заблокированной синхронизации с TThread.Synchronize. - person Warren P; 17.06.2011