Jump to content

Need help creating new files and folders using IFileOperation::NewItem


Go to solution Solved by Nine,

Recommended Posts

Posted (edited)

There is some initial IFileOperation structure and code from @Danyfirex in the CopyFiles Default Windows ProgressBar No Ovewrite (IFileOperation) thread that has a function which handles the IFileOperation::CopyItems method. In the Files Au3 project, I have extended that code to handle the IFileOperation::MoveItems and IFileOperation::DeleteItems methods. Those are all working great.

However, I am stuck on implementing the IFileOperation::NewItem method. I simply don't understand it all and if someone can help me with this, I would really appreciate it. IFileOperation::NewItem would allow creating new files based on filename and also file templates. It also allows the creation of new folders.

If anyone understands it better, could you please create a function that allows creating a new file and a function that allows creating a new folders?

Once I understand the structure and terminology better, I should be able to extend it further.

From IFileOperation::NewItem:

HRESULT NewItem(
  [in] IShellItem                 *psiDestinationFolder,
  [in] DWORD                      dwFileAttributes,
  [in] LPCWSTR                    pszName,
  [in] LPCWSTR                    pszTemplateName,
  [in] IFileOperationProgressSink *pfopsItem
);

It goes over all of the parameters there as usual. I understand a few of the parameters but not all of them.

Thank you so much for your time. :)

EDIT: I forgot to add the example code:

; by Danyfirex

#include <WinAPIShellEx.au3>
;~ Global Const $FOF_ALLOWUNDO = 0x40
;~ Global Const $FOF_CONFIRMMOUSE = 0x2
;~ Global Const $FOF_FILESONLY = 0x80
;~ Global Const $FOF_MULTIDESTFILES = 0x1
;~ Global Const $FOF_NO_CONNECTED_ELEMENTS = 0x2000
;~ Global Const $FOF_NOCONFIRMATION = 0x10
;~ Global Const $FOF_NOCONFIRMMKDIR = 0x200
;~ Global Const $FOF_NOCOPYSECURITYATTRIBS = 0x800
;~ Global Const $FOF_NOERRORUI = 0x400
;~ Global Const $FOF_NORECURSION = 0x1000
;~ Global Const $FOF_RENAMEONCOLLISION = 0x8
;~ Global Const $FOF_SILENT = 0x4
;~ Global Const $FOF_SIMPLEPROGRESS = 0x100
;~ Global Const $FOF_WANTMAPPINGHANDLE = 0x20
;~ Global Const $FOF_WANTNUKEWARNING = 0x4000
Global Const $FOFX_ADDUNDORECORD = 0x20000000
Global Const $FOFX_NOSKIPJUNCTIONS = 0x00010000
Global Const $FOFX_PREFERHARDLINK = 0x00020000
Global Const $FOFX_SHOWELEVATIONPROMPT = 0x00040000
Global Const $FOFX_EARLYFAILURE = 0x00100000
Global Const $FOFX_PRESERVEFILEEXTENSIONS = 0x00200000
Global Const $FOFX_KEEPNEWERFILE = 0x00400000
Global Const $FOFX_NOCOPYHOOKS = 0x00800000
Global Const $FOFX_NOMINIMIZEBOX = 0x01000000
Global Const $FOFX_MOVEACLSACROSSVOLUMES = 0x02000000
Global Const $FOFX_DONTDISPLAYSOURCEPATH = 0x04000000
Global Const $OFX_DONTDISPLAYDESTPATH = 0x08000000
Global Const $FOFX_RECYCLEONDELETE = 0x00080000
Global Const $FOFX_REQUIREELEVATION = 0x10000000
Global Const $FOFX_COPYASDOWNLOAD = 0x40000000
Global Const $FOFX_DONTDISPLAYLOCATIONS = 0x80000000


Global Const $IID_IShellItem = "{43826d1e-e718-42ee-bc55-a1e261c37bfe}"
Global Const $dtag_IShellItem = _
        "BindToHandler hresult(ptr;clsid;clsid;ptr*);" & _
        "GetParent hresult(ptr*);" & _
        "GetDisplayName hresult(int;ptr*);" & _
        "GetAttributes hresult(int;int*);" & _
        "Compare hresult(ptr;int;int*);"

