Im using this external version of the SHFileOperation (written by just me) in order to keep my GUI operational during file moves/copy of very large files, which has been working nicely up to now. Lately I have come across some issues with it (or rather more correct a lacking of feature). I do like to sort files using created date attribute and this attribute vanishes in cross-volume copy operations. Im abled to workaround that with some awkward coding, but instead of using messy workarounds I was wondering if anyone might be interested in converting it to the more modern IFileOperation, which supports the FOFX_MOVEACLSACROSSVOLUMES (0x02000000) flag which allows "copy the security attributes of the source item to the destination item when performing a cross-volume move operation. Without this flag, the destination item receives the security attributes of its new folder"? This c++ stuff is so over my head.
https://msdn.microsoft.com/en-us/librar ... s.85).aspx
Spoiler
Code: Select all
#NoEnv
#NoTrayIcon
; Example:
; Save this file to ParamToObj_Sample.ahk, then run with parameters:
; ParamToObj_Sample.ahk /x 20 -novalue1 -y 14 /novalue2 --width=710 --novalue3 --height 90 WillBeIgnored /title:"hi !"
; The result will be:
; param.height = 90
; param.novalue1 =
; param.novalue2 =
; param.novalue3 =
; param.title = hi !
; param.width = 710
; param.x = 20
; param.y = 14
param := ParamToObj()
;For k, v in param
; param_list .= "param." k " = " v "`n"
;MsgBox, % param_list
FileArray := []
DropEffect := param.DropEffect
TargetPath := param.FolderPath
DropFile := param.DroppedFiles
If (DropFile = "") {
FileRead, DroppedFiles, % A_ScriptDir . "\DroppedList.txt"
Loop, Parse, DroppedFiles, `n, `r
FileArray.Push(A_LoopField)
SHFileOperation(FileArray, TargetPath, DropEffect ^ 3, 0x0400)
FileDelete, % A_ScriptDir . "\DroppedList.txt"
}
Else
SHFileOperation(DropFile, TargetPath, DropEffect ^ 3, 0x0400)
; ==================================================================================================================================
ParamToObj() {
global 0
obj := {}
Loop, %0%
{
param := %A_Index%
If RegExMatch(param, "^(/|-)+(\w+)(\W(.+))?$", match) {
obj[match2] := match4
key := match2
}
Else If (key != "")
obj[key] := param, key := ""
}
Return obj
}
; ==================================================================================================================================
; Copies, moves, renames, or deletes a file system object using the SHFileOperation() function.
; SHFileOperation -> msdn.microsoft.com/en-us/library/bb762164(v=vs.85).aspx
; Parameters:
; Sources - A string containing a list of fully qualified source pathes separated by `n if needed
; or an array of fully qualified source pathes.
; Targets - A string containing a list of fully qualified target pathes separated by `n if needed
; or an array of fully qualified target pathes.
; Operation - A value that indicates which operation to perform. One of the following values:
; FO_MOVE = 1
; FO_COPY = 2
; FO_DELETE = 3
; FO_RENAME = 4
; Flags - Any reasonable combination of the FOF_ flags -> msdn.microsoft.com/en-us/library/bb759795(v=vs.85).aspx
; FOF_MULTIDESTFILES = 0x0001
; FOF_CONFIRMMOUSE = 0x0002
; FOF_SILENT = 0x0004
; FOF_RENAMEONCOLLISION = 0x0008
; FOF_NOCONFIRMATION = 0x0010
; FOF_WANTMAPPINGHANDLE = 0x0020
; FOF_ALLOWUNDO = 0x0040
; FOF_FILESONLY = 0x0080
; FOF_SIMPLEPROGRESS = 0x0100
; FOF_NOCONFIRMMKDIR = 0x0200
; FOF_NOERRORUI = 0x0400
; FOF_NOCOPYSECURITYATTRIBS = 0x0800
; FOF_NORECURSION = 0x1000
; FOF_NO_CONNECTED_ELEMENTS = 0x2000
; FOF_WANTNUKEWARNING = 0x4000
; FOF_NO_UI = 0x0614
; Default: FOF_NOCONFIRMMKDIR (0x0200)
; HWND - A handle to the window which will own the dialog.
; Default: 0
; MappedFiles - Optional ByRef Variable to return an array of files which were renamed because of name collisions
; if the FOF_RENAMEONCOLLISION and FOF_WANTMAPPINGHANDLE flags were specified. Each array element
; provides the keys OldPath and NewPath.
; Return values:
; Returns 1 (True) if successful, otherwise 0 (False).
; ErrorLevel is set to non-zero (True) if if any file operations were aborted before they were completed.
; Remarks:
; You cannot rename multiple files with a single function call. Use FO_MOVE instead.
; ==================================================================================================================================
SHFileOperation(Sources, Targets, Operation, Flags := 0x0200, HWND := 0, ByRef MappedFiles := "") {
; Special file operation flags
Static FOF_MULTIDESTFILES := 0x0001
, FOF_RENAMEONCOLLISION := 0x0008
, FOF_WANTMAPPINGHANDLE := 0x0020
, FOF_SIMPLEPROGRESS := 0x0100
Static TCS := A_IsUnicode ? 2 : 1 ; size of a TCHAR
If Operation Not Between 1 And 4
Return False
; Count source pathes and total string length
TotalLength := 0
Files := []
If IsObject(Sources) {
For Each, FilePath In Sources {
If (Length := StrLen(FilePath))
Files.Push({Path: FilePath, Len: Length + 1})
TotalLength += Length
}
}
Else {
Loop, Parse, Sources, `n
{
If (Length := StrLen(A_LoopField))
Files.Push({Path: A_LoopField, Len: Length + 1})
TotalLength += Length
}
}
FileCount := Files.Length()
If !(FileCount && TotalLength)
Return False
; Store the source pathes in Sources (the string must be double-null terminated)
VarSetCapacity(Sources, (TotalLength + FileCount + 1) * TCS, 0)
Offset := 0
For Each, File In Files
Offset += StrPut(File.Path, &Sources + Offset, File.Len) * TCS
; Count target pathes and total string length
TotalLength := 0
Files := []
If IsObject(Targets) {
For Each, FilePath In Targets {
If (Length := StrLen(FilePath))
Files.Push({Path: FilePath, Len: Length + 1})
TotalLength += Length
}
}
Else {
Loop, Parse, Targets, `n
{
If (Length := StrLen(A_LoopField))
Files.Push({Path: A_LoopField, Len: Length + 1})
TotalLength += Length
}
}
FileCount := Files.Length()
; Operations other than FO_DELETE require a target path
If !(FileCount && TotalLength) && (Operation <> 3)
Return False
; Store the target pathes in Targets (the string must be double-null terminated)
VarSetCapacity(Targets, (TotalLength + FileCount + 1) * TCS, 0)
Offset := 0
For Each, File In Files
Offset += StrPut(File.Path, &Targets + Offset, File.Len) * TCS
; Set FOF_MULTIDESTFILES if necessary
If (FileCount > 1)
Flags |= FOF_MULTIDESTFILES
; Check for FOF_SIMPLEPROGRESS to set the the title of the progress dialog box.
If (Flags & FOF_SIMPLEPROGRESS)
Title := A_ScriptName
Else
Title := ""
; Create and fill the SHFILEOPSTRUCT
SHFOSSize := A_PtrSize * (A_PtrSize = 8 ? 7 : 8)
VarSetCapacity(SHFOS, SHFOSSize, 0) ; SHFILEOPSTRUCT
NumPut(HWND, SHFOS, 0, "UPtr") ; hwnd
NumPut(Operation, SHFOS, A_PtrSize, "UInt") ; wFunc
NumPut(&Sources, SHFOS, A_PtrSize * 2, "UPtr") ; pFrom
NumPut(&Targets, SHFOS, A_PtrSize * 3, "UPtr") ; pTo
NumPut(Flags, SHFOS, A_PtrSize * 4, "UInt") ; fFlags
NumPut(&Title, SHFOS, 8 + (A_PtrSize * 5), "UPtr") ; lpszProgressTitle
If (A_IsUnicode)
RetVal := !DllCall("Shell32.dll\SHFileOperationW", "Ptr", &SHFOS, "Int")
Else
RetVal := !DllCall("Shell32.dll\SHFileOperationA", "Ptr", &SHFOS, "Int")
; Check for name collisions
If IsByRef(MappedFiles) && (Flags & FOF_RENAMEONCOLLISION) && (Flags & FOF_WANTMAPPINGHANDLE) {
If (MapPointer := NumGet(SHFOS, 8 + (A_PtrSize * 4), "UPtr")) {
If (MapCount := NumGet(MapPointer + 0, "UInt")) {
MappedFiles := []
ArrayPointer := NumGet(MapPointer + A_PtrSize, "UPtr")
Loop, %MapCount% {
OldLen := NumGet(ArrayPointer + 0, A_PtrSize * 2, "Int")
OldPath := StrGet(NumGet(ArrayPointer + 0, "UPtr"), OldLen, "UTF-16")
NewLen := NumGet(ArrayPointer + 4, A_PtrSize * 2, "Int")
NewPath := StrGet(NumGet(ArrayPointer + A_PtrSize, "UPtr"), NewLen, "UTF-16")
MappedFiles.Push({OldPath: OldPath, NewPath: NewPath})
ArrayPointer += 8 + (A_PtrSize * 2)
}
}
DllCall("Shell32.dll\SHFreeNameMappings", "Ptr", MapPointer)
}
}
ErrorLevel := NumGet(SHFOS, 4 + (A_PtrSize * 4), "UInt")
Return RetVal
}