Making A Copy Of A Folder

Hi,

I am having difficulties trying to figure out how to copy an entire folder to make a backup copy of the folder. The source folder is called Runs. The Backup folder I want to be called Backup. The Runs folder has subfolders and a bunch of files. The purpose is to create a easy data backup of all data for the customer.

In the Language Reference Library I found the following code"

Sub CopyFileorFolder (source as FolderItem, destination as FolderItem) Dim i as Integer Dim newFolder as FolderItem If source.directory then //it's a folder newFolder=destination.child(source.name) newFolder.createAsFolder For i=1 to source.count //go through each item If source.item(i).directory then //it's a folder CopyFileOrFolder source.item(i), newFolder //recursively call this //routine passing it the folder else source.item(i).CopyFileTo newFolder //it's a file so copy it end if next else //it's not a folder source.CopyFileTo destination end if

I created two properties - source as FolderItem and Destination as FolderItem.

But I can’t figure out how to wire the above code so that all the subfolders and files are copied from the folder Runs to the folder Backup.

Can anyone tell me where to put the Runs and Back folders into the above code?

Any help would be greatly appreciated.

Don’t be fooled by the name “CopyFileTo”. It does copy entire folders as well.

This short piece of code will copy an entire folder to the Documents folder :

dim source as folderitem = SelectFolder if source <> nil and source.Exists then dim destination as FolderItem = SpecialFolder.Documents source.CopyFileTo(destination) end if

Thank you so much Michel. That worked great. I really appreciate you taking the time to help me. You made my day.

I have one more question Michel. I used a modified version of your code (see below.) It works great, but once the folder is created it will not overwrite the old folder with an updated copy. So if you update the source folders, and then try to copy it to the destination folders it won’t overwrite the existing folder.

[code] dim source as folderitem = getfolderitem("").child(“Runs”)
if source <> nil and source.Exists then
//dim destination as FolderItem = SpecialFolder.Documents

dim destination as FolderItem 


Destination=(getfolderitem("").child("Backup"))


source.CopyFileTo(destination)

end if[/code]

Is there any easy way just to delete the destination folder in code without having to manually drag it to the trash?

Jim

This is from the documentation on folderitem.delete. MBS and I’m sure others have alternate methods.

[code]Function DeleteEntireFolder(theFolder as FolderItem, continueIfErrors as Boolean = false) As Integer
// Returns an error code if it fails, or zero if the folder was deleted successfully

dim returnCode, lastErr, itemCount as integer
dim files(), dirs() as FolderItem

if theFolder = nil or not theFolder.Exists() then
return 0
end if

// Collect the folder‘s contents first.
// This is faster than collecting them in reverse order and deleting them right away!
itemCount = theFolder.Count
for i as integer = 1 to itemCount
dim f as FolderItem
f = theFolder.TrueItem( i )
if f <> nil then
if f.Directory then
dirs.Append f
else
files.Append f
end if
end if
next

// Now delete the files
for each f as FolderItem in files
f.Delete
lastErr = f.LastErrorCode // Check if an error occurred
if lastErr <> 0 then
if continueIfErrors then
if returnCode = 0 then returnCode = lastErr
else
// Return the error code if any. This will cancel the deletion.
return lastErr
end if
end if
next

redim files(-1) // free the memory used by the files array before we enter recursion

// Now delete the directories
for each f as FolderItem in dirs
lastErr = DeleteEntireFolder( f, continueIfErrors )
if lastErr <> 0 then
if continueIfErrors then
if returnCode = 0 then returnCode = lastErr
else
// Return the error code if any. This will cancel the deletion.
return lastErr
end if
end if
next

if returnCode = 0 then
// We‘re done without error, so the folder should be empty and we can delete it.
theFolder.Delete
returnCode = theFolder.LastErrorCode
end if

return returnCode
End Function[/code]

I can’t remember if you use Mac or PC. There are ways though Shell.

But there must be a way through pure Xojo code. Let me see…

Thank you Michel and Scott.

I write for both Windows and Mac and develop on a Mac.

