Private Declare Function SHFileOperation Lib "shell32.dll" _
Alias "SHFileOperationA" (lpFileOp As SHFILEOPSTRUCT) As Long
Private Type SHFILEOPSTRUCT
hWnd As Long ' Window owner of any dialogs
wFunc As Long ' Copy, move, rename, or delete code
pFrom As String ' Source file
pTo As String ' Destination file or directory
fFlags As Integer ' Options to control the operations
“Look at that file creation time. When I see one that’s the same as the write time, 95 times out of 100 it’s one of you old programmers replacing a file without copying the time and attributes.”
“What’s wrong with that?”
fAnyOperationsAbortedLo As Integer ' Indicates partial failure
fAnyOperationsAbortedHi As Integer
hNameMappingsLo As Long ' Array indicating each success
hNameMappingsHi As Long
lpszProgressTitleLo As Long ' Title for progress dialog
lpszProgressTitleHi As Long
End Type
Sub ReplaceFile(sOld As String, sTmp As String)
Dim fnd As WIN32_FIND_DATA, hFind As Long, hOld As Long, f As Boolean
' Get file time and attributes of old file
hFind = FindFirstFile(sOld, fnd)
If hFind = hInvalid Then ApiRaise Err.LastDllError
' Replace by deleting old and renaming new to old
Kill sOld
Name sTmp As sOld
' Assign old attributes and time to new file
hOld = lopen(sOld, OF_WRITE Or OF_SHARE_DENY_WRITE)
If hOld = hInvalid Then ApiRaise Err.LastDllError
f = SetFileTime(hOld, fnd.ftCreationTime, _
fnd.ftLastAccessTime, fnd.ftLastWriteTime)
If f Then ApiRaise Err.LastDllError
lclose hOld
f = SetFileAttributes(sOld, fnd.dwFileAttributes)
If f Then ApiRaise Err.LastDllError
End Sub
“Now why didn’t I think of that? Hey, thanks.”
“No problem, buddy. But if I catch you overwriting file times again, I’m going to throw the disk at you.”
Why the packing? Normally, the only reason to pack structures is so that you can store more of them on disk, but nobody’s going to be using a big array of these structures. If the designers had some good reason (that I can’t imagine) to pack the structure and make the fFlags field (with its bad Hungarian name) an Integer rather than a Long, why not put it at the end so it doesn’t throw all the others out of alignment? This is typical of the SH functions, all of which seem half-baked, unfinished, and inconsistent with the rest of the Windows API. For example, why is the structure given the redundant name SHFILEOPSTRUCT instead of SHFILEOP or just FILEOP? If you like hopeless arguments about API design, the shell functions leave plenty of room for debate.
Function CopyAnyFile(sSrc As String, sDst As String, _
Optional Options As Long = 0, _
Optional Owner As Long = hNull) As Boolean
If MUtility.HasShell Then
Dim fo As SHFILEOPSTRUCT, f As Long
fo.wFunc = FO_COPY
Debug.Print TypeName(fo.wFunc)
fo.pFrom = sSrc
fo.pTo = sDst
fo.fFlags = Options
fo.hWnd = Owner
' Mask out invalid flags
fo.fFlags = fo.fFlags And FOF_COPYFLAGS
f = SHFileOperation(fo)
CopyAnyFile = (f = 0)
Else
' For Windows NT 3.51
On Error Resume Next
' FileCopy expects full name of destination file
FileCopy sSrc, sDst
If Err Then
Err = 0
' CopyAnyFile can handle destination directory
sDst = MUtility.NormalizePath(sDst) & _
MUtility.GetFileBaseExt(sSrc)
FileCopy sSrc, sDst
End If
' Enhance further to emulate SHFileOperation options
' such as validation and wild cards
CopyAnyFile = (Err = 0)
End If
End Function
Flag | Purpose |
FOF_NOCONFIRMATION | Overwrite or delete files without confirmation |
FOF_ALLOWUNDO | Put deleted files (except those from floppy disks) in Recycle Bin |
FOF_SILENT | Prevent display of a progress dialog box for slow operations |
FOF_SIMPLEPROGRESS | Simplify the progress dialog box by not showing filenames |
FOF_RENAMEONCOLLISION | Create new numbered files (Copy #1 of...) if copied or moved files conflict with existing files |
FOF_MULTIDESTFILES | Copy or move a wildcard source to multiple |
target files rather than to a single destination directory | |
FOF_FILESONLY | Interpret a wildcard source to mean files only, not directories |
FOF_NOCONFIRMMKDIR | Create any needed destination directories without confirmation |