Global Const $IID_IShellItemArray = "{b63ea76d-1f85-456f-a19c-48159efa858b}"
Global Const $dtagIShellItemArray = "BindToHandler hresult();GetPropertyStore hresult();" & _
        "GetPropertyDescriptionList hresult();GetAttributes hresult();GetCount hresult(dword*);" & _
        "GetItemAt hresult();EnumItems hresult();"

Global Const $BHID_EnumItems = "{94F60519-2850-4924-AA5A-D15E84868039}"
Global Const $IID_IEnumShellItems = "{70629033-e363-4a28-a567-0db78006e6d7}"
Global Const $dtagIEnumShellItems = "Next hresult(ulong;ptr*;ulong*);Skip hresult();Reset hresult();Clone hresult();"


Global Const $CLSID_IFileOperation = "{3AD05575-8857-4850-9277-11B85BDB8E09}"
Global Const $IID_IFileOperation = "{947AAB5F-0A5C-4C13-B4D6-4BF7836FC9F8}"
Global Const $dtagIFileOperation = "Advise hresult(ptr;dword*);" & _
        "Unadvise hresult(dword);" & _
        "SetOperationFlags hresult(dword);" & _
        "SetProgressMessage hresult(wstr);" & _
        "SetProgressDialog hresult(ptr);" & _
        "SetProperties hresult(ptr);" & _
        "SetOwnerWindow hresult(hwnd);" & _
        "ApplyPropertiesToItem hresult(ptr);" & _
        "ApplyPropertiesToItems hresult(ptr);" & _
        "RenameItem hresult(ptr;wstr;ptr);" & _
        "RenameItems hresult(ptr;wstr);" & _
        "MoveItem hresult(ptr;ptr;wstr;ptr);" & _
        "MoveItems hresult(ptr;ptr);" & _
        "CopyItem hresult(ptr;ptr;wstr;ptr);" & _
        "CopyItems hresult(ptr;ptr);" & _
        "DeleteItem hresult(ptr;ptr);" & _
        "DeleteItems hresult(ptr);" & _
        "NewItem hresult(ptr;dword;wstr;wstr;ptr);" & _
        "PerformOperations hresult();" & _
        "GetAnyOperationsAborted hresult(ptr*);"


;_Test()

Func _Test()
    Local $sPathFrom = @ScriptDir & "\PathFrom\"
    Local $sPathTo = @ScriptDir & "\PathTo\"

    DirRemove($sPathFrom, 1)
    DirRemove($sPathTo, 1)

    DirCreate($sPathFrom)
    For $i = 1 To 5000
        FileWrite($sPathFrom & $i & ".txt", "Hello World - " & $i)
    Next

    _WinAPI_ShellFileOperation($sPathFrom & "*.*", $sPathTo, $FO_COPY, BitOR($FOF_NOERRORUI, $FOF_NOCONFIRMATION))
    ;update file From and To
    FileWrite($sPathFrom & 1 & ".txt", " Only this should be update in 'To' Folder")
    FileWrite($sPathTo & 2 & ".txt", " This should not be overwritten but it does :(")
    MsgBox(0, "ShellFileOperation", "Check these files: " & @CRLF & $sPathFrom & 1 & ".txt" & @CRLF & @CRLF & $sPathTo & 2 & ".txt")
    _WinAPI_ShellFileOperation($sPathFrom & "*.*", $sPathTo, $FO_COPY, BitOR($FOF_NOERRORUI, $FOF_NOCONFIRMATION))
    MsgBox(0, "ShellFileOperation", "Check these files: " & @CRLF & $sPathFrom & 1 & ".txt" & @CRLF & @CRLF & $sPathTo & 2 & ".txt")



    ;update file From and To
    FileWrite($sPathFrom & 1 & ".txt", " - I was updated again :-S")
    FileWrite($sPathTo & 2 & ".txt", " This will not be overwritten :)")
    MsgBox(0, "IFileOperation", "Check these files: " & @CRLF & $sPathFrom & 1 & ".txt" & @CRLF & @CRLF & $sPathTo & 2 & ".txt")
    _IFileOperationCopyFiles($sPathFrom, $sPathTo)
    MsgBox(0, "IFileOperation", "Check these files: " & @CRLF & $sPathFrom & 1 & ".txt" & @CRLF & @CRLF & $sPathTo & 2 & ".txt")


    DirRemove($sPathFrom, 1)
    DirRemove($sPathTo, 1)
