--- ---
Question
Does anyone have an example of using CopyFileEx with a CopyProgressRoutine callback? I have created a function that takes the same parameters as CopyProgressRoutine, but when I pass it using @ or Addr() I get a "Variable Required" error message.

Let's assume you call CopyFileEx and want the callback to update a progress bar. The callback cannot be a method of an object, but you can use the lpData parameter of CopyFileEx to pass any kind of data to the callback, e.g. a form reference. So, if you want to update a progress form from the callback, here's a little project that does the job.

Create a new Delphi project with two forms. Name the main form Form1 and save it as `Unit1.pas`. Name the second form ProgressForm and save it as `Unit2.pas`. Ensure that ProgressForm is not auto-created by the project. Set ProgressForm's FormStyle property to fsStayOnTop.

On the main form drop a TButton named CopyFileButton and a TOpenDialog component named OpenDialog. Create an OnClick event handler for the button with the following code.

    procedure TForm1.CopyFileButtonClick(Sender: TObject);
    begin
      if OpenDialog.Execute then
        DoFileCopy(OpenDialog.FileName, OpenDialog.FileName + '.bak')
    end;

Somewhere above TForm1.CopyFileButtonClick in the implementation section of Unit1 add the following code:

    function CopyCallback(TotalFileSize, TotalBytesTransferred, StreamSize,
      StreamBytesTransferred: Int64; dwStreamNumber, dwCallbackReason: DWORD;
      hSourceFile, hDestinationFile: THandle; progressform: TProgressForm ):
      DWORD; stdcall;
    var
      newpos: Integer;
    const
      PROCESS_CONTINUE = 0;
    begin
      Result := PROCESS_CONTINUE;
      if dwCallbackReason = CALLBACK_CHUNK_FINISHED then
      begin
        newpos := Round(TotalBytesTransferred / TotalFileSize * 100);
        with progressform.Progressbar do
          if newpos <> Position then
            Position := newpos;
        Application.ProcessMessages;
      end;
    end;

    function DoFilecopy(const source, target: string): Boolean;
    var
      progressform: TProgressForm;
    begin
      progressform := TProgressform.Create(Form1);
      try
        progressform.Show;
        Application.ProcessMessages;
        Result := CopyFileEx(
          PChar(source), PChar(target), @CopyCallback,
          Pointer(progressform), @progressform.FCancel, 0
        );
     finally
       progressform.Hide;
       progressform.free;
     end;
    end;

DoFileCopy sets up the file copy operation and displays the progress dialog box (which we'll come to in a moment). CopyCallback is the CopyProgressRoutine. This callback calculates the progress of the file copy to date and updates the progress bar on the progress form accordingly.

Now for the progress form. Drop a TProgessBar named ProgressBar and a TButton named AbortButton on ProgressForm. Add a public Boolean field named fCancel to TProgressForm and add an OnClick event handler to AbortButton as follows:

    procedure TProgressForm.AbortButtonClick(Sender: TObject);
    begin
      fCancel := True;
    end;

Add a reference to Unit2 in the uses clause of Unit1.

You can now compile and run the program. Click the button in the main form to display the open file dialog box. Select a file to copy – something largish, say 60MB should enable the progress dialog to display for long enough to see what's happening. OK the dialog box to start the file copy. The progress dialog will now be displayed, and will update the progress bar as the copying proceeds. Try clicking the progress dialog's Abort button to cancel the copy.

Tip revised and made to work correctly by DelphiDabbler.