I saw the code Scott posted above, but I have no idea how to implement it. I don’t know anything about Functions so that is basically one problem I have. Where do you paste that code and how do you tell the function that “Runs” is the folder that needs to be deleted? I was really confused as to how to do that and was hoping for a quick and easy alternative way.

[quote=200619:@James Redway]Thank you Michel and Scott.

I write for both Windows and Mac and develop on a Mac.

I saw the code Scott posted above, but I have no idea how to implement it. I don’t know anything about Functions so that is basically one problem I have. Where do you paste that code and how do you tell the function that “Runs” is the folder that needs to be deleted? I was really confused as to how to do that and was hoping for a quick and easy alternative way.[/quote]

A function or a sub are methods.

Select a window, right click/add/Method. In the inspector, copy the name of the function, and the parameters below. Then copy all the code without the first and last line in the left side.

In the meantime, here here a pure Xojo way of deleting a folder :

dim f as folderitem = SpecialFolder.Documents.Child("myFolder") dim destination as FolderItem = SpecialFolder.Temporary f.MoveFileTo(destination)

By moving it to the Temporary folder, it will be deleted at the next system shutdown.

Ouch. There is an issue with CopyFileTo in Windows 10. It simply does not work. No error, but nothing is copied.

40133 - FolderItem.CopyFileTo() broken in Windows 10
Status: Needs Review Rank: Not Ranked Product: Xojo Category: N/A

Michel Bujardet Today at 7:26 PM

OS: Windows 10

Xojo: Xojo 2015r2.2

Steps: See attached project. All it does is to try to copy the Links folder from inside the user home to Documents.

It simply does nothing.

MoveFileTo works just fine.

Expected Result:
Folder is copied to destination

Actual Result:
Folder is not copied

Workarounds:
Shell CP ?

<https://xojo.com/issue/40133>

Windows 10 will be released on July 25 with free upgrade for all users of Windows 8 and Windows 7.

Workaround :

[code] dim f as folderitem = SpecialFolder.UserHome.Child(“Links”)
if f = nil or not f.exists then msgbox “oops”
dim destination as FolderItem = SpecialFolder.Documents
dim s as new shell
s.execute “XCopy /E /I “+f.ShellPath+” “+destination.ShellPath+”” +f.name

Msgbox “Links should have been copied to documents”[/code]

Hi Michel,

The move to Temporary code works once. But once the file is moved and you want to move it again, it won’t execute again until the system is rebooted. So if a user is working and makes a backup, then works some more and tries to backup again, it won’t happen until the system reboots again.

Also, I take it that the above Workaround will only work on Windows… right… since it is a shell?

Based on the DeleteEntireFolder method (straight out of the LR) above:

  1. Add a method
  2. Give it the name DeleteEntireFolder
  3. Add these 2 parameters: theFolder as FolderItem, continueIfErrors as Boolean = false
  4. Set the return type to integer
  5. Paste all of the text of the function above, except for the first and last lines, into the body of your function.

Now you’re ready to use it. It returns an integer which is either an error code or zero if there’s no error.

Assuming you have a folder reference named folder_to_delete:

dim i as integer i = DeleteEntireFolder(folder_to_delete)

That’s it. Really.

If you want to ignore any errors:

call DeleteEntireFolder(folder_to_delete)

If you don’t want to continue the process if it encounters an error:

dim i as integer i = DeleteEntireFolder(folder_to_delete, true)

Moving something to the temporary folder isn’t deleting it. It’s sweeping something under the rug and hoping someone else will come and clean it up later.

Sorry about that. Here is a snippet that works every time.

Sub Action() dim f as folderitem = SpecialFolder.Desktop.child("Links") dim destination as FolderItem = SpecialFolder.Temporary.Child(EncodeBase64(format(Microseconds, "###"))) destination.CreateAsFolder f.MoveFileTo(destination) system.DebugLog destination.ShellPath End Sub

As far as I know, the error I reported happens only with Windows 10. So with everything else, use the standard CopyFileTo.

You could do something like this :

