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;
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
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
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...
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...
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
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
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
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
CopyFileExW is a synchronous function, if there is any waiting going on
internally it would be inside Windows code.
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...
Ah, right, so the callback is merely about progress reporting. I
understand.
Thanks,
Martin
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.
> > 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
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.