EndFunc   ;==>_Test



Func _IFileOperationCopyFiles($sPathFrom, $sPathTo, $iFlags = BitOR($FOF_NOERRORUI, $FOFX_KEEPNEWERFILE, $FOFX_NOCOPYHOOKS, $FOF_NOCONFIRMATION))
    If Not FileExists($sPathFrom) Then
        Return SetError(1, 0, False)
    EndIf

    If Not FileExists($sPathTo) Then
        DirCreate($sPathTo)
    EndIf


    Local $tIIDIShellItem = CLSIDFromString($IID_IShellItem)
    Local $tIIDIShellItemArray = CLSIDFromString($IID_IShellItemArray)


    Local $oIFileOperation = ObjCreateInterface($CLSID_IFileOperation, $IID_IFileOperation, $dtagIFileOperation)
    If Not IsObj($oIFileOperation) Then Return SetError(2, 0, False)


    Local $pIShellItemFrom = 0
    Local $pIShellItemTo = 0


    _SHCreateItemFromParsingName($sPathFrom, 0, DllStructGetPtr($tIIDIShellItem), $pIShellItemFrom)
    _SHCreateItemFromParsingName($sPathTo, 0, DllStructGetPtr($tIIDIShellItem), $pIShellItemTo)


    If Not $pIShellItemFrom Or Not $pIShellItemTo Then Return SetError(3, 0, False)

    Local $oIShellItem = ObjCreateInterface($pIShellItemFrom, $IID_IShellItem, $dtag_IShellItem)
    Local $pEnum = 0
    $oIShellItem.BindToHandler(0, $BHID_EnumItems, $IID_IEnumShellItems, $pEnum)
    Local $oIEnumShellItems = ObjCreateInterface($pEnum, $IID_IEnumShellItems, $dtagIEnumShellItems)

    If Not $pEnum Then Return SetError(4, 0, False)

    $oIFileOperation.SetOperationFlags($iFlags)
    Local $pItem = 0
    Local $iFeched = 0
    While $oIEnumShellItems.Next(1, $pItem, $iFeched) = 0
        $oIFileOperation.CopyItems($pItem, $pIShellItemTo)
    WEnd

    Return $oIFileOperation.PerformOperations() = 0

EndFunc   ;==>_IFileOperationCopyFiles


Func _SHCreateItemFromParsingName($szPath, $pbc, $riid, ByRef $pv)
    Local $aRes = DllCall("shell32.dll", "long", "SHCreateItemFromParsingName", "wstr", $szPath, "ptr", $pbc, "ptr", $riid, "ptr*", 0)
    If @error Then Return SetError(1, 0, @error)
    $pv = $aRes[4]
    Return $aRes[0]
EndFunc   ;==>_SHCreateItemFromParsingName


Func CLSIDFromString($sString)
    Local $tCLSID = DllStructCreate("dword;word;word;byte[8]")
    Local $aRet = DllCall("Ole32.dll", "long", "CLSIDFromString", "wstr", $sString, "ptr", DllStructGetPtr($tCLSID))
    If @error Then Return SetError(1, 0, @error)
    If $aRet[0] <> 0 Then Return SetError(2, $aRet[0], 0)
    Return $tCLSID
EndFunc   ;==>CLSIDFromString

 

Edited by WildByDesign
  • Solution
Posted (edited)

Here :

; From Nine
#include <WinAPI.au3>

Opt("MustDeclareVars", True)

Global Const $IID_IShellItem = "{43826D1E-E718-42EE-BC55-A1E261C37BFE}"
Global Const $tIIDIShellItem = _WinAPI_GUIDFromString($IID_IShellItem)

