Comment Collector: Pull Every Review Note into One Printable Report
Scans every cell comment on every sheet and compiles them into a single report with hyperlinks back to each source cell. One click replaces 20 minutes of clicking through 30 tabs.
Table of Contents
TL;DR: Before you archive a completed engagement, run this macro. It extracts every preparer note, reviewer question, and partner sign-off scattered across 30 tabs into one searchable, printable report with clickable hyperlinks back to each source cell. Your original workpaper stays untouched.
The Problem
You’re archiving the Henderson Manufacturing engagement. The partner’s sign-off notes live in comments on the Fixed Assets tab (“Approved per discussion with client 03/15”). The reviewer’s questions are scattered across Sched-E (“Why did COGS increase 18% year-over-year? Reconcile.”). The preparer’s explanations are on the TB sheet (“Per bank statement — reconciled to statement #284”). Before the file goes to permanent storage, your firm requires a PDF of all review notes.
The traditional approach: Ctrl+Shift+O on every tab, copy-paste each comment into a Word doc, then format and print. Thirty tabs later you’ve wasted 20 minutes and probably missed a comment on the one tab you skipped by accident.
This macro does it all in one click. You get a new “Comments” sheet with every note, author, and cell reference — clickable hyperlinks take you straight back to the source. Print to PDF and you’re done.
#Prerequisites & Setup
What you’ll need:
- Excel 2016+ (desktop)
- A workbook with cell comments (the red triangle indicators in the corner of cells)
- Output is a new “Comments” sheet, created at the end of the workbook — your original data is never touched
Limitations:
- Only collects traditional cell comments — not threaded comments (modern Excel’s reply-thread system, introduced in Excel 365). Threaded comments use a different object model and are not included in the
Commentscollection. - Comment text is plain text only — formatting, shapes, and dimensions are not preserved in the report
- If a “Comments” sheet already exists from a previous run, it is deleted and replaced
#The Macro
Option Explicit
Sub CommentCollector()
' ── Comment Collector ──────────────────────────────
' Scans every comment on every sheet and compiles
' them into a new "Comments" sheet with hyperlinks
' back to each cell. Zero input. Does not modify
' original data.
' ──────────────────────────────────────────────────
Const OUT_SHEET As String = "Comments"
' ── State management ───────────────────────────────
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
' ── Variables ──────────────────────────────────────
Dim ws As Worksheet, wsOut As Worksheet
Dim cmt As Comment
Dim outRow As Long
Dim totalComments As Long
Dim sheetCount As Long
' ── Error handling ─────────────────────────────────
On Error GoTo CleanUp
' ── Count before building output ───────────────────
totalComments = 0
sheetCount = 0
For Each ws In ThisWorkbook.Worksheets
If ws.Comments.Count = 0 Then GoTo NextCount
sheetCount = sheetCount + 1
totalComments = totalComments + ws.Comments.Count
NextCount:
Next ws
If totalComments = 0 Then
MsgBox "No comments found in this workbook.", vbInformation
GoTo CleanUp
End If
' ── Remove old output sheet ────────────────────────
On Error Resume Next
Application.DisplayAlerts = False
ThisWorkbook.Worksheets(OUT_SHEET).Delete
Application.DisplayAlerts = True
On Error GoTo CleanUp
' ── Create output sheet ────────────────────────────
Set wsOut = ThisWorkbook.Worksheets.Add( _
After:=ThisWorkbook.Worksheets( _
ThisWorkbook.Worksheets.Count))
wsOut.Name = OUT_SHEET
' ── Write headers ──────────────────────────────────
wsOut.Range("A1:D1").Value = Array( _
"Sheet", "Cell", "Author", "Comment")
With wsOut.Range("A1:D1")
.Font.Bold = True
.Interior.Color = RGB(50, 50, 50)
.Font.Color = vbWhite
End With
' ── Collect comments ───────────────────────────────
outRow = 2
For Each ws In ThisWorkbook.Worksheets
If ws.Comments.Count = 0 Then GoTo NextSheet
For Each cmt In ws.Comments
' Sheet name
wsOut.Cells(outRow, 1).Value = ws.Name
' Hyperlinked cell address
wsOut.Hyperlinks.Add _
Anchor:=wsOut.Cells(outRow, 2), _
Address:="", _
SubAddress:="'" & ws.Name & "'!" & _
cmt.Parent.Address(False, False), _
TextToDisplay:=cmt.Parent.Address(False, False)
' Author
wsOut.Cells(outRow, 3).Value = cmt.Author
' Comment text
wsOut.Cells(outRow, 4).Value = cmt.Text
outRow = outRow + 1
Next cmt
NextSheet:
Next ws
' ── Format output ──────────────────────────────────
With wsOut
.Columns("A:D").AutoFit
.Columns("D").ColumnWidth = 55
.Range("A1").Select
ActiveWindow.FreezePanes = True
End With
MsgBox "Found " & totalComments & " comment(s) across " & _
sheetCount & " sheet(s)." & vbCrLf & vbCrLf & _
"Report created on '" & OUT_SHEET & "' sheet.", _
vbInformation, "Comment Collector"
CleanUp:
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
If Err.Number <> 0 Then
MsgBox "Error " & Err.Number & ": " & Err.Description, _
vbCritical, "Macro Error"
End If
End Sub
#How It Works
#Count first, build second
The macro makes two passes. The first pass counts how many comments exist across all sheets. If the count is zero, it exits immediately with a clean message — no empty output sheet to delete later. If comments are found, it reports the count in the MsgBox so you can mentally verify (“42 comments? That sounds about right for this engagement”).
For Each ws In ThisWorkbook.Worksheets
If ws.Comments.Count = 0 Then GoTo NextCount
sheetCount = sheetCount + 1
totalComments = totalComments + ws.Comments.Count
NextCount:
Next ws
This two-pass approach also means you get an accurate count in the MsgBox before you click OK. If you expected 50 comments and the macro says 42, you know to investigate which sheet’s comments are missing before archiving.
#The Comments collection — not SpecialCells
Excel offers multiple ways to find comments, but ws.Comments is the only one
that gives you author and text in one step. SpecialCells(xlCellTypeComments)
returns a Range — you’d still need to dig each comment out of each cell
individually. The Comments collection iterates cleanly and skips sheets with
no comments automatically (Count = 0).
Each Comment object exposes:
.Parent— the Range the comment is attached to (cell A2, B14, etc.).Author— the username of whoever created it.Text— the full comment body
#Hyperlinks that survive sheet renames
wsOut.Hyperlinks.Add _
Anchor:=wsOut.Cells(outRow, 2), _
Address:="", _
SubAddress:="'" & ws.Name & "'!" & _
cmt.Parent.Address(False, False), _
TextToDisplay:=cmt.Parent.Address(False, False)
The Address is empty (no external file), and SubAddress points directly to
the source cell. The single quotes around ws.Name handle tabs with spaces or
special characters — 'Sched-A Income'!B6 is a valid Excel reference, but
Sched-A Income!B6 is not.
Address(False, False) returns the cell reference without dollar signs (B6
instead of $B$6), keeping the report clean and readable.
#Output at the end — out of the way
Set wsOut = ThisWorkbook.Worksheets.Add( _
After:=ThisWorkbook.Worksheets( _
ThisWorkbook.Worksheets.Count))
The “Comments” sheet goes at the end of the workbook, not the beginning. This is a review artifact, not the primary workpaper. When you open the file to review the actual schedules (Sched-A, Fixed Assets), the Comments tab sits quietly at the far right where it doesn’t interfere with your workflow.
Contrast with the auto-table-of-contents macro, which places the TOC sheet first because it IS the primary navigation tool. Different purpose, different position.
#The cleanup label always runs
Every On Error GoTo CleanUp in this macro routes to the same label. If the
macro hits an error — a protected sheet blocking access, a corrupted comment
object, anything — the cleanup fires and restores ScreenUpdating and
Calculation. A crashed macro that leaves Excel in manual calculation mode
generates support tickets. The cleanup prevents that.
#Why a message box with counts
Silent macros are dangerous. The MsgBox confirms what happened and how much:
MsgBox "Found " & totalComments & " comment(s) across " & _
sheetCount & " sheet(s)." & vbCrLf & vbCrLf & _
"Report created on '" & OUT_SHEET & "' sheet.", _
vbInformation, "Comment Collector"
If you expected 8 sheets with comments but the macro says 5, you know to check which 3 sheets were skipped. If an earlier preparer left only 3 comments on a 50-tab workbook, the count tells you that without scrolling through the report.
#Adapt It
Get the next macro in your inbox
One copy-paste-ready macro recipe every two weeks. No spam, no VBA theory — just automation that saves you time.
Excel Macro Guy
Excel enthusiast · married to an accountant
I love Excel. My wife is an accountant. Every busy season, I watch her wrestle with workpapers and think "a macro could do that in half a second." So I build them. She tests them on real client data. What survives gets published here.