int __fastcall TForm1::CopyFile ( AnsiString Src, AnsiString Dest, bool Move = false )
{
SHFILEOPSTRUCT *FileOp = new SHFILEOPSTRUCT () ;
FileOp->hwnd = Handle ;
FileOp->wFunc = ( Move ? FO_MOVE : FO_COPY ) ;
FileOp->pFrom = Src.c_str () ;
FileOp->pTo = Dest.c_str () ;
return ( SHFileOperation ( FileOp ) ) ;
}
This function kind of works ok except for a few things. I've stepped through the code to see where the errors are and the only place where the error occurs is when it runs SHFileOperation (FileOp) ; which seems should be the Windows API problem. Here are the problems:-
1. When copying single files some files it pops up with a warning message that Windows cannot find the specified file and to press F5 to refresh and check the file still exists. I've stepped through the program to this point and watched FileExists ( Src ) ; and even after it's gone through that bit FileExists tells me that the file does exist (even though Windows doesn't think it does. I've also tried moving the file from within Windows Explorer just to make sure it's not being accessed and Windows Explorer allows me to move the file with no problems (even though Explorer uses the same bit of Windows API as I'm using!).
2. I thought of another way of moving the files would be to use a wildcard (i.e. A*.*) and see if it would move the files it couldn't find then. When doing this it moved the files it couldn't find without any problems except it got stuck in a loop. I used F7 to step up to the Windows API call and found the point it gets stuck in the loop is somewhere inside the Windows API call. It creates a directory within a directory within a directory about 40 times and then copies the files into the bottom directory (so you end up with all the files located at C:\A\A\A\A\A\A\A\A\A\A\A\A\A\A\A\A\A\A\A\A\A\A\A\A\A\A\A\A\
So does anyone know what am I doing wrong or is there an easier way (that is just as quick) to copy and move files around the system without using Windows API (I was expecting maybe a command like FileCopy to exist in C++ Builder (to go with RenameFile, FileOpen, FileClose, FileSeek, etc, etc) but it doesn't seem to exist). I am using Borland C++ Builder 4 (Build 14.4 (according to the BCB About box I am using Windows 95 4.90 (Build 3000: ) but I'm running Windows ME (another bug I presume!?!?)). I got this version of C++ Builder Professional free with July 2000 PC Plus.
"Ed Mulroy [TeamB]" <e...@mulroy.org> wrote:
>One thing you could do is to use the Windows API functions CopyFile to copy
>files and MoveFile to rename them.
>
>As for the SHFileOperation, I've never seen problems like that.
>
>One thing you do not show that is commonly done with structures used in
>calling Windows API functions is to zero out the structure so that anything
>you've not otherwise initialized in the structure is zero.
>
>Another thing I can see, a serious error, is that new is called to allocate
>a SHFILEOPSTRUCT structure but it is not deleted later. If you must do
>things that way then use an auto_ptr so that it gets deleted for you.
>
>.. Ed
>
>
I wish you good luck in getting it working.
. Ed
>int __fastcall TForm1::CopyFile ( AnsiString Src, AnsiString Dest, bool Move = false )
>{
> SHFILEOPSTRUCT *FileOp = new SHFILEOPSTRUCT () ;
> FileOp->hwnd = Handle ;
> FileOp->wFunc = ( Move ? FO_MOVE : FO_COPY ) ;
> FileOp->pFrom = Src.c_str () ;
> FileOp->pTo = Dest.c_str () ;
> return ( SHFileOperation ( FileOp ) ) ;
>}
This is a wrong code. You did not read the documentation that
accompanies this API in detail. In MSDN it states this for pFrom and
pTo members of SHFILEOPSTRUCT:
"pFrom
Address of a buffer to specify one or more source file names. Multiple
names must be null-separated. The list of names must be double
null-terminated.
pTo
Address of a buffer to contain the name of the destination file or
directory. The buffer can contain multiple destination file names if
the fFlags member specifies FOF_MULTIDESTFILES. Multiple names must be
null-separated. The list of names must be double null-terminated. "
And I repeat "Adress of buffer". Not temporary string returned with
c_str(). c_str() returns adress of temporary const char* that only
exists while the line of code in which it resides is being executed,
not after that. So you assigned adress of temporary string to member
of SHFILEOPSTRUCT and later when file operation is being executed it
tries to read something from that adress and finds garbage. And hence
the error message. You must understand that API knows nothing of
AnsiString. It uses char*, so by assigning src.c_str() to this DOES
NOT COPY string but instead places starting adress of that string to
structure member.
In short you must declare and allocate local char* variables and copy
the strings in these variables. Something like this:
bool ShCopyFile(AnsiString from, AnsiString to)
{
SHFILEOPSTRUCT shstr = {0};
//declare and allocate
char *mfrom = new char[from.Length()+1];
char *mto = new char[to.Length()+1];
//set the contents of string to zeros
memset(mfrom,0,from.Length()+1);
memset(mto,0,to.Length()+1);
//copy strings
strcpy(mfrom,from.c_str());
strcpy(mto,to.c_str());
//Setup the struct
shstr.hwnd = NULL;
shstr.wFunc = FO_COPY;
shstr.pFrom = mfrom;
shstr.pTo = mto;
int retval = ::SHFileOperation(&shstr);
delete [] mfrom;
delete [] mto;
return (retval == 0);
}
And to use this function here is the sample:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
if (ShCopyFile("C:\\temp\\win32.hlp","E:\\Swift\\win32.hlp"))
ShowMessage("I did it!\n");
else
ShowMessage("Failed!\n");
}
>So does anyone know what am I doing wrong or is there an easier way
>(that is just as quick) to copy and move files around the system
>without using Windows API (I was expecting maybe a
>command like FileCopy to exist in C++ Builder
>(to go with RenameFile, FileOpen, FileClose, FileSeek, etc, etc)
>but it doesn't seem to exist).
Of course file routines exist in BCB. Search for File management
routines in help.
>I am using Borland C++ Builder 4 (Build 14.4 (according to the BCB About box
> I am using Windows 95 4.90 (Build 3000: ) but I'm running Windows ME (another bug I presume!?!?)).
Windows Me is Windows 4.90 (build 3000) there is no mistake. You are
just new to all this. You need to read more.
Darko