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

File Copy Issues in Delphi

1,257 views
Skip to first unread message

Mad Martian

unread,
Jun 15, 2001, 2:08:15 PM6/15/01
to
I have tried several methods of copying files in Delphi. ALL of them have
shortcomings. Of the sortcomings I list here, can any be eliminated?

1. Memory Stream and File Stream have a problem overwriting files on a
network. They are not read-only files. Overwriting the same files on a local
drive works fine.

2. LZCopy works great most of the time, except it gets intermittent "invalid
souce file handle" for no apparent reason.

3. Shell to xcopy would work great except it can't seem to handle spaces in
the file names (even on Win2k) and DOS windows popup with each file transfer
(I need no popups). Does anyone have a list of all the xcopy error codes?

4. TShFileOpStruct seems to be the most reliable method, but is very slow
and it pops up a window to replace files. I need it to overwrite files by
default without popping up a window. Does anyone have a list of all the
TshFileOpStruct error codes? How about all the operating mode switches?

I skipped the "block read-write" method since it is the archaic way of
copying files.

Here is the source code I used for these, including 2 methods of invoking
LZCopy (1 of which does not read read-only files!). Note that for methods
that did not save the file date, I invoke a function to copy the original
file date (at the end of this post), though I wish there was a simpler
function without having to open the files again.