Global Const $CLSID_IFileOperation = "{3AD05575-8857-4850-9277-11B85BDB8E09}"
Global Const $IID_IFileOperation = "{947AAB5F-0A5C-4C13-B4D6-4BF7836FC9F8}"
Global Const $tagIFileOperation = _
    "Advise hresult(ptr;dword*);" & _
    "Unadvise hresult(dword);" & _
    "SetOperationFlags hresult(dword);" & _
    "SetProgressMessage hresult(wstr);" & _
    "SetProgressDialog hresult(ptr);" & _
    "SetProperties hresult(ptr);" & _
    "SetOwnerWindow hresult(hwnd);" & _
    "ApplyPropertiesToItem hresult(ptr);" & _
    "ApplyPropertiesToItems hresult(ptr);" & _
    "RenameItem hresult(ptr;wstr;ptr);" & _
    "RenameItems hresult(ptr;wstr);" & _
    "MoveItem hresult(ptr;ptr;wstr;ptr);" & _
    "MoveItems hresult(ptr;ptr);" & _
    "CopyItem hresult(ptr;ptr;wstr;ptr);" & _
    "CopyItems hresult(ptr;ptr);" & _
    "DeleteItem hresult(ptr;ptr);" & _
    "DeleteItems hresult(ptr);" & _
    "NewItem hresult(ptr;dword;wstr;wstr;ptr);" & _
    "PerformOperations hresult();" & _
    "GetAnyOperationsAborted hresult(ptr*);"

CreateNewItem(@ScriptDir & "\New", "Test.txt")

Func CreateNewItem($sPath, $sFile)
  If Not FileExists($sPath) Then DirCreate($sPath)
  Local $oIFileOperation = ObjCreateInterface($CLSID_IFileOperation, $IID_IFileOperation, $tagIFileOperation)
  Local $pIShellItem = SHCreateItemFromParsingName($sPath, 0, DllStructGetPtr($tIIDIShellItem))
  $oIFileOperation.NewItem($pIShellItem, 0, $sFile, "", 0)
  $oIFileOperation.PerformOperations()
EndFunc   ;==>CreateNewItem

Func SHCreateItemFromParsingName($sPath, $pPbc, $pRIID)
  Local $aRes = DllCall("shell32.dll", "long", "SHCreateItemFromParsingName", "wstr", $sPath, "ptr", $pPbc, "ptr", $pRIID, "ptr*", 0)
  If @error Or $aRes[0] Then Return SetError(1)
  Return $aRes[4]
EndFunc   ;==>_SHCreateItemFromParsingName

 

Edited by Nine
Posted
8 hours ago, Nine said:

Here :

Thank you for your help. :)

Also, thanks to you pointing me in the right direction, I was able to create a function for creating a folder as well. Although I can probably combine them and likely will later.

_IFileOperationCreateFolder(@ScriptDir, "Test")

Func _IFileOperationCreateFolder($sPath, $sFile)
  Local $AttribDir = 0x00000010
  If Not FileExists($sPath) Then DirCreate($sPath)
  Local $oIFileOperation = ObjCreateInterface($CLSID_IFileOperation, $IID_IFileOperation, $tagIFileOperation)
  Local $pIShellItem = SHCreateItemFromParsingName($sPath, 0, DllStructGetPtr($tIIDIShellItem))
  $oIFileOperation.NewItem($pIShellItem, $AttribDir, $sFile, "", 0)
  $oIFileOperation.PerformOperations()
EndFunc   ;==>_IFileOperationCreateFolder

 

Posted

Here is the combined IFileOperation::NewItem function that does files or folders:

; test creating file
_IFileOperationNewItem(@ScriptDir, "New Text File.txt")

; test creating folder
_IFileOperationNewItem(@ScriptDir, "New folder", True)

Func _IFileOperationNewItem($sPath, $sName, $bFolder = False)
    Local $Attrib = 0
    If $bFolder Then $Attrib = $FILE_ATTRIBUTE_DIRECTORY
    If Not FileExists($sPath) Then DirCreate($sPath)
    Local $oIFileOperation = ObjCreateInterface($CLSID_IFileOperation, $IID_IFileOperation, $tagIFileOperation)
    Local $pIShellItem = SHCreateItemFromParsingName($sPath, 0, DllStructGetPtr($tIIDIShellItem))
    $oIFileOperation.NewItem($pIShellItem, $Attrib, $sName, "", 0)
    $oIFileOperation.PerformOperations()
EndFunc   ;==>_IFileOperationNewItem

 

Posted

@Nine I am trying to create a function to copy a single item. The example is just supposed to copy the script file to a new sub directory of the script path. Do you have any idea why it is failing?

