Of course it is possible. The callback function is called in the context of the stream that CopyFileEx calls. If you need to synchronize some user interface commands, use the usual TThread.Synchronize Delphi or any other synchronization methods between threads that you want.
The callback function cannot be a stream class method. It must match the signature dictated by the API, so it must be a standalone function. When you declare it correctly, you will not need to use the @ operator when you pass it to CopyFileEx .
function CopyProgressRoutine(TotalFileSize, TotalBytesTransferred: Int64; StreamSize, StreamBytesTransferred: Int64; dwStreamNumber, dwCallbackReason: DWord; hSourceFile, hDestinationFile: THandle; lpData: Pointer): DWord; stdcall;
You can give the callback function access to the associated stream object with the lpData parameter. Pass a reference to the stream object for this parameter when you call CopyFileEx :
procedure TCopyThread.Execute; begin ... CopyResult := CopyFileEx(CurrentName, NewName, CopyProgressRoutine, Self, @Cancel, CopyFlags); ... end;
With access to the stream object, you can call methods on this object, including its own execution procedure, so the following may constitute the completeness of an autonomous function. It can delegate everything else back to your object's method. Here I assumed that the method has all the same parameters as the stand-alone function, except that it omits the lpData parameter, because it will be passed implicitly as the Self parameter.
function CopyProgressRoutine; var CopyThread: TCopyThread; begin CopyThread := lpData; Result := CopyThread.ProgressRoutine(TotalSize, TotalBytesTransferred, StreamSize, StreamBytesTransferred, dwStreamNumber, dwCallbackReason, hSourceFile, hDestinationFile); end;