Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

CopyFileExW not copying all files

563 views
Skip to first unread message

Mark Garnett

unread,
Dec 14, 2005, 3:31:14 AM12/14/05
to
Hi all,

Firstly, thanks to those that have helped me so far with the ExpandFileName
problems I was having with this function, your suggested advice did the
trick.

I am using the CopyFileExW function to write an app that copies files from
one drive or directory to another. I have run into a strange problem that I
hope somebody can help with. I have written a function to copy the files
which I have pasted below (minus error checking code to save space).
Basically, this function iterates through a list of directory names held
within a TStringList (DirNames), finds all the files in the directory and
then copies them to C:\Temp. Everything seems to work OK at first, however
the procedure does not copy any files contained within sub-directories even
though those directories are contained within the directory list.

For example:

C:\docs\ <----- copies all files no problems
C:\docs\MoreDocs\ <-------copies no files from this directory even though
it finds them OK

I have debugged and confirmed that the source directory contains the correct
path name and files are found in the directory, it just does not copy them.
I have no idea why the CopyFileExW function is failing. The source and
destination directories are correct and the file names are correct. I have
a suspicion that it has something to do with the way I am converting
widestrings/strings to a PWideChar but I really have no idea.

Any help would be greatly appreciated.

Source Code:

procedure TMirrorCopyDlg.CopyFilesIncludeW(RootIndex: integer);
var
CurrentDir: Widestring;
SR: TWin32FindDataW;
Index: integer;
NumFiles: Longword;
Counter: Longword;
DestinationDir: Widestring;
CopyContinue: Boolean;
hFile: THandle;
SourceFile: PWideChar;
begin
// Cycle through each directory looking for files
NumFiles := 0;
CopyContinue := False;
for Counter := 0 to (DirName.Count - 1) do
begin
ProgressBar.StepIt;
CurrentDir := Widestring((DirName[Counter]));
hFile := FindFirstFilew(PWideChar(CurrentDir + '\*.*'), SR);
if hFile <> INVALID_HANDLE_VALUE then
begin
repeat
if SR.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY <>
FILE_ATTRIBUTE_DIRECTORY then
begin
if (StrLICompW(SR.cFileName, '.', 1) <> 0) and
(StrLICompW(SR.cFileName, '..', 2) <> 0) then