; testing CopyItem single item
_IFileOperationCopyItem(@ScriptFullPath, @ScriptDir & "\copy")

Func _IFileOperationCopyItem($sPathFrom, $sPathTo, $iFlags = BitOR($FOFX_ADDUNDORECORD, $FOFX_RECYCLEONDELETE, $FOFX_NOCOPYHOOKS))
    If Not FileExists($sPathFrom) Then Return SetError(1, 0, False)
    If Not FileExists($sPathTo) Then DirCreate($sPathTo)
    Local $tIIDIShellItem = CLSIDFromString($IID_IShellItem)
    Local $oIFileOperation = ObjCreateInterface($CLSID_IFileOperation, $IID_IFileOperation, $tagIFileOperation)
    If Not IsObj($oIFileOperation) Then Return SetError(2, 0, False)
    Local $pIShellItemFrom = SHCreateItemFromParsingName($sPathFrom, 0, DllStructGetPtr($tIIDIShellItem))
    Local $pIShellItemTo = SHCreateItemFromParsingName($sPathTo, 0, DllStructGetPtr($tIIDIShellItem))
    If Not $pIShellItemFrom Or Not $pIShellItemTo Then Return SetError(3, 0, False)
    $oIFileOperation.SetOperationFlags($iFlags)
    $oIFileOperation.CopyItem($pIShellItemFrom, $pIShellItemTo, 0)
    Return $oIFileOperation.PerformOperations() = 0
EndFunc   ;==>_IFileOperationCopyItem

I have tried the following (separately) and all fail on the same line:

$oIFileOperation.CopyItem($pIShellItemFrom, $pIShellItemTo)
$oIFileOperation.CopyItem($pIShellItemFrom, $pIShellItemTo, 0)
$oIFileOperation.CopyItem($pIShellItemFrom, $pIShellItemTo, "")
$oIFileOperation.CopyItem($pIShellItemFrom, $pIShellItemTo, Null)

 

Posted (edited)

I've almost got all IFileOperation methods covered now. I'm just trying to document it better and still W.I.P. with adding examples.

I also added flags for whether to send to recycling bin on Delete or to delete permanently. This probably could become a UDF and be quite useful for using these modern file operation methods compared to the older _WinAPI_ShellFileOperation methods which are no longer recommended.

I think that we might have something nice coming out of this. But if we are to make this into a more thorough UDF, I would need someone to help with the IFileOperation::Advise and IFileOperationProgressSink interface part. Those are not mandatory, however, but they would be nice to have I suppose.

 

 

Edited by WildByDesign
Posted

Here is IFileOperation::RenameItem for single file or folder renaming.

RenameItem()
Func RenameItem()
    ; EXAMPLE: Renaming a single item (file or folder)
    ;
    Local $sPath = "D:\Documents\GitHub\.IFileOperation\test.txt"
    Local $sNewName = "test-renamed.txt"
    _IFileOperationRenameItem($sPath, $sNewName)
EndFunc

Func _IFileOperationRenameItem($sPath, $sNewName)
    Local $oIFileOperation = ObjCreateInterface($CLSID_IFileOperation, $IID_IFileOperation, $tagIFileOperation)
    Local $pIShellItem = SHCreateItemFromParsingName($sPath, 0, DllStructGetPtr($tIIDIShellItem))
    $oIFileOperation.RenameItem($pIShellItem, $sNewName, 0)
    $oIFileOperation.PerformOperations()
EndFunc   ;==>_IFileOperationRenameItem

 

I know how to do IFileOperation::RenameItems for multiple file and folder renaming but I'm not sure that I see much purpose is mass renaming a whole list of files to the same name.

Quote

If more than one of the items in the collection at pUnkItems is in the same folder, the renamed files are appended with a number in parentheses to differentiate them, for instance newfile(1).txt, newfile(2).txt, and newfile(3).txt.

 

Posted
3 hours ago, WildByDesign said:

but I'm not sure that I see much purpose is mass renaming a whole list of files

Files are not from the same folder.

Quote

If more than one of the items in the collection at pUnkItems is in the same folder, the renamed files are appended with a number in parentheses to differentiate them, for instance newfile(1).txt, newfile(2).txt, and newfile(3).txt.

Let say you want to rename all files from Test.txt to TestNew.txt all over your disk space...