{memory stream}
{cannot overwrite some files on a network}
function filecopya(const sourcepath, targetpath: string): integer;
var
memstream: tmemorystream;
ferror: integer;
begin
{ progressmsg.lines.add('STARTING FILECOPY A'); }
ferror:= 0;
memstream:= tmemorystream.create;
try
try
if allmsgon then progressmsg.lines.add('Reading from source file>
'+sourcepath);
memstream.LoadFromFile(sourcepath);
if allmsgon then progressmsg.lines.add('Writing to target file>
'+targetpath);
memstream.SaveToFile(targetpath);
ferror:= copydate(sourcepath,targetpath);
if allmsgon then progressmsg.lines.add('Copying attributes to>
'+targetpath);
if ferror = 0 then begin
ferror:= filesetattr(targetpath,filegetattr(sourcepath));
if ferror <> 0 then ferror:= 3;
end
else ferror:= 4;
except
on EFOpenError do ferror:= 1;
on EFCreateError do ferror:= 2;
else
ferror:= 6;
end;
finally
memstream.Free;
end;
result:= ferror;
end;

{Filestream}
{cannot overwrite some files on a network}
function FileCopyb( const sourcepath, targetpath: String ): integer;
Var
source,target: TFileStream;
ferror: integer;
Begin
ferror:= 0;
try
try
if allmsgon then progressmsg.lines.add('Reading from source>
'+sourcepath);
source := tfilestream.create(sourcepath,fmopenread);
if allmsgon then progressmsg.lines.add('Writing to target>
'+targetpath);
target := tfilestream.create(targetpath,fmopenwrite or fmcreate);
if allmsgon then progressmsg.lines.add('Copying file to target>
'+targetpath);
target.copyfrom(source,0);
if allmsgon then progressmsg.lines.add('Copying date to target>
'+targetpath);
ferror:= filesetdate(target.handle,filegetdate(source.handle));
if ferror = 0 then begin
if allmsgon then progressmsg.lines.add('Copying attributes to
target> '+targetpath);
ferror:= filesetattr(targetpath,filegetattr(sourcepath));
end;
except
on EFOpenError do ferror:= 1;
on EFCreateError do ferror:= 2;
else
ferror:= 3;
end;
finally
target.free;
source.free;
end;
result:= ferror;
End;

{LZCopy}
{Cannot read read-only files(!), intermittent invalid souce file handle, see
better LZCopy method further down}
function filecopyc(sourcepath, targetpath: string): integer;
var
FromFile, ToFile: File;
ferror: integer;
begin
ferror:= 0;
AssignFile(FromFile, sourcepath); { Assign FromFile to FromFileName }
AssignFile(ToFile, targetpath); { Assign ToFile to ToFileName }
try
try
if allmsgon then progressmsg.lines.add('Opening source> '+sourcepath);
Reset(FromFile); { Open file for input }
except
ferror:= 1;
end;
if ferror = 0 then try
if allmsgon then progressmsg.lines.add('Opening target> '+targetpath);
Rewrite(ToFile); { Create file for output }
except
ferror:= 2;
end;
if ferror = 0 then try {copying file}
if allmsgon then progressmsg.lines.add('Copying source to target>
'+targetpath);
ferror:= LZCopy(TFileRec(FromFile).Handle, TFileRec(ToFile).Handle);
if ferror < 0 then case ferror of
LZERROR_BADINHANDLE : ferror:= 11;
LZERROR_BADOUTHANDLE : ferror:= 12;
LZERROR_BADVALUE : ferror:= 13;
LZERROR_GLOBALLOC : ferror:= 14;
LZERROR_GLOBLOCK : ferror:= 15;
LZERROR_READ : ferror:= 16;
LZERROR_UNKNOWNALG : ferror:= 17;
LZERROR_WRITE : ferror:= 18;
end
else ferror:= 0;
except
ferror:= 6;
end;
finally
try
CloseFile(ToFile); { Close ToFile }
except
end;
try
CloseFile(FromFile); { Close FromFile }
except
end;
end;
if ferror = 0 then ferror:= copydate(sourcepath,targetpath);
if ferror = 0 then begin
if allmsgon then progressmsg.lines.add('Copying attributes to target>
'+targetpath);
ferror:= filesetattr(targetpath,filegetattr(sourcepath));
end;
result:= ferror;
end; {filecopyc}


{Shell to xcopy}
{cannot handle spaces in filenames, annoying windows pop up}
function FileCopyd(const sourcepath, targetpath: string): integer;
var
SEInfo: TShellExecuteInfo;
ExitCode: DWORD;
ExecuteFile: string;
begin
ExecuteFile:='c:\winnt\system32\xcopy.exe';
FillChar(SEInfo, SizeOf(SEInfo), 0);
SEInfo.cbSize := SizeOf(TShellExecuteInfo);
with SEInfo do begin
fMask := SEE_MASK_NOCLOSEPROCESS;
Wnd := Application.Handle;
lpFile := PChar(ExecuteFile);
{contains the
application parameters.
R:overwrite readonly, H:copy hidden/system, Y:overwrite, Q:quiet, K:copy
attributes, X:copy audit settings}
lpParameters := PChar('/hrkqxy '+sourcepath+' '+targetpath);
{specifies the
name of the working directory.
If ommited, the current directory is used.}
// lpDirectory := PChar(insertnameofstartdirectoryhere);
nShow := SW_SHOWNORMAL;
end;
if ShellExecuteEx(@SEInfo) then begin
repeat
Application.ProcessMessages;
GetExitCodeProcess(SEInfo.hProcess, ExitCode);
until (ExitCode <> STILL_ACTIVE) or Application.Terminated;
end
else ShowMessage('Error starting Calc!');
progressmsg.lines.add('EXIT CODE> '+floattostr(exitcode));
result:= exitcode;
end; {filecopyd}


{TShFileOpStruct}
{very slow, annoying confirmation windows pop up}
function filecopye(sourcepath, targetpath: string; Protect: boolean):
integer;
{ Copies files or directory to another directory. }
{protect must be false or will add "copy of"}
var
F: TShFileOpStruct;
tmp1, tmp2: string;
ferror: integer;
begin
ferror:= 0;
FillChar(F, SizeOf(F), #0);
F.Wnd := 0;
F.wFunc := FO_COPY;
{ Add an extra null char }
tmp1 := sourcepath + #0;
tmp2 := targetpath + #0;
try
if allmsgon then progressmsg.lines.add('Opening source> '+sourcepath);
F.pFrom := PChar(tmp1);
except
ferror:= 1;
end;
if ferror= 0 then try
if allmsgon then progressmsg.lines.add('Opening target> '+targetpath);
F.pTo := PChar(tmp2);
except
ferror:= 2;
end;
if ferror = 0 then try
if allmsgon then progressmsg.lines.add('Copying source to target>
'+targetpath);
if Protect then F.fFlags := FOF_RENAMEONCOLLISION or FOF_SIMPLEPROGRESS
else F.fFlags := FOF_SIMPLEPROGRESS;
except
ferror:= 6;
end;
F.fAnyOperationsAborted := False;
F.hNameMappings := nil;
Result := ShFileOperation(F);
end; {filecopye}

{LZCopy method 2 (better than LZCopy method 1)}
{intermittent invalid souce file handle}
function filecopyf(sourcepath,targetpath : string) : integer;
var
sourcefile, targetfile : integer;
ferror: integer;
Msg: string;

begin
ferror:= 0;
try
try
if allmsgon then progressmsg.lines.add('Opening source> '+sourcepath);
sourcefile := FileOpen(sourcepath,0); { Open ReadOnly = 0,
Write=1, Readwrite=2}
except
ferror:= 1;
end;
if ferror = 0 then try
if allmsgon then progressmsg.lines.add('Opening target> '+targetpath);
targetfile := FileCreate(targetpath);
except
ferror:= 2;
end;
if ferror = 0 then begin
if allmsgon then progressmsg.lines.add('Copying source to target>
'+targetpath);
ferror := LZCopy(sourcefile,targetfile);
end;
finally
FileClose(sourcefile);
FileClose(targetfile);
end;
if ferror < 0 then case ferror of
LZERROR_BADINHANDLE : ferror:= 11;
LZERROR_BADOUTHANDLE : ferror:= 12;
LZERROR_BADVALUE : ferror:= 13;
LZERROR_GLOBALLOC : ferror:= 14;
LZERROR_GLOBLOCK : ferror:= 15;
LZERROR_READ : ferror:= 16;
LZERROR_UNKNOWNALG : ferror:= 17;
LZERROR_WRITE : ferror:= 18;
end
else ferror:= 0;
if ferror = 0 then begin
if allmsgon then progressmsg.lines.add('Copying attributes to target>
'+targetpath);
ferror:= filesetattr(targetpath,filegetattr(sourcepath));
end;
result := ferror;
end;

function filecopy(const sourcepath, targetpath: string): integer;
begin
{ progressmsg.lines.add('STARTING CURRENTSAVE'+inttostr(currentsave)); }
case currentsave of
1: filecopy:= filecopya(sourcepath,targetpath);
2: filecopy:= filecopyb(sourcepath,targetpath);
3: filecopy:= filecopyc(sourcepath,targetpath);
4: filecopy:= filecopyd(sourcepath,extractfiledir(targetpath));
5: filecopy:= filecopye(sourcepath,extractfiledir(targetpath),false);
6: filecopy:= filecopyf(sourcepath,targetpath);
end;
end;

{for copy methods that don't keep the original date automatically}
function copydate(source,target: string): integer;
var
shandle,thandle: integer;
ferror: integer;
begin
ferror:= 0;
try
try
if allmsgon then progressmsg.lines.add('Opening file to read date
from> '+targetpath);
shandle := FileOpen(source, 0);
if allmsgon then progressmsg.lines.add('Opening file to write date to
target> '+targetpath);
thandle := FileOpen(target, 1);
if allmsgon then progressmsg.lines.add('Copying date to target>
'+targetpath);
ferror:= filesetdate(thandle,filegetdate(shandle));
if ferror <> 0 then ferror:= 4;
except
ferror:= 4;
end;
finally
fileclose(thandle);
fileclose(shandle);
end;
result:= ferror;
end;

AlanGLLoyd

unread,
Jun 15, 2001, 4:55:33 PM6/15/01
to
In article <jasW6.178454$p33.3...@news1.sttls1.wa.home.com>, "Mad Martian"
<ne...@madmartian.com> writes:

>I have tried several methods of copying files in Delphi. ALL of them have
>shortcomings. Of the sortcomings I list here, can any be eliminated?
>
>1. Memory Stream and File Stream have a problem overwriting files on a
>network. They are not read-only files. Overwriting the same files on a local
>drive works fine.
>

I would be inclined to Free the streams before setting the attributes, but it
might not make any difference.

I don't know whether THandleStream is any better, or even the mmio file
functions (they have their own buffers).

I have had problems on some network file copy where it errors with error code
103 - "Too many semaphores" whatever _that_ means.

I have a sneaking feeling that there is some network file cache which does not
get flushed.

Alan Lloyd
alang...@aol.com

Thomas Nelvik

unread,
Jun 15, 2001, 5:57:38 PM6/15/01
to
Mad Martian wrote:
> I have tried several methods of copying files in Delphi. ALL of them
have
> shortcomings. Of the sortcomings I list here, can any be eliminated?
>
> 4. TShFileOpStruct seems to be the most reliable method, but is very
slow
> and it pops up a window to replace files. I need it to overwrite
files by
> default without popping up a window. Does anyone have a list of all
the
> TshFileOpStruct error codes? How about all the operating mode
switches?

You could try to include FOF_NOCONFIRMATION with the Flags.
Error codes should be in the "win32 error code listing" in win32.hlp

PS: There is also the straight forward CopyFile() function in
Windows.pas.

HTH
-ThomasN

Jan Philips

unread,
Jun 15, 2001, 6:02:47 PM6/15/01
to
"Mad Martian" <ne...@madmartian.com> wrote:

>I skipped the "block read-write" method since it is the archaic way of
>copying files.

Does it suffer from any of the problems with the other methods?

Mad Martian

unread,
Jun 15, 2001, 6:39:40 PM6/15/01
to
The only mention of the copyfile routine in the help says almost nothing:

"The runtime library does not provide any routines for copying a file.
However, you can directly call the Windows API CopyFile function to copy a
file. Like most of the Delphi runtime library file routines, CopyFile takes
a filename as a parameter, not a Window Handle. When copying a file, be
aware that the file attributes for the existing file are copied to the new
file, but the security attributes are not. CopyFile is also useful when
moving files across drives because neither the Delphi RenameFile function
nor the Windows API MoveFile function can rename/move files across drives. "

-Mike

"Thomas Nelvik" <thomas...@chello.no> wrote in message
news:mxvW6.6597$qR5.5...@news01.chello.no...

Blade

unread,
Jun 15, 2001, 8:04:14 PM6/15/01
to
Whats wrong with CopyFile(olddir,newdir,true/false) ?

--
Blade
"Mad Martian" <ne...@madmartian.com> wrote in message
news:jasW6.178454$p33.3...@news1.sttls1.wa.home.com...

KBH

unread,
Jun 16, 2001, 12:43:12 AM6/16/01
to
Except when loading a memo control or a String List (or something similar),
most D programmers use the Pascal file procedures. So I would think that
Block read and write would be fast and reliable.


KBH

unread,
Jun 16, 2001, 2:26:27 AM6/16/01
to
Or there is FileRead and FileWrite. Those things use file handles instead of
Pascal file variables and so the help files warn not to mix the two things.
(But all are listed together under 'File Mangement Routines'.)

But if you are having trouble with file handles...then BlockRead and
BlockWrite with a record size of 1 is the idea of the first message.


Thomas Nelvik

unread,
Jun 16, 2001, 8:02:20 AM6/16/01
to
Mad Martian wrote:
> The only mention of the copyfile routine in the help says almost
nothing:
>
> "The runtime library does not provide any routines for copying a
file.
> However, you can directly call the Windows API CopyFile function to
copy a
> file. Like most of the Delphi runtime library file routines,
CopyFile takes
> a filename as a parameter, not a Window Handle. When copying a file,
be
> aware that the file attributes for the existing file are copied to
the new
> file, but the security attributes are not. CopyFile is also useful
when
> moving files across drives because neither the Delphi RenameFile
function
> nor the Windows API MoveFile function can rename/move files across
drives. "
>
> -Mike


This is from Delphi's help, not "Win32 Programmers reference"
(win32.hlp).
Write "CopyFile" in your editor and push F1 to get to the correct
page.

The WinAPI CopyFile() does truly _not_ copy security attributes.
If this is needed, then use the CopyFileEx(), available on WinNT
platform.

HTH
-ThomasN


Jan Philips

unread,
Jun 16, 2001, 3:17:52 PM6/16/01
to
"Mad Martian" <ne...@madmartian.com> wrote:

>I have tried several methods of copying files in Delphi.

If the files aren't too large, you can use LoadFromFile and
SaveToFile.

Ingolf

unread,
Jun 18, 2001, 11:18:52 AM6/18/01
to
Try:

Filemode:=0:

When creating first form.

--
Regards
Ingolf

"Mad Martian" <ne...@madmartian.com> wrote in message
news:jasW6.178454$p33.3...@news1.sttls1.wa.home.com...

Mad Martian

unread,
Jun 18, 2001, 3:22:25 PM6/18/01
to
Thanks Thomas. I got copyfile working, but copyfileex is what I really want
to use.

I tried the following:

res:= copyfileex(pchar(sourcepath),pchar(targetpath),null,null,false,0);

and got the error "incompatable types: Variant and Pointer" with the cursor
between the 2 nulls. What am I doing wrong?

Also, do you know the difference between "Copyfile", "CopyfileA", and
"CopyfileW"?

Thanks,

-Mike

"Thomas Nelvik" <thomas...@chello.no> wrote in message

news:gVHW6.6639$qR5.5...@news01.chello.no...

Thomas Nelvik

unread,
Jun 18, 2001, 3:56:24 PM6/18/01
to
Mad Martian wrote:
> Thanks Thomas. I got copyfile working, but copyfileex is what I
really want
> to use.
>
> I tried the following:
>
> res:=
copyfileex(pchar(sourcepath),pchar(targetpath),null,null,false,0);
>
> and got the error "incompatable types: Variant and Pointer" with the
cursor
> between the 2 nulls. What am I doing wrong?

In pascal, a standard null-pointer is called "NIL".


> Also, do you know the difference between "Copyfile", "CopyfileA",
and
> "CopyfileW"?

With most of the windows API functions there is an Ansi- and a
WideChar
version, hence the "A" or "W" suffix. Only difference is that the one
expect AnsiStrings and the other WideStrings as parameters. If you see
the "Windows.pas" unit, you find that Delphi uses the Ansi-version as
"default", that is "CopyFile" _means_ "CopyFileA" , and that will do
in most cases.

Regards
-ThomasN


Mad Martian

unread,
Jun 18, 2001, 6:30:57 PM6/18/01
to
Which problem will that resolve? And what exactly does it do?


"Ingolf" <DONT_SPA...@musling.dk> wrote in message
news:YFqX6.3982$VQ4.6...@news010.worldonline.dk...

Mad Martian

unread,
Jun 18, 2001, 6:32:17 PM6/18/01
to
I'm using that in the first method, with memory streams. Is there something
other than memory streams that use loadfromfile and savetofile?


"Jan Philips" <jud.mccra...@mindspring.com> wrote in message
news:65cnitsiniop6t2ag...@4ax.com...

0 new messages