dim s as new shell s.execute("ver") if instr(s.Result,"Version 10") > 0 then MsgBox "Windows 10" // That is where you do the workaround else // Here, you use the CopyFileTo method end if

Come on. No need for dishing. The “someone else” is the system, which flushes all files upon shutdown. It is quite comparable to the trashbin.

Besides, James may not be ready to buy a plugin just yet and may appreciate a method in pure Xojo.

Moving something, to the trash or otherwise, is not deletion. Please take a look at the method, again straight out of Xojo’s Language Reference, and show me where a plugin is needed.

OK. The MBS mention confused my speed reading.

Now, I am not trying to fight and would appreciate less aggressivity. Thank you.

Hi Michel,

Your a genius. That worked perfectly. That is just what I was looking for. Thanks so much.

Thank you also Scott for helping out. I appreciate you taking the time. I understand the function stuff a lot more than I did earlier.

I think I will go with Michel’s code since it is so much less complicated. As long as the folder is removed from the current directory and it can be rewritten as many times as the user wants, I’m not really concern that it goes to a temporary file since the system will remove such files when the machine is rebooted. Seems to me its a concise self-cleaning solution.

Thanks again. I really appreciate all the help I get here. Couldn’t do it without you guys.

James, I ran some additional tests. The CopyFileTo method simply does not work for folders in Windows, whatever the version. Use the shell workaround .

Something like

If TargetWin32 then // Use the shell method elseIf TargetMacOS then // Use the CopyFileTo endif

Hi Michel,

Thank you for the update. I can’t get the Windows version to work. It throws a Nil Exception error.

This is the code I have:

[code] If TargetWin32 then

dim f as folderitem = getfolderitem("").child("Runs")
if f = nil or not f.exists then msgbox "oops"
dim destination as FolderItem =(getfolderitem("").child("Backups").child("Backup"))
dim s as new shell
s.execute "XCopy /E /I "+f.ShellPath+" "+destination.ShellPath+"\" +f.name

Msgbox "Links should have been copied to documents"

elseIf TargetMacOS then

dim source as folderitem = getfolderitem("").child("Runs")
if source <> nil and source.Exists then
  //dim destination as FolderItem = SpecialFolder.Documents
  dim destination as FolderItem =(getfolderitem("").child("Backups").child("Backup"))
  source.CopyFileTo(destination)
  destination.Name=M
end if

End If[/code]

On the Mac it works perfectly. The Runs, and Backups folders are in the same folder as the App, just as it is on the Mac. I get an “oops” message, and it throws the error on the s.execute line.

Any idea what I’m doing wrong?

[quote=200986:@James Redway] I get an “oops” message, and it throws the error on the s.execute line.

Any idea what I’m doing wrong?[/quote]

This means the “Runs” folder is not found.

I think it is due to the way the debug is made in Windows. In Mac, the debug app is in the same folder as the project. In Windows, it has its own folder.

This should fix it :

dim f as folderitem If DebugBuild then getfolderitem("").Parent.child("Runs") else getfolderitem("").child("Runs") end if

Hi Michel,

That code worked to find the Runs folder, but I still get an error on the actual code that copies to folder. The Shell code throws an error.

I uploaded a sample project, if you want to try it yourself. The link is:

link text

The issue you encountered is the same for all folders. I have modified your code to set the value of Destination, and added a DestinationBackup one, all set in the if TargetBuild.

Whenever you want to refer to “” you got to make the difference between debug and build in Windows.

[code] dim destination as FolderItem
dim destinationBackup as FolderItem

If TargetWin32 then
If DebugBuild Then
f = getfolderitem("").Parent.child(“Runs”)
destination =(getfolderitem("").parent.child(“Backups”).child(“Backup”))
destinationbackup =(getfolderitem("").parent.child(“Backups”).child(“Backup”))
else

  f = getfolderitem("").child("Runs")
  destination =(getfolderitem("").child("Backups").child("Backup"))
  destinationbackup =(getfolderitem("").child("Backups").child("Backup"))
End If
[/code]

Modified project