Using FileCopyEx and CopyProgressRoutine?

Jan 16, 2011 at 11:20am
Hi!
I am trying to create a program with different functionalities, among which copying a large file from one location to another. The program itself is not created directly in C++, but since the original programming language cannot do this in a good way, I have chosen to create a DLL in C++ for copying the file and performing other minor tasks.
Everything works fine; for the copying process I have found a function called CopyFileEx(...), which is described here: http://msdn.microsoft.com/en-us/library/aa363852%28v=vs.85%29.aspx
The problem is: I am trying to real time retrieve the progress in bytes (or anything) of the copy, for creating a visible progress bar. I have found that the third argument of CopyFileEx is supposed to be (according to MSDN) "the address of a callback function of type LPPROGRESS_ROUTINE that is called each time another portion of the file has been copied". I have searched for the function called CopyProgressRoutine, whose address should be passed there, and I have also found a MSDN article: http://msdn.microsoft.com/en-us/library/aa363854%28v=vs.85%29.aspx from which I have copy-pasted the function definition (removing the __in's, because otherwise the file won't compile), and
tried to pass the function's address as an argument for CopyFileEx(). But it won't work this way:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <windows.h>
#include <fstream>
#include <iostream>
#include <time.h>
using namespace std;

fstream ifile,ofile;

DWORD CALLBACK CopyProgressRoutine(
  LARGE_INTEGER TotalFileSize,
  LARGE_INTEGER TotalBytesTransferred,
  LARGE_INTEGER StreamSize,
  LARGE_INTEGER StreamBytesTransferred,
  DWORD dwStreamNumber,
  DWORD dwCallbackReason,
  HANDLE hSourceFile,
  HANDLE hDestinationFile,
  LPVOID lpData
);

int main(){
    clock_t current_time=clock();
    DWORD CALLBACK(*Test)(LARGE_INTEGER,LARGE_INTEGER,LARGE_INTEGER,LARGE_INTEGER,DWORD,DWORD,HANDLE,HANDLE,LPVOID);
    Test=&CopyProgressRoutine;
    CopyFileEx(
        "D:/CPP/Projects/FileCopyExTest/MyBigFile.txt",
        "D:/CPP/Projects/FileCopyExTest/MyBigFileB.txt",
        Test,
        NULL,
        FALSE,
        0
    );
    cout<<(clock()-current_time)<<endl;
    system("PAUSE");
    return 0;
};

This won't compile, throwing me an error like this:
[Linker error] undefined reference to `_Z19CopyProgressRoutine14_LARGE_INTEGERS_S_S_mmPvSO_SO_@52'
Id returned 1 exit status.

Note that the file is successfully copied if I pass NULL for the third argument.

Using Dev-C++ 4.9.9.2

Thank you!

-Ramses12
Last edited on Jan 16, 2011 at 11:21am
Jan 16, 2011 at 12:54pm
You appear to have defined a new function, called CopyProgressRoutine.

This is not sensible, as that function has already been written. If you include Windows.h, you will have access to it. The linker error means that you have not linked to the library that contains it.
Last edited on Jan 16, 2011 at 12:55pm
Jan 16, 2011 at 1:44pm
Thanks for the reply!

Actually MSDN and some other forum topics around the Internet say that the CopyProgressRoutine function has to be application defined. (anyway not defining the function results in an error saying that the function does not exist even if windows.h was included).

Note: it seems like the Test=&CopyProgressRoutine; line causes the error. Commenting it in and out and using NULL as parameter for CopyFileEx() makes the program compile-able. Is it anything wring with declaring the function pointer?
Jan 16, 2011 at 2:39pm
That depends on what CopyFileEx is doing with that particular input variable.
Jan 16, 2011 at 2:59pm
I got it working!
The actual problem was caused by an unbelievably simple reason: I was not defining the callback function's body. When I added:
1
2
3
4
5
6
7
8
9
10
11
12
13
DWORD CALLBACK CopyProgressRoutine(
  LARGE_INTEGER TotalFileSize,
  LARGE_INTEGER TotalBytesTransferred,
  LARGE_INTEGER StreamSize,
  LARGE_INTEGER StreamBytesTransferred,
  DWORD dwStreamNumber,
  DWORD dwCallbackReason,
  HANDLE hSourceFile,
  HANDLE hDestinationFile,
  LPVOID lpData
){
cout<<"Test"<<endl
};

It compiled without any problems.
Jan 19, 2011 at 4:54pm
I'm sorry to bump this, but now I've got another problem:
MSDN clearly states that CopyProgressRoutine can return the value of PROGRESS_STOP, which causes the function to
Stop the copy operation. It can be restarted at a later time.

And it also states that if I pass FILE_COPY_RESTARTABLE as flag, the
Progress of the copy is tracked in the target file in case the copy fails. The failed copy can be restarted at a later time by specifying the same values for lpExistingFileName and lpNewFileName as those used in the call that failed.


http://msdn.microsoft.com/en-us/library/aa363852
http://msdn.microsoft.com/en-us/library/aa363854

So normally, for pausing the copy process, I need to:
- Call CopyFileEx() with FILE_COPY_RESTARTABLE as flag
- Return PROGRESS_STOP from CopyProgressRoutine when I want to pause the process
- Call CopyFileEx() again, with the same arguments as in the first call, when I want to continue the process.

But when I call CopyFileEx again, it will simply restart from 0, not continuing from the previous progress. Any idea how to solve that?

Many thanks!
Topic archived. No new replies allowed.