Posted

I have attached the latest IFileOperation functions to this post with examples as well. You would have to tweak the examples to match testing scenarios on your systems.

I have functions covering the following methods:

; _IFileOperationNewItem
; _IFileOperationRenameItem
; _IFileOperationRenameItems (not working properly)
; _IFileOperationDeleteItem
; _IFileOperationDeleteItems
; _IFileOperationCopyItem
; _IFileOperationCopyItems
; _IFileOperationMoveItem
; _IFileOperationMoveItems

From my own testing, all of them are working as expected with the exception of _IFileOperationRenameItems. I keep getting weird errors and inconsistencies, whether the files are in different folders or the same folder. Something is wrong with it.

@Nine Could you test _IFileOperationRenameItems and see what I have done wrong there?

Func RenameItems()
    ; EXAMPLE: Rename multiple items (files and folders)
    ;
    ; Items must be in a 1D array
    Local $sName1 = "D:\Documents\GitHub\.IFileOperation\test\test.txt"
    Local $sName2 = "D:\Documents\GitHub\.IFileOperation\test2\test2.txt"
    Local $sName3 = "D:\Documents\GitHub\.IFileOperation\test3\test3.txt"
    Local $sNewName = "test-renamed.txt"
    Local $aItems[3] = [$sName1, $sName2, $sName3]
    ; IDataObject is created from array
    Local $pDataObj = GetDataObject($aItems)
    _IFileOperationRenameItems($pDataObj, $sNewName)
    ; Object must be released after file operation is complete
    _Release($pDataObj)
EndFunc

Func _IFileOperationRenameItems($pDataObj, $sNewName)
    Local $tIIDIShellItem = CLSIDFromString($IID_IShellItem)
    Local $tIIDIShellItemArray = CLSIDFromString($IID_IShellItemArray)
    Local $oIFileOperation = ObjCreateInterface($CLSID_IFileOperation, $IID_IFileOperation, $tagIFileOperation)
    If Not IsObj($oIFileOperation) Then Return SetError(2, 0, False)
    $oIFileOperation.RenameItems($pDataObj, $sNewName)
    Return $oIFileOperation.PerformOperations() = 0
EndFunc   ;==>_IFileOperationRenameItems

 

Examples.au3 IFileOperation.au3

Posted

Here my take on it :

CreateNewItem(@ScriptDir & "\New", "Test1.txt")
CreateNewItem(@ScriptDir & "\New", "Test2.txt")
Local $aFile = _FileListToArray(@ScriptDir & "\New", "*.txt", $FLTA_FILES, False)
_ArrayDisplay($aFile)

RenameItems(@ScriptDir & "\New", $aFile, "TestNew.txt")

Func RenameItems($sPath, $aFileOld, $sFileNew)
  Local $oIFileOperation = ObjCreateInterface($CLSID_IFileOperation, $IID_IFileOperation, $tagIFileOperation)
  Local $pPIDL = _WinAPI_ShellILCreateFromPath($sPath)
  Local $tPtr = DllStructCreate("ptr array[" & $aFile[0] & "]")
  For $i = 1 To $aFile[0]
    $tPtr.array(($i)) = _WinAPI_ShellILCreateFromPath($sPath & '\' & $aFileOld[$i])
  Next
  Local $aRet = DllCall("shell32.dll", "long", "SHCreateShellItemArray", "ptr", $pPIDL, "ptr", 0, "uint", $aFile[0], "struct*", $tPtr, "ptr*", 0)
  $oIFileOperation.SetOperationFlags($FOF_RENAMEONCOLLISION)
  $oIFileOperation.RenameItems($aRet[5], $sFileNew)
  $oIFileOperation.PerformOperations()
EndFunc   ;==>RenameItems

 

Posted
On 2/21/2026 at 11:05 AM, Nine said:

Here my take on it :

Sorry, I had forgotten to reply. I tested this code the day you posted it and completely forgot. This is a brilliant take on RenameItems and it gives much more purpose and just makes more sense to me now. I've never really been a big fan of mass file renaming so therefore never put much thought into it. It makes sense that you have FOF_RENAMEONCOLLISION there as well. We could possible use FOFX_PRESERVEFILEEXTENSIONS there as well, but they may depend completely on the overall goal.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...