Help with implementing IFileSystemBindData interface Topic is solved

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
iPhilip
Posts: 817
Joined: 02 Oct 2013, 12:21

Help with implementing IFileSystemBindData interface

Post by iPhilip » 17 Dec 2021, 14:27

Hi Folks,

I am trying to implement the IFileSystemBindData interface and I would appreciate your help. More specifically I am following the following instruction in the Remarks seciton of the IFileSystemBindData interface documentation page (link).
IFileSystemBindData wrote:1. Create an instance of the object that exposes the IFileSystemBindData interface.
I am familiar with classes and constructing COM calls but this one is currently beyond my understanding. Normally, I would use ComObjCreate() or ComObjQuery() to get the pointer to the interface and go from there. According to this post there is a Windows classic sample that shows how to implement the IFileSystemBindData interface but I am not familiar with C++.

Any help would be appreciated.
Windows 10 Pro (64 bit) - AutoHotkey v2.0+ (Unicode 64-bit)

iPhilip
Posts: 817
Joined: 02 Oct 2013, 12:21

Re: Help with implementing IFileSystemBindData interface

Post by iPhilip » 17 Jan 2022, 20:42

Hi Folks,

If you are reading this post, thank you. Below is code that shows how far I have gotten with this. As I mentioned above, I am stuck trying to get a pointer to the FileSystemBindData interface.

Code: Select all

Path := A_ScriptFullPath

o := new FileSystemBindData
MsgBox % o.ptr  ; Displays 0 indicating that the FileOperation object doesn't support the FileSystemBindData interface. What does?

SetFindData(FILE_ATTRIBUTE_DIRECTORY := 0x10, o)
pBindCtx := CreateBindCtx()
RegisterObjectParam(pBindCtx, o.ptr)

IIDFromString(IID_IShellItem, "{43826d1e-e718-42ee-bc55-a1e261c37bfe}")
DllCall("Shell32\SHCreateItemFromParsingName", "WStr", Path, "Ptr", pBindCtx, "Ptr", &IID_IShellItem, "Ptr*", pShellItem, "Int")

ObjRelease(pShellItem)
ObjRelease(pBindCtx)
o := ""
ExitApp

SetFindData(FileAttributes, o) {
   VarSetCapacity(WIN32_FIND_DATA, 592, 0)
   NumPut(FileAttributes, WIN32_FIND_DATA, 0, "UInt")
   o.SetFindData(WIN32_FIND_DATA)
}

CreateBindCtx() {
   DllCall("Ole32\CreateBindCtx", "UInt", 0, "Ptr*", pBindCtx, "Int")
   return pBindCtx
}

RegisterObjectParam(pBindCtx, pUnknown) {
   static STR_FILE_SYS_BIND_DATA := "File System Bind Data"
   pVtbl := NumGet(pBindCtx + 0, 0, "Ptr")
   RegisterObjectParam := NumGet(pVtbl + 0, 9 * A_PtrSize, "Ptr")
   if DllCall(RegisterObjectParam, "Ptr", pBindCtx, "WStr", STR_FILE_SYS_BIND_DATA, "Ptr", pUnknown, "Int")  ; IBindCtx::RegisterObjectParam
      throw Exception("IBindCtx::RegisterObjectParam error.", -1)
   ObjRelease(pUnknown)  ; IBindCtx::RegisterObjectParam calls AddRef on the pUnknown pointer.
}

IIDFromString(ByRef IID, String) {
   VarSetCapacity(IID, 16)
   DllCall("Ole32.dll\IIDFromString", "WStr", String, "Ptr", &IID)
}

class FileSystemBindData
{
   __New()
   {
      static IID := "{01E18D10-4D8B-11d2-855D-006008059367}"  ; IID_IFileSystemBindData (shobjidl_core.h)
      
      pFileOperation := ComObjCreate("{3ad05575-8857-4850-9277-11b85bdb8e09}"   ; CLSID_FileOperation (ShObjIdl_core.h)
                                   , "{947aab5f-0a5c-4c13-b4d6-4bf7836fc9f8}")  ; IID_IFileOperation  (ShObjIdl_core.h)
      this.ptr := ComObjQuery(pFileOperation, IID)
      ObjRelease(pFileOperation)
   }
   
   __Delete()
   {
      ObjRelease(this.ptr)
   }
   
   SetFindData(ByRef WIN32_FIND_DATA)
   {
      DllCall(this.vTable(3), "Ptr", this.ptr, "Ptr", &WIN32_FIND_DATA, "Int")
   }
   
   GetFindData(ByRef WIN32_FIND_DATA)
   {
      DllCall(this.vTable(4), "Ptr", this.ptr, "Ptr", &WIN32_FIND_DATA, "Int")
   }
   
   vTable(Index)
   {
      return NumGet(NumGet(this.ptr + 0, 0, "Ptr"), Index * A_PtrSize, "Ptr")
   }
}
Any suggestions are welcome.
Windows 10 Pro (64 bit) - AutoHotkey v2.0+ (Unicode 64-bit)

teadrinker
Posts: 4326
Joined: 29 Mar 2015, 09:41
Contact:

Re: Help with implementing IFileSystemBindData interface

Post by teadrinker » 17 Jan 2022, 21:51

iPhilip wrote: I am stuck trying to get a pointer to the FileSystemBindData interface
I think, you have to create its vtable in memory by yourself.

iPhilip
Posts: 817
Joined: 02 Oct 2013, 12:21

Re: Help with implementing IFileSystemBindData interface

Post by iPhilip » 19 Jan 2022, 17:54

teadrinker wrote:
17 Jan 2022, 21:51
I think, you have to create its vtable in memory by yourself.
Thank you for the suggestion, @teadrinker. It looks promising. I have a working draft based on your suggestion. I will post something soon. Much appreciated. :)
Windows 10 Pro (64 bit) - AutoHotkey v2.0+ (Unicode 64-bit)

iPhilip
Posts: 817
Joined: 02 Oct 2013, 12:21

Re: Help with implementing IFileSystemBindData interface  Topic is solved

Post by iPhilip » 23 Jan 2022, 17:36

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  
}
Windows 10 Pro (64 bit) - AutoHotkey v2.0+ (Unicode 64-bit)

Post Reply

Return to “Ask for Help (v1)”