Here is the implementation of the interface with example code showing its use.
Code: Select all
#NoEnv
Item := A_ScriptFullPath
DestinationFolder := A_ScriptDir "\Some\Path\That\Did\Not\Previously\Exist"
pBindCtx := CreateBindCtx()
Copy(Item, DestinationFolder, pBindCtx)
ObjRelease(pBindCtx)
if FileExist(DestinationFolder "\" A_ScriptName) {
MsgBox, 0x40, , SUCCESS :)
FileRemoveDir, % A_ScriptDir "\Some", 1
} else
MsgBox, 0x10, , FAILURE :(
ExitApp
Copy(Item, Folder, pBindCtx := 0) {
static IID_IShellItem := "{43826d1e-e718-42ee-bc55-a1e261c37bfe}" ; (ShObjIdl_core.h)
static IID_IFileOperation := "{947aab5f-0a5c-4c13-b4d6-4bf7836fc9f8}" ; (ShObjIdl_core.h)
static CLSID_FileOperation := "{3ad05575-8857-4850-9277-11b85bdb8e09}" ; (ShObjIdl_core.h)
local IID, pItem, pFolder, pFileOperation
IIDFromString(IID, IID_IShellItem)
CheckHRESULT(DllCall("Shell32\SHCreateItemFromParsingName", "WStr", Item, "Ptr", 0, "Ptr", &IID, "Ptr*", pItem := 0, "Int"))
CheckHRESULT(DllCall("Shell32\SHCreateItemFromParsingName", "WStr", Folder, "Ptr", pBindCtx, "Ptr", &IID, "Ptr*", pFolder := 0, "Int"))
pFileOperation := ComObjCreate(CLSID_FileOperation, IID_IFileOperation)
CheckHRESULT(DllCall(vTable(pFileOperation, 16), "Ptr", pFileOperation, "Ptr", pItem, "Ptr", pFolder, "Ptr", 0, "Ptr", 0, "Int")) ; IFileOperation::CopyItem
CheckHRESULT(DllCall(vTable(pFileOperation, 21), "Ptr", pFileOperation, "Int")) ; IFileOperation::PerformOperations
ObjRelease(pFileOperation)
ObjRelease(pFolder)
ObjRelease(pItem)
}
CreateBindCtx(FileAttributes := 0x10) { ; FILE_ATTRIBUTE_DIRECTORY = 0x10
local ptr, WIN32_FIND_DATA, pBindCtx
ptr := FileSystemBindData_Create()
VarSetCapacity(WIN32_FIND_DATA, A_IsUnicode ? 592 : 320, 0)
NumPut(FileAttributes, WIN32_FIND_DATA, 0, "UInt")
FileSystemBindData_SetFindData(ptr, &WIN32_FIND_DATA)
static STR_FILE_SYS_BIND_DATA := "File System Bind Data"
CheckHRESULT(DllCall("Ole32\CreateBindCtx", "UInt", 0, "Ptr*", pBindCtx := 0, "Int"))
CheckHRESULT(DllCall(vTable(pBindCtx, 9), "Ptr", pBindCtx, "WStr", STR_FILE_SYS_BIND_DATA, "Ptr", ptr, "Int")) ; IBindCtx::RegisterObjectParam
return pBindCtx
}
; ---------------------------------------
; IFileSystemBindData interface functions
; ---------------------------------------
FileSystemBindData_Create() {
static Bytes := 7 * A_PtrSize + (A_IsUnicode ? 592 : 320)
static Heap := DllCall("GetProcessHeap", "Ptr")
local ptr, Each, Name
ptr := DllCall("HeapAlloc", "Ptr", Heap, "UInt", 0xC, "UInt", Bytes, "Ptr") ; HEAP_GENERATE_EXCEPTIONS | HEAP_ZERO_MEMORY
NumPut(ptr + 2 * A_PtrSize, ptr + 0, 0, "Ptr") ; Store the pointer to the vTable at the top of the structure.
NumPut(1, ptr + 0, A_PtrSize, "UInt") ; Initialize the reference count.
for Each, Name in ["QueryInterface","AddRef","Release","SetFindData","GetFindData"] ; (ShObjIdl_core.h)
NumPut(RegisterCallback("FileSystemBindData_" Name), ptr + 0, (Each + 1) * A_PtrSize , "Ptr")
OnExit(Func("DllCall").Bind("HeapFree", "Ptr", Heap, "UInt", 0, "Ptr", ptr, "Int"))
return ptr
}
FileSystemBindData_QueryInterface(ptr, rIID, ppvObject) {
static IID_IUnknown, IID_IFileSystemBindData
if !VarSetCapacity(IID_IUnknown)
IIDFromString(IID_IUnknown, "{00000000-0000-0000-C000-000000000046}") ; (Unknwn.h)
if !VarSetCapacity(IID_IFileSystemBindData)
IIDFromString(IID_IFileSystemBindData, "{01E18D10-4D8B-11d2-855D-006008059367}") ; (ShObjIdl_core.h)
if IsEqualIID(rIID, &IID_IUnknown) || IsEqualIID(rIID, &IID_IFileSystemBindData) {
NumPut(ptr, ppvObject + 0, "Ptr")
FileSystemBindData_AddRef(ptr)
return 0 ; S_OK
} else {
NumPut(0, ppvObject + 0, "Ptr")
return 0x80004002 ; E_NOINTERFACE
}
}
FileSystemBindData_AddRef(ptr) {
local Count := NumGet(ptr + 0, A_PtrSize, "UInt") + 1
NumPut(Count, ptr + 0, A_PtrSize, "UInt")
return Count
}
FileSystemBindData_Release(ptr) {
local Count := NumGet(ptr + 0, A_PtrSize, "UInt") - 1
if (Count = 0)
throw
NumPut(Count, ptr + 0, A_PtrSize, "UInt")
return Count
}
FileSystemBindData_SetFindData(ptr, pWIN32_FIND_DATA) {
CopyMemory(pWIN32_FIND_DATA, ptr + 7 * A_PtrSize, A_IsUnicode ? 592 : 320)
return 0 ; S_OK
}
FileSystemBindData_GetFindData(ptr, pWIN32_FIND_DATA) {
CopyMemory(ptr + 7 * A_PtrSize, pWIN32_FIND_DATA, A_IsUnicode ? 592 : 320)
return 0 ; S_OK
}
; ---------
; Utilities
; ---------
IIDFromString(ByRef IID, String) {
VarSetCapacity(IID, 16)
CheckHRESULT(DllCall("Ole32\IIDFromString", "WStr", String, "Ptr", &IID, "Int"))
}
IsEqualIID(ptr1, ptr2) {
return DllCall("Ole32\IsEqualGUID", "Ptr", ptr1, "Ptr", ptr2, "Int")
}
CopyMemory(src, dest, size) {
return DllCall("msvcrt\memcpy", "Ptr", dest, "Ptr", src, "UInt", size, "Ptr")
}
vTable(ptr, n) {
return NumGet(NumGet(ptr + 0, 0, "Ptr"), n * A_PtrSize, "Ptr")
}
CheckHRESULT(HRESULT) {
if (HRESULT < 0)
throw Exception(FormatMessage(HRESULT), -1)
}
FormatMessage(HRESULT)
{
static Flags := 0x00000100 ; FORMAT_MESSAGE_ALLOCATE_BUFFER
| 0x00001000 ; FORMAT_MESSAGE_FROM_SYSTEM
| 0x00000200 ; FORMAT_MESSAGE_IGNORE_INSERTS
| 0x000000FF ; FORMAT_MESSAGE_MAX_WIDTH_MASK
local pBuffer, NoChars, Message
NoChars := DllCall("FormatMessage", "UInt", Flags, "Ptr", 0, "Int", HRESULT, "UInt", 0, "Ptr*", pBuffer := 0, "UInt", 0, "Ptr", 0, "UInt")
Message := StrGet(pBuffer, NoChars), DllCall("LocalFree", "Ptr", pBuffer)
return Message
}
; -----
; Other
; -----
; https://docs.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants - File Attribute Constants (WinNT.h)
class FILE_ATTRIBUTE
{
static READONLY := 0x00000001
static HIDDEN := 0x00000002
static SYSTEM := 0x00000004
static DIRECTORY := 0x00000010
static ARCHIVE := 0x00000020
static DEVICE := 0x00000040
static NORMAL := 0x00000080
static TEMPORARY := 0x00000100
static SPARSE_FILE := 0x00000200
static REPARSE_POINT := 0x00000400
static COMPRESSED := 0x00000800
static OFFLINE := 0x00001000
static NOT_CONTENT_INDEXED := 0x00002000
static ENCRYPTED := 0x00004000
static VIRTUAL := 0x00010000
}