Skip to main content
Code Review

Return to Answer

Commonmark migration
Source Link

#UX

UX

#Readability

Readability

#Comments

Comments

#Non-Responsiveness

Non-Responsiveness

#Performance

Performance

#Error Handling

Error Handling

#Abstraction & Maintainability

Abstraction & Maintainability

##Algorithm

Algorithm

#UX

#Readability

#Comments

#Non-Responsiveness

#Performance

#Error Handling

#Abstraction & Maintainability

##Algorithm

UX

Readability

Comments

Non-Responsiveness

Performance

Error Handling

Abstraction & Maintainability

Algorithm

added 50 characters in body
Source Link
Mathieu Guindon
  • 75.5k
  • 18
  • 194
  • 467
Option Explicit
Public Sub RenameMe()
 Dim rootPath As String
 rootPath = GetRootPath
 If rootPath = vbNullString Then Exit Sub

 Dim folders() As Variant
 Set folders = FindAllFolders(rootPath)
 Dim targetSheet As Worksheet
 Set targetSheet = Application.ActiveSheet
 OutputFolders targetSheet, folders
End Sub
Option Explicit
Public Sub RenameMe()
 Dim rootPath As String
 rootPath = GetRootPath
 Dim folders() As Variant
 Set folders = FindAllFolders(rootPath)
 Dim targetSheet As Worksheet
 Set targetSheet = Application.ActiveSheet
 OutputFolders targetSheet, folders
End Sub
Option Explicit
Public Sub RenameMe()
 Dim rootPath As String
 rootPath = GetRootPath
 If rootPath = vbNullString Then Exit Sub

 Dim folders() As Variant
 Set folders = FindAllFolders(rootPath)
 Dim targetSheet As Worksheet
 Set targetSheet = Application.ActiveSheet
 OutputFolders targetSheet, folders
End Sub
Source Link
Mathieu Guindon
  • 75.5k
  • 18
  • 194
  • 467

#UX

Before diving into the code, I have a few points to mention about the user experience of it.

  • You're not resetting the Application.Statusbar before the procedure exits, which means whenever the code runs to completion, the Excel status bar remains "frozen" with the last processed folder.
  • You're warning the user that the operation may take a long time, regardless of how many folders/subfolders there actually are.
  • Message boxes are bare-bones, without a title or an icon, and punctuation is missing from the message strings.

#Readability

The code itself reads a bit like a clogged script. Indentation is insufficient and inconsistent, and vertical whitespace is completely inexistent. This code needs to breathe a little - here's your code, without any other changes:

Private Sub PrintFolders()
 Dim objFSO As Object
 Dim objFolder As Object
 Dim objSubFolder As Object
 Dim i As Integer
 Application.StatusBar = ""
 'Create an instance of the FileSystemObject
 Set objFSO = CreateObject("Scripting.FileSystemObject")
 'Get the folder object
 Set objFolder = objFSO.GetFolder("C:\Temp")
 i = 1
 'loops through each folder in the directory and prints their names and path
 On Error GoTo handleCancel
 Application.EnableCancelKey = xlErrorHandler
 MsgBox "This may take a long time: press ESC to cancel"
 For Each objSubFolder In objFolder.subfolders
 Application.StatusBar = objSubFolder.Path & " " & objSubFolder.Name
 'print folder name
 Cells(i + 1, 1) = objSubFolder.Name
 'print folder path
 Cells(i + 1, 2) = objSubFolder.Path
 i = i + 1
 Next objSubFolder
handleCancel:
 If Err = 18 Then
 MsgBox "You cancelled"
 End If
End Sub

#Comments

There are way too many comments in that code. Good, valuable comments explain why code is doing what it does - the code itself should be self-explanatory about the what.

I would simply remove them... all.

'Create an instance of the FileSystemObject
Set objFSO = CreateObject("Scripting.FileSystemObject")

Isn't too far from:

'increment i:
i = i + 1

#Non-Responsiveness

You're running a pretty tight loop here:

For Each objSubFolder In objFolder.subfolders
 Application.StatusBar = objSubFolder.Path & " " & objSubFolder.Name
 Cells(i + 1, 1) = objSubFolder.Name
 Cells(i + 1, 2) = objSubFolder.Path
 i = i + 1
Next objSubFolder

You never give Excel a chance to breathe and actually respond to the events you're sending - namely updating the statusbar and listening to ESC keypresses.

This would fix it:

For Each objSubFolder In objFolder.subfolders
 Application.StatusBar = objSubFolder.Path & " " & objSubFolder.Name
 Cells(i + 1, 1) = objSubFolder.Name
 Cells(i + 1, 2) = objSubFolder.Path
 i = i + 1
 DoEvents
Next objSubFolder

...but it comes with a pretty high price: updating the status bar and listening for keypresses at each and every iteration will considerably slow down execution. How about reorganizing it a little, and only do that once every 10 iterations? Avoid magic numbers, give that value a meaningful name and assign it to a constant:

Const IterationsToUpdate As Integer = 10
For Each objSubFolder In objFolder.subfolders
 Cells(i + 1, 1) = objSubFolder.Name
 Cells(i + 1, 2) = objSubFolder.Path
 i = i + 1
 If i Mod IterationsToUpdate = 0 Then
 Application.StatusBar = objSubFolder.Path & " " & objSubFolder.Name
 DoEvents
 End If
Next objSubFolder

Now, you're writing to worksheet cells in a loop, while keeping Excel relatively responsive. Are you sure you're writing to the correct worksheet?

Cells(i + 1, 1) = objSubFolder.Name
Cells(i + 1, 2) = objSubFolder.Path

Without an object reference, Cells is referring to the active sheet, which the user is free to change any time as the code runs. This is another UX issue if not a bug.


#Performance

I'm willing to bet anything that your biggest bottleneck isn't the FSO, but actually writing to the cells. Of course you need to do that.. but do you need Excel to repaint its grid and verify that nothing needs to be recalculated every time you write to a cell? Of course you don't.

Switch it off:

Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual

Now, whenever you do that, you must handle runtime errors appropriately.


#Error Handling

Your error handling is copy-pasted from MSDN, which is only an example to illustrate how a specific feature works - these examples are always focused on a very specific feature, and never about best practices.

Your method should fail cleanly, and correctly cleanup whether or not an error is raised - every error-sensitive method should be templated something like this:

Sub DoSomething()
 On Error GoTo CleanFail
 'implementation code here
CleanExit:
 'cleanup code here
 Exit Sub
CleanFail:
 'error-handling code here
 Resume CleanExit
End Sub

In your case, it would look something like this:

CleanExit:
 Application.Statusbar = False
 Application.Cursor = xlDefault
 Application.ScreenUpdating = True
 Application.Calculation = xlCalculationAutomatic
 Exit Sub
CleanFail:
 Const MsgTitle As String = "Operation not completed"
 If Err.Number = 18 Then
 MsgBox "Operation was cancelled.", vbInformation, MsgTitle
 Else
 MsgBox "An error has occurred: " & Err.Description, vbCritical, MsgTitle
 End If
 Resume CleanExit

#Abstraction & Maintainability

If you need to change the folder to run it for, you need to change the code: it's screaming for a String parameter containing the root path to start searching in.

The procedure is also doing too many things. To add features you need to add more code to it, making it do even more things.

Consider these concerns:

  • Knowing what folder to start searching in
  • Acquiring the folders/subfolders' names
  • Warning the user about a potentially lengthy operation
  • Enabling/disabling/toggling screen updating, calculation, statusbar and cursor
  • Knowing what worksheet to output to
  • Dropping the folders/subfolders' names onto the output worksheet

Most of these concerns deserve a method of their own. Here's how I would write your code:

Option Explicit
Public Sub RenameMe()
 Dim rootPath As String
 rootPath = GetRootPath
 Dim folders() As Variant
 Set folders = FindAllFolders(rootPath)
 Dim targetSheet As Worksheet
 Set targetSheet = Application.ActiveSheet
 OutputFolders targetSheet, folders
End Sub

Split concerns into small, immutable procedures - a procedure should ideally have no more than one reason to change. Doing that increases the abstraction level of your code, which instantly boosts readability, which in turn automatically reduces the possibility for hidden bugs.


##Algorithm

If you noticed, the above snippet separates getting the folders and writing them to a worksheet. You may think "but I'll have to iterate all 6,000 folders twice, this is going to be so much slower!"... but I'll give you a hint: read up on Arrays and Ranges in VBA (bookmark that site!).

The crux is that you don't need to iterate anything other than the folders: you populate a 2D array as you go, and then write that entire array onto the worksheet in a single operation. Then your performance bottleneck will be the FileSystemObject, but at that point your code will be so unrecognizeable that it'll be worth posting another Code Review question! :)

lang-vb

AltStyle によって変換されたページ (->オリジナル) /