begin
Application.ProcessMessages;
DestinationDir := 'C:\Temp\';
SourceFile := PWideChar(CurrentDir + '\' + SR.cFileName);

// Copy the files across to the new location
CopyFileExW(SourceFile,
PWideChar(DestinationDir + SR.cFileName),
@CopyCallback,
MirrorCopyDlg.CurrentFileCopyProgress,
@CopyContinue,
0);
end;
end;
until FindNextFilew(hFile, SR) = False;
end;
Windows.FindClose(hFile);
end;
end;


Chris Morgan

unread,
Dec 14, 2005, 4:56:01 AM12/14/05
to
> I am using the CopyFileExW function to write an app that copies files from
> one drive or directory to another. I have run into a strange problem that
> I hope somebody can help with. I have written a function to copy the
> files which I have pasted below (minus error checking code to save space).
> Basically, this function iterates through a list of directory names held
> within a TStringList (DirNames), finds all the files in the directory and
> then copies them to C:\Temp. Everything seems to work OK at first,
> however the procedure does not copy any files contained within
> sub-directories even though those directories are contained within the
> directory list.

Hi there,

CopyFileEx(W) may not create destination subdirectories which do not already
exist, like the COPY command. You may need to very closely examine the
documentation for CopyFileEx
You have 2 options:
1. Manually create the destination subdirs before calling CopyFileEx(W)
2. Use ShFileOperation instead. This has a couple of advantages:
* you can supply a whole list of files to copy at once, so you don't
need to call it multiple times
* it has an optional built-in progress monitor like when you copy files
in Explorer

There have been lots of previous questions on ShFileOperation - google
for some code examples. Or is there some reason why you are using CopyFileEx
instead?

Cheers,

Chris


mgar...@bigpond.net.au

unread,
Dec 14, 2005, 9:53:50 PM12/14/05
to

Hi Chris,

I don't think that is the problem because all of the files are going
straight to c:\temp so I don't have any subdirectories. Also, in my
app I do create all subdirectories first anyway.

The copied files are going into a flat directory structure.

The reason I am using CopyFileExW is so I can access and copy files
where the path is greater that 256 characters and I don't think
ShFileOperation can do that, although I could be wrong.

Regards

Mark

Mark Garnett

unread,
Dec 15, 2005, 4:24:38 AM12/15/05
to
Hi Chris,

I am fairly sure this is not the problem as all of the files are being
copied into the same directory c:\temp and no subdirectories at all need
creating. Also, in my test application, I do create the sub-directories
before-hand and I still have the same problem.

I am using the CopyFileExW function so I can copy directories and files
where the path is longer than 256 characters and I don't think that
ShFileOperation supports that.

I hope somebody has the answer.....

Thanks anyway.

Mark


"Chris Morgan" <chris.nospam at lynxinfo.co.uk> wrote in message
news:439feb59$1...@newsgroups.borland.com...

Ronaldo Souza

unread,
Dec 15, 2005, 4:27:14 AM12/15/05
to
Mark Garnett wrote:
> // Copy the files across to the new location
> CopyFileExW(SourceFile,
> PWideChar(DestinationDir + SR.cFileName),
> @CopyCallback,
> MirrorCopyDlg.CurrentFileCopyProgress,
> @CopyContinue,
> 0);
Add a ShowMessage(SysErrorMessage(GetLastError)) here to see what it
returns.
HTH,
Ronaldo

Mark Garnett

unread,
Dec 16, 2005, 5:32:13 AM12/16/05
to
Hi,

The last error says "The request was aborted".....not very helpful.

The copy command is being executed because when I trace execution through
the app, my callback function is being executed it's just that nothing is
happening. I have also posted my callback function below just in case there
are any errors there that I have not noticed.

I hope somebody can help.

function CopyCallback(TotalFileSize,
TotalBytesTransferred,
StreamSize,
StreamBytesTransferred: Int64;
dwStreamNumber,
dwCallbackReason: DWORD;
hSourceFile,
hDestinationFile: THandle;
CurrentCopyProgress: TJvXPProgressBar): DWORD;
stdcall;
var
EnCourse: Int64;
begin
Result := PROGRESS_CONTINUE;
If dwCallbackReason = CALLBACK_CHUNK_FINISHED then
begin
EnCourse := Round(TotalBytesTransferred / TotalFileSize * 100);
With MirrorCopyDlg.CurrentFileCopyProgress Do
if EnCourse <> Position then
Position := EnCourse;
Application.ProcessMessages;
end;
end;

"Ronaldo Souza" <nos...@nospam.org> wrote in message
news:43A136F2...@nospam.org...

Peter Below (TeamB)

unread,
Dec 16, 2005, 8:31:19 AM12/16/05
to
In article <43a2975d$1...@newsgroups.borland.com>, Mark Garnett wrote:
> The copy command is being executed because when I trace execution through
> the app, my callback function is being executed it's just that nothing is
> happening. I have also posted my callback function below just in case there
> are any errors there that I have not noticed.
>
> I hope somebody can help.
>
> function CopyCallback(TotalFileSize,
> TotalBytesTransferred,
> StreamSize,
> StreamBytesTransferred: Int64;
> dwStreamNumber,
> dwCallbackReason: DWORD;
> hSourceFile,
> hDestinationFile: THandle;
> CurrentCopyProgress: TJvXPProgressBar): DWORD;
> stdcall;
> var
> EnCourse: Int64;
> begin
> Result := PROGRESS_CONTINUE;
> If dwCallbackReason = CALLBACK_CHUNK_FINISHED then
> begin
> EnCourse := Round(TotalBytesTransferred / TotalFileSize * 100);
> With MirrorCopyDlg.CurrentFileCopyProgress Do

Ahem, you are passing the progress bar instance to update to the routine.
Why don't you use it?

> if EnCourse <> Position then
> Position := EnCourse;
> Application.ProcessMessages;

Remove the ProcessMessages call, instead call the progress bars Update method.

--
Peter Below (TeamB)
Use the newsgroup archives :
http://www.mers.com/searchsite.html
http://www.tamaracka.com/search.htm
http://groups.google.com
http://www.prolix.be


Martin James

unread,
Dec 17, 2005, 5:40:07 AM12/17/05
to
This all looks a bit dubious to me. Windows callback functions usually
require that the initiating thread to wait in an alertable state. Does the
Delphi main thread do this now, while waiting for messages?

Also, it's 'usual', when a callback function has to be called multiple
times, for the completion routine to issue the next function call, rather
than a loop in the initiating thread.

I say 'looks dubious' because I am not sure - I have never, (explicitly),
used CopyFileEx.

If I wanted to perform such a copying operation, I would probably thread it
off and call an 'onProgress' event in the completion routine. If the copy
request cam from the main thread, this event could PostMessage off the
progress reports.

Rgds,
Martin


Angus Robertson - Magenta Systems Ltd

unread,
Dec 17, 2005, 6:14:00 AM12/17/05
to
In article <43a3ea3e$1...@newsgroups.borland.com>,
mjames...@dial.pipex.com (Martin James) wrote:

> This all looks a bit dubious to me. Windows callback functions
> usually require that the initiating thread to wait in an alertable
> state. Does the Delphi main thread do this now, while waiting for
> messages?

I have a component, TMagCopy (http://www.magsys.co.uk/delphi/magxfer.asp)
that uses CopyFileEx (not W) for file copying, using the callback
function. It calls Application.ProcessMessages without causing any
problems.

The main issue with the callback is not updating progress displays too
often, since this will slow down the application. I check GetTickCount
to ensure progress is not updated more than once a second, or less often.

Angus

Martin James

unread,
Dec 17, 2005, 6:51:02 AM12/17/05
to
>
> I have a component, TMagCopy (http://www.magsys.co.uk/delphi/magxfer.asp)
> that uses CopyFileEx (not W) for file copying, using the callback
> function. It calls Application.ProcessMessages without causing any
> problems.

IMHO, if a component calls A.P internally, it should have a health warning:

NOTE: Must not be called from secondary threads
Can cause your event hander to be reentered
Will only work if your main thread is available to process APCs

> The main issue with the callback is not updating progress displays too
> often, since this will slow down the application. I check GetTickCount
> to ensure progress is not updated more than once a second, or less often.

Surely a good idea :)

Rgds,
Martin

Peter Below (TeamB)

unread,
Dec 17, 2005, 3:07:20 PM12/17/05
to
In article <43a3ea3e$1...@newsgroups.borland.com>, Martin James wrote:
> This all looks a bit dubious to me. Windows callback functions usually
> require that the initiating thread to wait in an alertable state. Does the
> Delphi main thread do this now, while waiting for messages?

CopyFileExW is a synchronous function, if there is any waiting going on
internally it would be inside Windows code.

Mark Garnett

unread,
Dec 18, 2005, 1:28:28 AM12/18/05
to
Thanks to everybody for their suggestions.

Peter (TeamB), I changed the callback function so that it now calls the
update method of the progress bar and it works fine, thanks for the tip.

I have also fixed the copy problem and this has taught me a very valuable
lesson. Always look for the simple things first before making the problem
more complex than it has to be. The problem was fixed by changing a couple
of simple things, like changing to using strings, but most importantly,
changing the line

CurrentDir := Widestring((DirName[Counter]));

to

CurrentDir := DirName.Strings[Counter];

A simple and very obvious error! I must have read that function a hundred
times and skipped straight past that line every time.

Mental note to self.......read EVERY line of code, read EVERY line of code
and last but not least read EVERY line of code.

Thanks again to everybody for the help. My application has certainly been
improved by using your ideas.

Regards

Mark


"Martin James" <mjames...@dial.pipex.com> wrote in message
news:43a3...@newsgroups.borland.com...

Martin James

unread,
Dec 18, 2005, 2:02:38 AM12/18/05
to

>
> CopyFileExW is a synchronous function, if there is any waiting going on
> internally it would be inside Windows code.
>

Ah, right, so the callback is merely about progress reporting. I
understand.

Thanks,
Martin


Peter Below (TeamB)

unread,
Dec 18, 2005, 5:49:38 AM12/18/05
to
In article <43a5...@newsgroups.borland.com>, Mark Garnett wrote:
> I have also fixed the copy problem and this has taught me a very valuable
> lesson. Always look for the simple things first before making the problem
> more complex than it has to be.

It's called Occams razor <g>.

> A simple and very obvious error! I must have read that function a hundred
> times and skipped straight past that line every time.
>
> Mental note to self.......read EVERY line of code, read EVERY line of code
> and last but not least read EVERY line of code.

That does not work reliably since the brain seems to have an autocorrect
circuit between retina and visual cortex. You know what's supposed to be there
in the code and so that's what you read <g>. If possible have another person
do the code review, way more effective.

Angus Robertson - Magenta Systems Ltd

unread,
Dec 18, 2005, 6:42:00 AM12/18/05
to
In article <VA.0000c3e...@nomail.please>,
10011...@compuXXserve.com (Peter Below (TeamB)) wrote:

> > Mental note to self.......read EVERY line of code, read EVERY line
> > of code and last but not least read EVERY line of code.
>
> That does not work reliably since the brain seems to have an
> autocorrect circuit between retina and visual cortex.

My most annoying error was a memory leak from a TStringList, both these
lines do the same thing, but the second one leaks memory because it's a
Pchar:

MyString := StringList.Text ;
MyString := StringList.GetText ;

Not obvious atall.

Angus

Peter Below (TeamB)

unread,
Dec 19, 2005, 2:48:44 PM12/19/05
to
In article <memo.2005121...@magsys.co.uk>, Angus Robertson -
Magenta Systems Ltd wrote:
> My most annoying error was a memory leak from a TStringList, both these
> lines do the same thing, but the second one leaks memory because it's a
> Pchar:
>
> MyString := StringList.Text ;
> MyString := StringList.GetText ;
>
> Not obvious atall.

Oh yes, functions like GetText are a booby trap waiting to be sprung on
the hapless programmer. Any function returning a pointer is, basically,
since you have to figure out whether you need to free the returned memory
or not. Bad design in general, but sometimes used for performance reasons.

0 new messages