{"id":1779,"date":"2024-02-01T10:30:00","date_gmt":"2024-02-01T00:30:00","guid":{"rendered":"https:\/\/digitalbbq.au\/?p=1779"},"modified":"2024-01-15T08:03:45","modified_gmt":"2024-01-14T22:03:45","slug":"better-file-and-folder-management-with-excel","status":"publish","type":"post","link":"https:\/\/digitalbbq.au\/index.php\/2024\/02\/01\/better-file-and-folder-management-with-excel\/","title":{"rendered":"Better File and Folder Management &#8211; With Excel"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">An old colleague shared this fantastic Excel file with me when I needed to bulk rename files. Our IT policies didn&#8217;t allow us to install useful tools such as <a href=\"https:\/\/www.publicspace.net\/windows\/BetterFileRename\/index.html\" data-type=\"link\" data-id=\"https:\/\/www.publicspace.net\/windows\/BetterFileRename\/index.html\">Better File Rename<\/a>. The Excel file contains VBA macros that list folder contents and rename files, and it has now become my go-to method to bulk rename files as you can use all the smarts of Excel formulas to generate file names.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"323\" src=\"https:\/\/digitalbbq.au\/wp-content\/uploads\/2024\/01\/image-4-1024x323.png\" alt=\"\" class=\"wp-image-1780\" srcset=\"https:\/\/digitalbbq.au\/wp-content\/uploads\/2024\/01\/image-4-1024x323.png 1024w, https:\/\/digitalbbq.au\/wp-content\/uploads\/2024\/01\/image-4-300x95.png 300w, https:\/\/digitalbbq.au\/wp-content\/uploads\/2024\/01\/image-4-768x242.png 768w, https:\/\/digitalbbq.au\/wp-content\/uploads\/2024\/01\/image-4.png 1306w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The tool has even come to save me countless hours in a complex series of events where, due to a misunderstanding of an intermediary between our team and the client, drawing revisions and titles became out of sequence between what we had on record as our latest information versus what the client had received. In some instances, the client had unknowingly missed two revisions of some of our drawings.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Using what I had on record, collecting what the client had on record, and using <a href=\"https:\/\/digitalbbq.au\/index.php\/2023\/10\/05\/fast-track-your-midp-quality-checks-with-bluebeam-bookmarks\/\" data-type=\"post\" data-id=\"1306\">Bluebeam to extract information from the PDFs<\/a> across the project, I was able to quickly identify the discrepancies in drawings, identify drawings where revisions had to be removed and rename the deliverables to suit, including the revision as a suffix to the file name.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">While I certainly can&#8217;t take credit for the original Excel file, I recently had to make a series of changes for various reasons.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">x64 Compatibility<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The first modification came when I wanted to use the Excel file on an x64 installation of Office. It just didn&#8217;t work. Not to be defeated, after some research I discovered that the issue was related to the 32-bit API declarations in the code, which won&#8217;t work on a 64-bit system. I had to set about modifying the declarations to make them compatible with both 32-bit and 64-bit systems.<\/p>\n\n\n\n<details class=\"wp-block-details is-layout-flow wp-block-details-is-layout-flow\"><summary>Code (click to expand)<\/summary><pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">#If VBA7 Then<\/span>\n    <span class=\"hljs-keyword\">Public<\/span> Type BROWSEINFO\n        hOwner <span class=\"hljs-keyword\">As<\/span> LongPtr\n        pidlRoot <span class=\"hljs-keyword\">As<\/span> LongPtr\n        pszDisplayName <span class=\"hljs-keyword\">As<\/span> String\n        lpszTitle <span class=\"hljs-keyword\">As<\/span> String\n        ulFlags <span class=\"hljs-keyword\">As<\/span> Long\n        lpfn <span class=\"hljs-keyword\">As<\/span> LongPtr\n        lParam <span class=\"hljs-keyword\">As<\/span> Long\n        iImage <span class=\"hljs-keyword\">As<\/span> Long\n    End Type\n\n    <span class=\"hljs-keyword\">Declare<\/span> PtrSafe <span class=\"hljs-function\"><span class=\"hljs-keyword\">Function<\/span> <span class=\"hljs-title\">SHGetPathFromIDList<\/span> <span class=\"hljs-title\">Lib<\/span> \"<span class=\"hljs-title\">shell32<\/span>.<span class=\"hljs-title\">dll<\/span>\" <span class=\"hljs-title\">Alias<\/span> \"<span class=\"hljs-title\">SHGetPathFromIDListA<\/span>\" <span class=\"hljs-params\">(ByVal pidl As LongPtr, ByVal pszPath As String)<\/span> <span class=\"hljs-title\">As<\/span> <span class=\"hljs-title\">Long<\/span>\n    <span class=\"hljs-title\">Declare<\/span> <span class=\"hljs-title\">PtrSafe<\/span> <span class=\"hljs-title\">Function<\/span> <span class=\"hljs-title\">SHBrowseForFolder<\/span> <span class=\"hljs-title\">Lib<\/span> \"<span class=\"hljs-title\">shell32<\/span>.<span class=\"hljs-title\">dll<\/span>\" <span class=\"hljs-title\">Alias<\/span> \"<span class=\"hljs-title\">SHBrowseForFolderA<\/span>\" <span class=\"hljs-params\">(ByRef lpBrowseInfo As BROWSEINFO)<\/span> <span class=\"hljs-title\">As<\/span> <span class=\"hljs-title\">LongPtr<\/span>\n#<span class=\"hljs-title\">Else<\/span>\n    <span class=\"hljs-title\">Public<\/span> <span class=\"hljs-title\">Type<\/span> <span class=\"hljs-title\">BROWSEINFO<\/span>\n        <span class=\"hljs-title\">hOwner<\/span> <span class=\"hljs-title\">As<\/span> <span class=\"hljs-title\">Long<\/span>\n        <span class=\"hljs-title\">pidlRoot<\/span> <span class=\"hljs-title\">As<\/span> <span class=\"hljs-title\">Long<\/span>\n        <span class=\"hljs-title\">pszDisplayName<\/span> <span class=\"hljs-title\">As<\/span> <span class=\"hljs-title\">String<\/span>\n        <span class=\"hljs-title\">lpszTitle<\/span> <span class=\"hljs-title\">As<\/span> <span class=\"hljs-title\">String<\/span>\n        <span class=\"hljs-title\">ulFlags<\/span> <span class=\"hljs-title\">As<\/span> <span class=\"hljs-title\">Long<\/span>\n        <span class=\"hljs-title\">lpfn<\/span> <span class=\"hljs-title\">As<\/span> <span class=\"hljs-title\">Long<\/span>\n        <span class=\"hljs-title\">lParam<\/span> <span class=\"hljs-title\">As<\/span> <span class=\"hljs-title\">Long<\/span>\n        <span class=\"hljs-title\">iImage<\/span> <span class=\"hljs-title\">As<\/span> <span class=\"hljs-title\">Long<\/span>\n    <span class=\"hljs-title\">End<\/span> <span class=\"hljs-title\">Type<\/span>\n\n    <span class=\"hljs-title\">Declare<\/span> <span class=\"hljs-title\">Function<\/span> <span class=\"hljs-title\">SHGetPathFromIDList<\/span> <span class=\"hljs-title\">Lib<\/span> \"<span class=\"hljs-title\">shell32<\/span>.<span class=\"hljs-title\">dll<\/span>\" <span class=\"hljs-title\">Alias<\/span> \"<span class=\"hljs-title\">SHGetPathFromIDListA<\/span>\" <span class=\"hljs-params\">(ByVal pidl As Long, ByVal pszPath As String)<\/span> <span class=\"hljs-title\">As<\/span> <span class=\"hljs-title\">Long<\/span>\n    <span class=\"hljs-title\">Declare<\/span> <span class=\"hljs-title\">Function<\/span> <span class=\"hljs-title\">SHBrowseForFolder<\/span> <span class=\"hljs-title\">Lib<\/span> \"<span class=\"hljs-title\">shell32<\/span>.<span class=\"hljs-title\">dll<\/span>\" <span class=\"hljs-title\">Alias<\/span> \"<span class=\"hljs-title\">SHBrowseForFolderA<\/span>\" <span class=\"hljs-params\">(ByRef lpBrowseInfo As BROWSEINFO)<\/span> <span class=\"hljs-title\">As<\/span> <span class=\"hljs-title\">Long<\/span>\n#<span class=\"hljs-title\">End<\/span> <span class=\"hljs-title\">If<\/span>\n\n<span class=\"hljs-title\">Function<\/span> <span class=\"hljs-title\">GetDirectory<\/span><span class=\"hljs-params\">(Optional Msg)<\/span> <span class=\"hljs-title\">As<\/span> <span class=\"hljs-title\">String<\/span>\n    <span class=\"hljs-title\">Dim<\/span> <span class=\"hljs-title\">bInfo<\/span> <span class=\"hljs-title\">As<\/span> <span class=\"hljs-title\">BROWSEINFO<\/span>\n    <span class=\"hljs-title\">Dim<\/span> <span class=\"hljs-title\">Path<\/span> <span class=\"hljs-title\">As<\/span> <span class=\"hljs-title\">String<\/span>\n    <span class=\"hljs-title\">Dim<\/span> <span class=\"hljs-title\">r<\/span> <span class=\"hljs-title\">As<\/span> <span class=\"hljs-title\">Long<\/span>\n    #<span class=\"hljs-title\">If<\/span> <span class=\"hljs-title\">VBA7<\/span> <span class=\"hljs-title\">Then<\/span>\n        <span class=\"hljs-title\">Dim<\/span> <span class=\"hljs-title\">x<\/span> <span class=\"hljs-title\">As<\/span> <span class=\"hljs-title\">LongPtr<\/span>\n    #<span class=\"hljs-title\">Else<\/span>\n        <span class=\"hljs-title\">Dim<\/span> <span class=\"hljs-title\">x<\/span> <span class=\"hljs-title\">As<\/span> <span class=\"hljs-title\">Long<\/span>\n    #<span class=\"hljs-title\">End<\/span> <span class=\"hljs-title\">If<\/span>\n    <span class=\"hljs-title\">Dim<\/span> <span class=\"hljs-title\">pos<\/span> <span class=\"hljs-title\">As<\/span> <span class=\"hljs-title\">Integer<\/span>\n \n    ' <span class=\"hljs-title\">Rest<\/span> <span class=\"hljs-title\">of<\/span> <span class=\"hljs-title\">your<\/span> <span class=\"hljs-title\">code<\/span> <span class=\"hljs-title\">remains<\/span> <span class=\"hljs-title\">the<\/span> <span class=\"hljs-title\">same<\/span>\n<span class=\"hljs-title\">End<\/span> <span class=\"hljs-title\">Function<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">The <code>#If VBA7 Then<\/code> directive checks for VBA 7.0 or later, which includes all 64-bit versions of Office. If so VBA 7.0 is detected, it uses the LongPtr data type and the PtrSafe keyword, which is required for 64-bit compatibility. Using this solution, if the file is opened on a system that is using an older version of VBA, it falls back to the original 32-bit declarations.<\/p>\n<\/details>\n\n\n\n<h3 class=\"wp-block-heading\">Adding Folder Renaming Functionality<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">I was then approached with a scenario that involved auditing a series of folders, which included tasks like renaming and relocating these folders. I thought this would be a perfect use of the Excel tool; it&#8217;s just it didn&#8217;t have the functionality to work with folders.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Looking into VBA <em>FileSystemObject<\/em> methods, I built in the functionality to perform the same work on folders as it does with files. Not being particularly adept at VBA, I stumbled when I created a &#8220;Loop without do&#8221; error, but with review and adjustment of the loop structures in the code, the fix ended up being straightforward enough, but obviously critical to have working correctly when dealing with critical business documents.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Fixing the Folder Dialog Browser Button<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Pleased with the new functionality for renaming and relocating folders, I passed the tool to a user for testing. The very first thing she did when using the Excel tool was to click the browse button, which, of course, makes complete sense to do so.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"776\" height=\"28\" src=\"https:\/\/digitalbbq.au\/wp-content\/uploads\/2024\/01\/image-5.png\" alt=\"\" class=\"wp-image-1785\" srcset=\"https:\/\/digitalbbq.au\/wp-content\/uploads\/2024\/01\/image-5.png 776w, https:\/\/digitalbbq.au\/wp-content\/uploads\/2024\/01\/image-5-300x11.png 300w, https:\/\/digitalbbq.au\/wp-content\/uploads\/2024\/01\/image-5-768x28.png 768w\" sizes=\"auto, (max-width: 776px) 100vw, 776px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The thing is, it turns out it didn&#8217;t work. You could select the folder, but then it would cause Excel to crash and close without warning. Whenever I used the tool, I cut and paste the folder location I wanted to work with as text to the cell where it was required instead of clicking the button.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It turned out to be another issue with 32 and 64bit compatibility as the original code included the functions <em>SHBrowseFolder<\/em> and <em>SHGetPathFromIDList,<\/em> which I could have resolved by going down the path of <a href=\"https:\/\/stackoverflow.com\/questions\/67379192\/how-to-use-windows-shbrowseforfolder-function-on-32-bit-or-64-bit-excel-vba\">declaring 32 and 64bit compatible variables<\/a>, but instead, I decided the best action would be to use Excel&#8217;s built-in folder picker dialogue, which is generally more stable and should be compatible with any version of Excel.<\/p>\n\n\n\n<details class=\"wp-block-details is-layout-flow wp-block-details-is-layout-flow\"><summary>Code (click to expand)<\/summary><pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">Function<\/span> <span class=\"hljs-title\">GetDirectory<\/span><span class=\"hljs-params\">(Optional Msg As String)<\/span> <span class=\"hljs-title\">As<\/span> <span class=\"hljs-title\">String<\/span>\n    <span class=\"hljs-title\">Dim<\/span> <span class=\"hljs-title\">fd<\/span> <span class=\"hljs-title\">As<\/span> <span class=\"hljs-title\">FileDialog<\/span>\n    <span class=\"hljs-title\">Set<\/span> <span class=\"hljs-title\">fd<\/span> = <span class=\"hljs-title\">Application<\/span>.<span class=\"hljs-title\">FileDialog<\/span><span class=\"hljs-params\">(msoFileDialogFolderPicker)<\/span>\n\n    <span class=\"hljs-title\">With<\/span> <span class=\"hljs-title\">fd<\/span>\n        <span class=\"hljs-title\">If<\/span> <span class=\"hljs-title\">Msg<\/span> &lt;&gt; \"\" <span class=\"hljs-title\">Then<\/span> .<span class=\"hljs-title\">Title<\/span> = <span class=\"hljs-title\">Msg<\/span> ' <span class=\"hljs-title\">Set<\/span> <span class=\"hljs-title\">the<\/span> <span class=\"hljs-title\">title<\/span> <span class=\"hljs-title\">of<\/span> <span class=\"hljs-title\">the<\/span> <span class=\"hljs-title\">dialog<\/span> <span class=\"hljs-title\">box<\/span> <span class=\"hljs-title\">if<\/span> <span class=\"hljs-title\">provided<\/span>\n        <span class=\"hljs-title\">If<\/span> .<span class=\"hljs-title\">Show<\/span> = -1 <span class=\"hljs-title\">Then<\/span> ' <span class=\"hljs-title\">If<\/span> <span class=\"hljs-title\">the<\/span> <span class=\"hljs-title\">user<\/span> <span class=\"hljs-title\">makes<\/span> <span class=\"hljs-title\">a<\/span> <span class=\"hljs-title\">selection<\/span>\n            <span class=\"hljs-title\">GetDirectory<\/span> = .<span class=\"hljs-title\">SelectedItems<\/span><span class=\"hljs-params\">(<span class=\"hljs-number\">1<\/span>)<\/span> ' <span class=\"hljs-title\">Return<\/span> <span class=\"hljs-title\">the<\/span> <span class=\"hljs-title\">selected<\/span> <span class=\"hljs-title\">path<\/span>\n        <span class=\"hljs-title\">Else<\/span>\n            <span class=\"hljs-title\">GetDirectory<\/span> = \"\" ' <span class=\"hljs-title\">If<\/span> <span class=\"hljs-title\">the<\/span> <span class=\"hljs-title\">user<\/span> <span class=\"hljs-title\">cancels<\/span>, <span class=\"hljs-title\">return<\/span> <span class=\"hljs-title\">an<\/span> <span class=\"hljs-title\">empty<\/span> <span class=\"hljs-title\">string<\/span>\n        <span class=\"hljs-title\">End<\/span> <span class=\"hljs-title\">If<\/span>\n    <span class=\"hljs-title\">End<\/span> <span class=\"hljs-title\">With<\/span>\n<span class=\"hljs-title\">End<\/span> <span class=\"hljs-title\">Function<\/span>\n<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre><\/details>\n\n\n\n<h3 class=\"wp-block-heading\">Auditing Folders Based on Last Modified Dates of the Contained Files<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The final development request I received was to implement a feature in the tool that calculates the time in months since the last modification of a folder&#8217;s contents. This would assist in the decision-making process of determining when a folder could be archived.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To tackle this request, it required the creation of an <em>AuditFolders<\/em> subroutine that goes into each folder and collects the timestamps from each file, and returning the date of the newest file. I achieved this by modifying the <em>ListFolders<\/em> subroutine to iterate through each file in the folder, get the latest modified date of each file and the name of that file, then report the most recent date to a column in Excel.<\/p>\n\n\n\n<details class=\"wp-block-details is-layout-flow wp-block-details-is-layout-flow\"><summary>Code (click to expand)<\/summary><pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">Sub AuditFolders()\r\n    <span class=\"hljs-string\">'Create a list of folders and find the newest file'<\/span>s date in each\r\n\r\n    <span class=\"hljs-string\">'Check for errors\r\n    ErrorMsg = \"Problem creating list - check path.\"\r\n    If Range(\"Path\").Value = \"\" Then GoTo ErrorHandler\r\n    \r\n    '<\/span><span class=\"hljs-keyword\">If<\/span> no error then main code\r\n    Application.ScreenUpdating = <span class=\"hljs-keyword\">False<\/span>\r\n    \r\n    DirName = Range(<span class=\"hljs-string\">\"Path\"<\/span>).Value\r\n    <span class=\"hljs-keyword\">If<\/span> Right(DirName, <span class=\"hljs-number\">1<\/span>) &lt;&gt; <span class=\"hljs-string\">\"\\\" Then DirName = DirName &amp; \"<\/span>\\<span class=\"hljs-string\">\"\r\n    \r\n    'Clear area for list\r\n    Range(\"<\/span>Filelist<span class=\"hljs-string\">\").Offset(1, 0).Select\r\n    RowCounter = 0\r\n    Range(\"<\/span><span class=\"hljs-string\">B\" &amp; ActiveCell.Row &amp; \"<\/span>:G65536<span class=\"hljs-string\">\").ClearContents\r\n    Range(\"<\/span><span class=\"hljs-string\">B\" &amp; ActiveCell.Row &amp; \"<\/span>:G65536<span class=\"hljs-string\">\").Interior.ColorIndex = 2\r\n    \r\n    ' Loop to insert folder names and details\r\n    NextFile = Dir(DirName &amp; \"<\/span>*.*<span class=\"hljs-string\">\", vbDirectory)\r\n    Do While NextFile &lt;&gt; \"<\/span><span class=\"hljs-string\">\"\r\n        If (GetAttr(DirName &amp; NextFile) And vbDirectory) = vbDirectory Then\r\n            If NextFile &lt;&gt; \"<\/span>.<span class=\"hljs-string\">\" And NextFile &lt;&gt; \"<\/span>..<span class=\"hljs-string\">\" Then\r\n                ' Populate the list with folder details\r\n                ActiveCell.Offset(RowCounter, 0).Value = NextFile\r\n                'ActiveCell.Offset(RowCounter, 5).Value = NextFile ' Copy folder name to column G\r\n\r\n                ' Find the newest file in the folder\r\n                Dim NewestFileInfo As FileInfo\r\n                NewestFileInfo = GetNewestFileInfo(DirName &amp; NextFile)\r\n\r\n                ' Add the newest file's date to column D and name to the next column\r\n                If NewestFileInfo.DateModified &lt;&gt; 0 Then\r\n                    ActiveCell.Offset(RowCounter, 2).Value = NewestFileInfo.DateModified\r\n                    ActiveCell.Offset(RowCounter, 3).Value = NewestFileInfo.FileName\r\n\r\n                    ' Calculate the age in months and add to the next column\r\n                    Dim FileAgeMonths As Integer\r\n                    FileAgeMonths = DateDiff(\"<\/span>m<span class=\"hljs-string\">\", NewestFileInfo.DateModified, Now)\r\n                    ActiveCell.Offset(RowCounter, 4).Value = FileAgeMonths\r\n                End If\r\n\r\n                RowCounter = RowCounter + 1\r\n            End If\r\n        End If\r\n        NextFile = Dir() ' Get next folder\r\n    Loop\r\n    If ActiveCell.Offset(1, 0).Value = \"<\/span><span class=\"hljs-string\">\" Then &#91;A1].Select: Exit Sub\r\n    \r\n    'Sort alphabetically\r\n    Selection.CurrentRegion.Select\r\n    Selection.Sort key1:=Range(ActiveCell.Address), order1:=xlAscending, Header:=xlYes\r\n    &#91;A1].Select\r\n    Exit Sub\r\n\r\nErrorHandler:\r\n    MsgBox ErrorMsg, vbInformation, \"<\/span><span class=\"hljs-keyword\">List<\/span> folders<span class=\"hljs-string\">\"\r\n    &#91;A1].Select\r\nEnd Sub\n\nSub AuditFiles()\r\n    'Create a list of all files in folders\r\n\r\n    'Check for errors\r\n    ErrorMsg = \"<\/span>Problem creating <span class=\"hljs-keyword\">list<\/span> - check path.<span class=\"hljs-string\">\"\r\n    If Range(\"<\/span>Path<span class=\"hljs-string\">\").Value = \"<\/span><span class=\"hljs-string\">\" Then GoTo ErrorHandler\r\n    \r\n    'If no error then main code\r\n    Application.ScreenUpdating = False\r\n    \r\n    Dim fso As Object\r\n    Set fso = CreateObject(\"<\/span>Scripting.FileSystemObject<span class=\"hljs-string\">\")\r\n\r\n    DirName = Range(\"<\/span>Path<span class=\"hljs-string\">\").Value\r\n    If Right(DirName, 1) &lt;&gt; \"<\/span>\\<span class=\"hljs-string\">\" Then DirName = DirName &amp; \"<\/span>\\<span class=\"hljs-string\">\"\r\n\r\n    'Clear area for list\r\n    Range(\"<\/span>Filelist<span class=\"hljs-string\">\").Offset(1, 0).Select\r\n    RowCounter = 0\r\n    Range(\"<\/span><span class=\"hljs-string\">B\" &amp; ActiveCell.Row &amp; \"<\/span>:F65536<span class=\"hljs-string\">\").ClearContents\r\n    Range(\"<\/span><span class=\"hljs-string\">B\" &amp; ActiveCell.Row &amp; \"<\/span>:F65536<span class=\"hljs-string\">\").Interior.ColorIndex = 2\r\n\r\n    ' Get the root folder\r\n    Dim rootFolder As Object\r\n    Set rootFolder = fso.GetFolder(DirName)\r\n    \r\n    ' Loop through each subfolder\r\n    Dim subFolder As Object\r\n    For Each subFolder In rootFolder.SubFolders\r\n        ' Loop through each file in the subfolder\r\n        Dim file As Object\r\n        For Each file In subFolder.Files\r\n            ' Populate the list with file details\r\n            ActiveCell.Offset(RowCounter, 0).Value = subFolder.Name\r\n            ActiveCell.Offset(RowCounter, 3).Value = file.Name\r\n            ActiveCell.Offset(RowCounter, 2).Value = file.DateLastModified\r\n\r\n            ' Calculate the age in months and add to the next column\r\n            Dim FileAgeMonths As Integer\r\n            FileAgeMonths = DateDiff(\"<\/span>m<span class=\"hljs-string\">\", file.DateLastModified, Now)\r\n            ActiveCell.Offset(RowCounter, 4).Value = FileAgeMonths\r\n\r\n            RowCounter = RowCounter + 1\r\n        Next file\r\n    Next subFolder\r\n\r\n    If ActiveCell.Offset(1, 0).Value = \"<\/span><span class=\"hljs-string\">\" Then &#91;A1].Select: Exit Sub\r\n    \r\n    'Sort alphabetically\r\n    Selection.CurrentRegion.Select\r\n    Selection.Sort key1:=Range(ActiveCell.Address), order1:=xlAscending, Header:=xlYes\r\n    &#91;A1].Select\r\n    Exit Sub\r\n\r\nErrorHandler:\r\n    MsgBox ErrorMsg, vbInformation, \"<\/span><span class=\"hljs-keyword\">List<\/span> files<span class=\"hljs-string\">\"\r\n    &#91;A1].Select\r\nEnd Sub\r\n\r\n\r\nFunction GetNewestFileDate(FolderPath As String) As Date\r\n    Dim fso As Object\r\n    Set fso = CreateObject(\"<\/span>Scripting.FileSystemObject<span class=\"hljs-string\">\")\r\n    \r\n    Dim folder As Object\r\n    Set folder = fso.GetFolder(FolderPath)\r\n    \r\n    Dim file As Object\r\n    Dim mostRecentDate As Date\r\n    mostRecentDate = 0 ' Initialize to a zero date\r\n\r\n    For Each file In folder.Files\r\n        If file.DateLastModified &gt; mostRecentDate Then\r\n            mostRecentDate = file.DateLastModified\r\n        End If\r\n    Next file\r\n\r\n    GetNewestFileDate = mostRecentDate\r\nEnd Function\r\n\r\nFunction GetNewestFileInfo(FolderPath As String) As FileInfo\r\n    Dim fso As Object\r\n    Set fso = CreateObject(\"<\/span>Scripting.FileSystemObject<span class=\"hljs-string\">\")\r\n    \r\n    Dim folder As Object\r\n    Set folder = fso.GetFolder(FolderPath)\r\n    \r\n    Dim file As Object\r\n    Dim mostRecentInfo As FileInfo\r\n    mostRecentInfo.DateModified = 0 ' Initialize to a zero date\r\n\r\n    For Each file In folder.Files\r\n        If file.DateLastModified &gt; mostRecentInfo.DateModified Then\r\n            mostRecentInfo.DateModified = file.DateLastModified\r\n            mostRecentInfo.FileName = file.Name\r\n        End If\r\n    Next file\r\n\r\n    GetNewestFileInfo = mostRecentInfo\r\nEnd Function<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n<\/details>\n\n\n\n<p class=\"wp-block-paragraph\">As you can see from the code, there was a little more to it than adding a single subroutine, but the result is worth it, quickly providing a visual representation of the age of folders based on their most recently modified file, with the below example from the Autodesk installation folder on my machine:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"518\" src=\"https:\/\/digitalbbq.au\/wp-content\/uploads\/2024\/01\/image-7-1024x518.png\" alt=\"\" class=\"wp-image-1788\" srcset=\"https:\/\/digitalbbq.au\/wp-content\/uploads\/2024\/01\/image-7-1024x518.png 1024w, https:\/\/digitalbbq.au\/wp-content\/uploads\/2024\/01\/image-7-300x152.png 300w, https:\/\/digitalbbq.au\/wp-content\/uploads\/2024\/01\/image-7-768x389.png 768w, https:\/\/digitalbbq.au\/wp-content\/uploads\/2024\/01\/image-7-870x440.png 870w, https:\/\/digitalbbq.au\/wp-content\/uploads\/2024\/01\/image-7.png 1304w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Potential Future Development<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">While I don&#8217;t intend to develop the tool any further, as it fits my current needs quite nicely, there is ample opportunity to build in more functionality to elevate the tool&#8217;s utility for a wider range of applications, such as:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Advanced filtering options to locate files\/folders by various criteria<\/li>\n\n\n\n<li>A duplicate file finder to manage redundancy<\/li>\n\n\n\n<li>A feature to calculate and display file\/folder sizes for efficient disk space management<\/li>\n\n\n\n<li>The ability to export lists and details to CSV or PDF for record-keeping<\/li>\n\n\n\n<li>Integrating customizable reports for user-defined data analysis<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">I often tell people that a tool&#8217;s potential is limited only by their imagination and skill. While some might see this Excel VBA tool as using the right tool for the wrong job, I&#8217;d argue for its versatility in managing complex data and manipulating it with ease. This tool offers unmatched flexibility in file renaming tasks for anyone skilled in Excel formulas. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I&#8217;m sharing both the code and the complete Excel file on GitHub, and I&#8217;m keen to see the potential for collaborative enhancements that the tool might receive from the community. If you find new ways to enhance or utilise this tool, or have insights to share, I&#8217;d love to hear about it.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>An old colleague shared this fantastic Excel file with me when I needed to bulk rename files. Our IT policies didn&#8217;t allow us to install useful tools such as Better File Rename. The Excel file contains VBA macros that list folder contents and rename files, and it has now become my go-to method to bulk rename files as you can&#8230;<\/p>\n","protected":false},"author":1,"featured_media":1789,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"advanced_seo_description":"Maximize file management efficiency with Excel VBA with custom tool creation for bulk file renaming and more","jetpack_seo_html_title":"","jetpack_seo_noindex":false,"jetpack_seo_schema_type":"","_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"Discover how Excel VBA can revolutionize file management! Check out my latest blog post where I share a powerful tool and code for efficient file renaming and more. Explore its potential on GitHub. #ExcelVBA #FileManagement #TechInnovation","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[1],"tags":[130,73],"class_list":["post-1779","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-digital","tag-code","tag-process"],"jetpack_publicize_connections":[],"jetpack-related-posts":[{"id":2371,"url":"https:\/\/digitalbbq.au\/index.php\/2025\/03\/27\/fixing-the-failed-to-open-document-error-in-cloud-hosted-revit-models\/","url_meta":{"origin":1779,"position":0},"title":"Fixing the &#8220;Failed to Open Document&#8221; Error in Cloud-Hosted Revit Models","author":"Ryan Lenihan","date":"27 March 2025","format":false,"excerpt":"Having trouble opening cloud-hosted #Revit models? If you're getting the \"Failed to open document\" error, there's a simple fix that can save you hours of troubleshooting. \ud83d\udee0\ufe0f The issue often happens when transferring files via #DesktopConnector\u2014Revit models appear as .rvt but are actually .zip files. \u2705 Solution: Rename, extract, and\u2026","rel":"","context":"In &quot;Digital&quot;","block_context":{"text":"Digital","link":"https:\/\/digitalbbq.au\/index.php\/category\/digital\/"},"img":{"alt_text":"Upset stressed African American man negative emotion fail business project with laptop desperate","src":"https:\/\/i0.wp.com\/digitalbbq.au\/wp-content\/uploads\/2025\/02\/upset-stressed-african-american-man-negative-emotion-fail-business-project-with-laptop-desperate.jpg?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/digitalbbq.au\/wp-content\/uploads\/2025\/02\/upset-stressed-african-american-man-negative-emotion-fail-business-project-with-laptop-desperate.jpg?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/digitalbbq.au\/wp-content\/uploads\/2025\/02\/upset-stressed-african-american-man-negative-emotion-fail-business-project-with-laptop-desperate.jpg?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/digitalbbq.au\/wp-content\/uploads\/2025\/02\/upset-stressed-african-american-man-negative-emotion-fail-business-project-with-laptop-desperate.jpg?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/digitalbbq.au\/wp-content\/uploads\/2025\/02\/upset-stressed-african-american-man-negative-emotion-fail-business-project-with-laptop-desperate.jpg?resize=1050%2C600&ssl=1 3x, https:\/\/i0.wp.com\/digitalbbq.au\/wp-content\/uploads\/2025\/02\/upset-stressed-african-american-man-negative-emotion-fail-business-project-with-laptop-desperate.jpg?resize=1400%2C800&ssl=1 4x"},"classes":[]},{"id":2018,"url":"https:\/\/digitalbbq.au\/index.php\/2024\/05\/16\/expanding-on-ifc-metadata-extraction-in-powerbi\/","url_meta":{"origin":1779,"position":1},"title":"Expanding on IFC Metadata Extraction in PowerBI","author":"Ryan Lenihan","date":"16 May 2024","format":false,"excerpt":"In a previous post, \"Extracting IFC Metadata Directly in Power BI Using Power Query,\" we explored how to use Power Query in PowerBI to directly extract metadata from IFC files using a Power Query shared by Shift Construction. If you haven't read that post, I recommend checking it out first\u2026","rel":"","context":"In &quot;Digital&quot;","block_context":{"text":"Digital","link":"https:\/\/digitalbbq.au\/index.php\/category\/digital\/"},"img":{"alt_text":"Currency exchange data graph and charts data","src":"https:\/\/i0.wp.com\/digitalbbq.au\/wp-content\/uploads\/2024\/05\/currency-exchange-data-graph-and-charts-data.jpg?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/digitalbbq.au\/wp-content\/uploads\/2024\/05\/currency-exchange-data-graph-and-charts-data.jpg?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/digitalbbq.au\/wp-content\/uploads\/2024\/05\/currency-exchange-data-graph-and-charts-data.jpg?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/digitalbbq.au\/wp-content\/uploads\/2024\/05\/currency-exchange-data-graph-and-charts-data.jpg?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/digitalbbq.au\/wp-content\/uploads\/2024\/05\/currency-exchange-data-graph-and-charts-data.jpg?resize=1050%2C600&ssl=1 3x, https:\/\/i0.wp.com\/digitalbbq.au\/wp-content\/uploads\/2024\/05\/currency-exchange-data-graph-and-charts-data.jpg?resize=1400%2C800&ssl=1 4x"},"classes":[]},{"id":1828,"url":"https:\/\/digitalbbq.au\/index.php\/2024\/02\/22\/revit-bulk-unit-conversion-addin\/","url_meta":{"origin":1779,"position":2},"title":"Revit Bulk Unit Conversion Addin","author":"Ryan Lenihan","date":"22 February 2024","format":false,"excerpt":"Journal scripting once offered a quick fix for converting Revit model and family units; it helped speed up the process, but it had the habit of failing on certain family types or dialogue boxes. To tackle this, I developed an add-in, which I shared on revit.com.au; which both overcame the\u2026","rel":"","context":"In &quot;Digital&quot;","block_context":{"text":"Digital","link":"https:\/\/digitalbbq.au\/index.php\/category\/digital\/"},"img":{"alt_text":"Tape measure with numbers","src":"https:\/\/i0.wp.com\/digitalbbq.au\/wp-content\/uploads\/2024\/02\/tape-measure-with-numbers.jpg?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/digitalbbq.au\/wp-content\/uploads\/2024\/02\/tape-measure-with-numbers.jpg?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/digitalbbq.au\/wp-content\/uploads\/2024\/02\/tape-measure-with-numbers.jpg?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/digitalbbq.au\/wp-content\/uploads\/2024\/02\/tape-measure-with-numbers.jpg?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/digitalbbq.au\/wp-content\/uploads\/2024\/02\/tape-measure-with-numbers.jpg?resize=1050%2C600&ssl=1 3x, https:\/\/i0.wp.com\/digitalbbq.au\/wp-content\/uploads\/2024\/02\/tape-measure-with-numbers.jpg?resize=1400%2C800&ssl=1 4x"},"classes":[]},{"id":2516,"url":"https:\/\/digitalbbq.au\/index.php\/2025\/11\/06\/introducing-the-bim-cache-cleaner\/","url_meta":{"origin":1779,"position":3},"title":"Introducing the BIM Cache Cleaner","author":"Ryan Lenihan","date":"6 November 2025","format":false,"excerpt":"Back in 2017, I wrote a post about cleaning up Autodesk Collaboration for Revit (C4R) cache files. I then wrote a small windows application, C4R Cache Cleaner, that helped the end user work through that process without having to trawl through the journal files manually. That application became one of\u2026","rel":"","context":"In &quot;Digital&quot;","block_context":{"text":"Digital","link":"https:\/\/digitalbbq.au\/index.php\/category\/digital\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/digitalbbq.au\/wp-content\/uploads\/2025\/10\/bim_cleaner.jpg?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/digitalbbq.au\/wp-content\/uploads\/2025\/10\/bim_cleaner.jpg?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/digitalbbq.au\/wp-content\/uploads\/2025\/10\/bim_cleaner.jpg?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/digitalbbq.au\/wp-content\/uploads\/2025\/10\/bim_cleaner.jpg?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/digitalbbq.au\/wp-content\/uploads\/2025\/10\/bim_cleaner.jpg?resize=1050%2C600&ssl=1 3x"},"classes":[]},{"id":2422,"url":"https:\/\/digitalbbq.au\/index.php\/2025\/08\/21\/building-a-material-cube-farm\/","url_meta":{"origin":1779,"position":4},"title":"Building a Material Cube Farm","author":"Ryan Lenihan","date":"21 August 2025","format":false,"excerpt":"When you\u2019ve got 200+ materials in your model and someone in the viz team asks for a preview, the last thing you want to do is manually place and assign materials one by one to a cube. But that\u2019s exactly what we were staring down on a recent project \u2014\u2026","rel":"","context":"In &quot;How to guides&quot;","block_context":{"text":"How to guides","link":"https:\/\/digitalbbq.au\/index.php\/category\/how-to-guides\/"},"img":{"alt_text":"colour wooden cube","src":"https:\/\/i0.wp.com\/digitalbbq.au\/wp-content\/uploads\/2025\/07\/colour-wooden-cube.jpg?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/digitalbbq.au\/wp-content\/uploads\/2025\/07\/colour-wooden-cube.jpg?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/digitalbbq.au\/wp-content\/uploads\/2025\/07\/colour-wooden-cube.jpg?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/digitalbbq.au\/wp-content\/uploads\/2025\/07\/colour-wooden-cube.jpg?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/digitalbbq.au\/wp-content\/uploads\/2025\/07\/colour-wooden-cube.jpg?resize=1050%2C600&ssl=1 3x, https:\/\/i0.wp.com\/digitalbbq.au\/wp-content\/uploads\/2025\/07\/colour-wooden-cube.jpg?resize=1400%2C800&ssl=1 4x"},"classes":[]},{"id":1261,"url":"https:\/\/digitalbbq.au\/index.php\/2023\/09\/21\/navigating-the-maze-information-management-in-the-common-data-environment\/","url_meta":{"origin":1779,"position":5},"title":"Navigating the Maze: Information Management in the Common Data Environment","author":"Ryan Lenihan","date":"21 September 2023","format":false,"excerpt":"Today, I'd like to share a portion of a recent conversation that came to me through the week. It's a real-world scenario that many of us in the AEC industry might find familiar, revolving around the challenges of managing files in a common data environment (CDE). Part 1 - WIP\u2026","rel":"","context":"In &quot;Digital&quot;","block_context":{"text":"Digital","link":"https:\/\/digitalbbq.au\/index.php\/category\/digital\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/digitalbbq.au\/wp-content\/uploads\/2023\/08\/Picture1.png?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]}],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"https:\/\/digitalbbq.au\/wp-content\/uploads\/2024\/01\/files-selection-and-management.jpg","_links":{"self":[{"href":"https:\/\/digitalbbq.au\/index.php\/wp-json\/wp\/v2\/posts\/1779","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/digitalbbq.au\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/digitalbbq.au\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/digitalbbq.au\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/digitalbbq.au\/index.php\/wp-json\/wp\/v2\/comments?post=1779"}],"version-history":[{"count":7,"href":"https:\/\/digitalbbq.au\/index.php\/wp-json\/wp\/v2\/posts\/1779\/revisions"}],"predecessor-version":[{"id":1791,"href":"https:\/\/digitalbbq.au\/index.php\/wp-json\/wp\/v2\/posts\/1779\/revisions\/1791"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/digitalbbq.au\/index.php\/wp-json\/wp\/v2\/media\/1789"}],"wp:attachment":[{"href":"https:\/\/digitalbbq.au\/index.php\/wp-json\/wp\/v2\/media?parent=1779"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/digitalbbq.au\/index.php\/wp-json\/wp\/v2\/categories?post=1779"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/digitalbbq.au\/index.php\/wp-json\/wp\/v2\/tags?post=1779"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}