We help IT Professionals succeed at work.

Copying Files From A Directory

petelazell
petelazell asked
on
I am having a problem copying files from a directory using the Windows API SHFileOperation function.

I have a constant which is the root directory for where to copy the files to and then concatenate the username on the constant to form the full path of where to copy the files. EG.

const
  ROOT_DIRECTORY = 'C:\My Directory';

var
  sFromDir;
  sFromDir := ROOT_DIRECTORY +'\Users Directory'\';

When I then setup the SHFILEOPSTRUCT and use the SHFileOperation it will work fine for a couple times and then says 'Cannot copy file. File system error (1026).

However if i pass the pFrom part of the SHFILEOPSTRUCT as a literal everything works fine, even though it has exactly the same value????

Any help at all will be greatly appreciated!!
Comment
Watch Question

Commented:
Why not to use CopyFile api function:

CopyFile('c:\autoexec.bat', 'd:\autoexec.bak', false);

or

CopyFile(PChar(Edit1.Text), PChar(Edit2.Text), fase);

Motaz

Commented:
Hello petelazell,

I dont know how do you setup the Shfileopstruct but this is an exmaple:

procedure TForm1.Button1Click(Sender: TObject);
 var
   F : TShFileOpStruct;
 begin
   F.Wnd := Handle;     // if 0, then no parent and can task switch away
   F.wFunc := FO_COPY;
   F.pFrom := 'D:\SPEEDIS.AVI';
   F.pTo := 'C:\WINDOWS\DESKTOP\SPEEDIS.AVI';
   F.fFlags := FOF_ALLOWUNDO or FOF_RENAMEONCOLLISION;
   if ShFileOperation(F) <> 0 then ShowMessage('Copy Failed');
 end;

good luck ;-)

Cesario
Russell LibbySoftware Engineer, Advisory
CERTIFIED EXPERT
Top Expert 2005

Commented:

petelazell,

Microsoft indicates that both pFrom and pTo are double-null terminated strings. I have had similar problems in the past using a single null terminated string (pchar).

per MS:

Address of a buffer to specify one or more source file names. These names must be fully qualified paths. Standard DOS wild cards, such as "*", are permitted in the file-name position. Although this member is declared as a null-terminated string, it is used as a buffer to hold multiple file names. Each file name must be terminated by a single NULL character. An additional NULL character must be appended to the end of the final name to indicate the end of pFrom. (also applies to pTo)

Hope this helps,
Russell

Commented:
petelazell,
  The problem is the space in the directory name. You can circumvent this by either using a 8.3 type filename ,"C:\Mydirec~1" or if you are working in NT or W2K you can quote the directory name :

Quotedstr('C:\My Directory');




Good luck!!
CERTIFIED EXPERT

Commented:
this code works for me even with spaces in the file name

var
FileName, toDir: String;
shFileOpRec : TSHFileOpStruct;


Filename := 'E:\Go Mo\New file.exe';
FileName := FileName+#0;

toDir := 'F:\New Folder';
with shFileOpRec do
begin
 Wnd    := Handle;
 wFunc  := FO_COPY;
 pFrom  := PAnsiChar(FileName);
 pTo    := PAnsiChar(toDir);
 fFlags :=  FOF_FILESONLY;
 hNameMappings := nil;
 lpszProgressTitle     := 'Title';
 end;
Commented:
iv aready done someting like this the unit
//////////////////////////
unit copy;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, FileCtrl;

type
  TForm1 = class(TForm)
    Button1: TButton;
    from: TEdit;
    too: TEdit;
    Label1: TLabel;
    Label2: TLabel;
    FileListBox1: TFileListBox;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    procedure writefiles;
  end;

var
  Form1: TForm1;

implementation
var
screate:string;
{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
del:integer;
begin
if from.text=too.Text then screate:=extractfilepath(application.exename)+'temp\'
else screate:=too.text;
writefiles;
if screate<>too.Text then
begin
showmessage('insert next disk');
from.text:=extractfilepath(application.exename)+'temp\';
screate:=too.text;
writefiles;
for del:=0 to FileListBox1.Items.Count-1 do
begin
deletefile(FileListBox1.Items.Strings[del]);
end;
end;
end;
procedure tform1.writefiles;
var
  stream1, stream2:TStream;
  i:integer;
  begin
  FileListBox1.Directory:=From.Text;
for i:=0 to FileListBox1.Items.Count-1 do
begin
stream1:=TFileStream.Create(FileListBox1.Items.Strings[i],fmOpenRead or fmShareDenyWrite);
stream2 := TFileStream.Create(screate+FileListBox1.Items.Strings[i], fmcreate or fmShareDenyRead);
stream2.CopyFrom(Stream1,Stream1.Size);
end;
stream2.Free;
stream1.Free;
  end;
end.
////////////////////////////////////
heres the form
/////////////////////////////////////
object Form1: TForm1
  Left = 192
  Top = 107
  Width = 167
  Height = 112
  Caption = 'Copy'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object Label1: TLabel
    Left = 0
    Top = 8
    Width = 23
    Height = 13
    Caption = 'From'
  end
  object Label2: TLabel
    Left = 0
    Top = 32
    Width = 13
    Height = 13
    Caption = 'To'
  end
  object Button1: TButton
    Left = 0
    Top = 56
    Width = 153
    Height = 25
    Caption = 'Copy'
    TabOrder = 0
    OnClick = Button1Click
  end
  object from: TEdit
    Left = 32
    Top = 8
    Width = 121
    Height = 21
    TabOrder = 1
  end
  object too: TEdit
    Left = 32
    Top = 32
    Width = 121
    Height = 21
    TabOrder = 2
  end
  object FileListBox1: TFileListBox
    Left = 8
    Top = 8
    Width = 145
    Height = 73
    FileType = [ftReadOnly, ftHidden, ftSystem, ftArchive, ftNormal]
    ItemHeight = 13
    TabOrder = 3
    Visible = False
  end
end
///////////////////////////////////////////

Author

Commented:
Sorry I have taken so long to get respond to your answer!! Thanks for the information, worked a treat!

Explore More ContentExplore courses, solutions, and other research materials related to this topic.