If you’re like me, you might have hundreds, or even thousands of databases and finding VBA code, know what is where might be a challenge to put things lightly.
In the past I provided a procedure to get some information about modules and procedures:
I thought I’d share a slightly modified version . The reason this one can be useful is because you can actually use it to pull information for external databases, not just the current database. I use this type of approach to create an inventory of modules/functions making it much easier to locate things in amongst all my databases, workbooks & documents.
It extracts a list of modules with your VBE projects and further enumerate the various procedures within each module providing information about the name, type, no lines of code, …
The Code
So the code below will generate a listing of summary statistics about every modules (form, report, standard, class) within the database.
'---------------------------------------------------------------------------------------
' Procedure : VBE_GetModuleFunctionInfo
' Author : Daniel Pineault, CARDA Consultants Inc.
' Website : http://www.cardaconsultants.com
' Purpose : Enumerate summary information about the Access Database modules and functions
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
' (CC BY-SA 4.0) - https://creativecommons.org/licenses/by-sa/4.0/
' Req'd Refs: Late Binding -> none required
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' oAccess : Access object, for the local database simply use 'Application'
'
' Usage:
' ~~~~~~
' VBE_GetModuleFunctionInfo Application
' Returns -> Result output to the VBE Immediate Window
'
' Revision History:
' Rev Date(yyyy-mm-dd) Description
' **************************************************************************************
' 1 2013-06-06
' 2 2023-04-24 Updated Error Handling and CopyRight
'---------------------------------------------------------------------------------------
Sub VBE_GetModuleFunctionInfo(oAccess As Access.Application)
Dim oVBC As Object 'VBComponent
Dim sModuleName As String
Dim sPreviousProcedure As String
Dim sCurrentProcedure As String
Dim sProcedureDeclaration As String
Dim sProcedureType As String
Dim lModuleLineNo As Long
Dim lProcedureNoLines As Long
Dim lProcedureStartLineNo As Long
Dim lPositionComment As Long
Dim lPositionParenthesis As Long
Dim lPk As Long 'vbext_ProcKind
Dim lCounter As Long
Const vbext_pk_Proc As Long = 0
Const vbext_pk_Let As Long = 1
Const vbext_pk_Set As Long = 2
Const vbext_pk_Get As Long = 3
On Error GoTo Error_Handler
'Header for the output in the Immediate Window
Debug.Print "Module", _
"Proc Name", _
"Proc Type", _
"Proc Declaration", _
"Proc No Lines", _
"Proc Start", _
"Proc Act. Start"
Debug.Print String(115, "~")
'Iterate over each module
For lCounter = 1 To oAccess.VBE.VBProjects(1).VBComponents.Count
Set oVBC = oAccess.VBE.VBProjects(1).VBComponents.Item(lCounter)
lModuleLineNo = 1 'Reset the variable for each iteration
sPreviousProcedure = "" 'Reset the variable for each iteration
'Name of the module
sModuleName = oVBC.Name
'Output Module Level Information
Debug.Print sModuleName;
On Error Resume Next 'Some module throw an error with the following line?!
Debug.Print , Switch(Modules(oVBC.CodeModule).Type = acStandardModule, "Standard Module", Modules(oVBC.CodeModule).Type = acClassModule, "Class Module");
If Err.Number <> 0 Then Debug.Print , "Object Module";
Err.Clear
On Error GoTo Error_Handler
Debug.Print , oVBC.CodeModule.CountOfLines
Debug.Print String(50, "-")
'Iterate over each line in the module
For lModuleLineNo = 1 To oVBC.CodeModule.CountOfLines
'Name of the procedure
sCurrentProcedure = oVBC.CodeModule.ProcOfLine(lModuleLineNo, lPk)
'Declaration of the procedure
sProcedureDeclaration = oVBC.CodeModule.Lines(lModuleLineNo, 1)
'Position of the 1st comment character
lPositionComment = InStr(1, sProcedureDeclaration, "'")
'Adjust the sProcedureDeclaration value accordingly to remove the comment portion
If lPositionComment > 0 Then sProcedureDeclaration = Left(sProcedureDeclaration, lPositionComment - 1)
'Position of the 1st parenthesis '(' character
lPositionParenthesis = InStr(1, sProcedureDeclaration, "(")
'Adjust the sProcedureDeclaration value accordingly to remove the argument portion
If lPositionParenthesis > 0 Then sProcedureDeclaration = Left(sProcedureDeclaration, lPositionParenthesis - 1)
'Remove leading/trailing spaces
sProcedureDeclaration = Trim(sProcedureDeclaration)
'Find the 1st line that isn't blank and for which the procedure name has changed (start of the procedure at hand)
If sProcedureDeclaration <> "" And sPreviousProcedure <> sCurrentProcedure Then
'Total no. of lines in the procedure 'section'
lProcedureNoLines = oVBC.CodeModule.ProcCountLines(sCurrentProcedure, lPk)
'Line no. the procedure starts on
lProcedureStartLineNo = oVBC.CodeModule.ProcStartLine(sCurrentProcedure, lPk)
'Extract just the Declaration from the full line
sProcedureDeclaration = Trim(Left(sProcedureDeclaration, Len(sProcedureDeclaration) - Len(sCurrentProcedure)))
'Determine the Type of Procedure
Select Case lPk
Case vbext_pk_Proc
'sProcedureType = "Procedure"
sProcedureType = Switch(InStr(1, sProcedureDeclaration, "Function") > 0, "Function", _
InStr(1, sProcedureDeclaration, "Sub") > 0, "Sub")
Case vbext_pk_Let
sProcedureType = "Property Let"
Case vbext_pk_Set
sProcedureType = "Property Set"
Case vbext_pk_Get
sProcedureType = "Property Get"
Case Else
sProcedureType = "Unknown"
End Select
'Output Procedure Level Information
Debug.Print sModuleName, _
sCurrentProcedure, _
sProcedureType, _
sProcedureDeclaration, _
lProcedureNoLines, _
lProcedureStartLineNo, _
lModuleLineNo
sPreviousProcedure = sCurrentProcedure
End If
Next
Debug.Print
Next
Error_Handler_Exit:
On Error Resume Next
Set oVBC = Nothing
If oAccess.CurrentDb.Name <> Application.CurrentDb.Name Then oAccess.Quit
Exit Sub
Error_Handler:
MsgBox "The following error has occurred" & vbCrLf & vbCrLf & _
"Error Source: VBE_GetModuleFunctionInfo" & vbCrLf & _
"Error Number: " & Err.Number & vbCrLf & _
"Error Description: " & Err.Description & _
Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
, vbOKOnly + vbCritical, "An Error has Occurred!"
Resume Error_Handler_Exit
End Sub
Usage Example
To pull a listing you can simple run it by doing:
VBE_GetModuleFunctionInfo Application
which will output a listing like the following to the VBE Immediate window:
Module Proc Name Proc Type Proc Declaration Proc No Lines Proc Start Proc Act. Start ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Module1 Standard Module 170 -------------------------------------------------- Module1 Db_GetApp Function Public Function 36 3 5 Module1 Db_GetApp2 Function Public Function 47 39 40 Module1 VBE_GetModuleInfo Function Public Function 35 86 90 Module1 VBE_GetModuleFunctionInfo2 Function Public Function 50 121 123 clsWB Class Module 19 -------------------------------------------------- clsWB oClsWBButton_OnClick Function Private Function 8 6 8 clsWB Init Sub Public Sub 6 14 16 Form_frm_Db_ProgressBar Object Module 74 -------------------------------------------------- Form_frm_Db_ProgressBar Detail_Click Sub Private Sub 4 42 43 Form_frm_Db_ProgressBar Form_Load Sub Private Sub 29 46 47
So it enumerates the modules indicating the type of module and total number of lines of code and then it breaks down and provides a summary of each procedure within each module providing:
- Procedure’s Name
- Procedure Type
- Procedure’s Declaration
- Total number of lines (can include blank lines, header, …)
- Line number that the VBE considers the start of the procedure (can include blank lines, header, …)
- Actual line number the procedure’s declaration starts at
You can also use something like OpenCurrentDatabase to create an instance to an external db and pass that instance to the procedure to get information about any external database you want.
Room For Further Options
Obviously, this can be further extended. If you have a standard header … then you could extract the version, arguments, … the sky is the limit! You could also push this to a table and create an inventory of all your database modules/procedures and create a lookup form to easily locate code when you need it! (hint hint, that’s what I did)
Extend To Other Applications? Why Of Course!
Furthermore, this code can easily be adapted to work in other applications rather than just Microsoft Access (what it was originally designed for), it is just a question of changing it ever so slightly. Here’s a modified version for use in Excel for instance:
'---------------------------------------------------------------------------------------
' Procedure : VBE_XLS_GetModuleFunctionInfo
' Author : Daniel Pineault, CARDA Consultants Inc.
' Website : http://www.cardaconsultants.com
' Purpose : Enumerate summary information about the Excel Workbook modules and functions
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
' (CC BY-SA 4.0) - https://creativecommons.org/licenses/by-sa/4.0/
' Req'd Refs: Late Binding -> none required
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' oApp : Access object, for the local database simply use 'Application'
'
' Usage:
' ~~~~~~
' VBE_XLS_GetModuleFunctionInfo ThisWorkbook
' Returns -> Result output to the VBE Immediate Window
'
' Revision History:
' Rev Date(yyyy-mm-dd) Description
' **************************************************************************************
' 1 2023-04-24 Updated Error Handling and CopyRight
'---------------------------------------------------------------------------------------
Sub VBE_XLS_GetModuleFunctionInfo(oApp As Object)
Dim oVBC As Object 'VBComponent
Dim sModuleName As String
Dim sPreviousProcedure As String
Dim sCurrentProcedure As String
Dim sProcedureDeclaration As String
Dim sProcedureType As String
Dim lModuleLineNo As Long
Dim lProcedureNoLines As Long
Dim lProcedureStartLineNo As Long
Dim lPositionComment As Long
Dim lPositionParenthesis As Long
Dim lPk As Long 'vbext_ProcKind
Dim lCounter As Long
Const vbext_pk_Proc As Long = 0
Const vbext_pk_Let As Long = 1
Const vbext_pk_Set As Long = 2
Const vbext_pk_Get As Long = 3
On Error GoTo Error_Handler
'Header for the output in the Immediate Window
Debug.Print "Module", _
"Proc Name", _
"Proc Type", _
"Proc Declaration", _
"Proc No Lines", _
"Proc Start", _
"Proc Act. Start"
Debug.Print String(115, "~")
'Iterate over each module
For lCounter = 1 To oApp.VBProject.VBComponents.Count
Set oVBC = oApp.VBProject.VBComponents.Item(lCounter)
lModuleLineNo = 1 'Reset the variable for each iteration
sPreviousProcedure = "" 'Reset the variable for each iteration
'Name of the module
sModuleName = oVBC.Name
'Output Module Level Information
Debug.Print sModuleName;
'Doesn't work in Excel! No such enum
' On Error Resume Next 'Some module throw an error with the following line?!
' Debug.Print , Switch(Modules(oVBC.CodeModule).Type = acStandardModule, "Standard Module", Modules(oVBC.CodeModule).Type = acClassModule, "Class Module");
' If Err.Number <> 0 Then Debug.Print , "Object Module";
' Err.Clear
' On Error GoTo Error_Handler
Debug.Print , oVBC.CodeModule.CountOfLines
Debug.Print String(50, "-")
'Iterate over each line in the module
For lModuleLineNo = 1 To oVBC.CodeModule.CountOfLines
'Name of the procedure
sCurrentProcedure = oVBC.CodeModule.ProcOfLine(lModuleLineNo, lPk)
'Declaration of the procedure
sProcedureDeclaration = oVBC.CodeModule.Lines(lModuleLineNo, 1)
'Position of the 1st comment character
lPositionComment = InStr(1, sProcedureDeclaration, "'")
'Adjust the sProcedureDeclaration value accordingly to remove the comment portion
If lPositionComment > 0 Then sProcedureDeclaration = Left(sProcedureDeclaration, lPositionComment - 1)
'Position of the 1st parenthesis '(' character
lPositionParenthesis = InStr(1, sProcedureDeclaration, "(")
'Adjust the sProcedureDeclaration value accordingly to remove the argument portion
If lPositionParenthesis > 0 Then sProcedureDeclaration = Left(sProcedureDeclaration, lPositionParenthesis - 1)
'Remove leading/trailing spaces
sProcedureDeclaration = Trim(sProcedureDeclaration)
'Find the 1st line that isn't blank and for which the procedure name has changed (start of the procedure at hand)
If sProcedureDeclaration <> "" And sPreviousProcedure <> sCurrentProcedure Then
'Total no. of lines in the procedure 'section'
lProcedureNoLines = oVBC.CodeModule.ProcCountLines(sCurrentProcedure, lPk)
'Line no. the procedure starts on
lProcedureStartLineNo = oVBC.CodeModule.ProcStartLine(sCurrentProcedure, lPk)
'Extract just the Declaration from the full line
sProcedureDeclaration = Trim(Left(sProcedureDeclaration, Len(sProcedureDeclaration) - Len(sCurrentProcedure)))
'Determine the Type of Procedure
Select Case lPk
Case vbext_pk_Proc
'sProcedureType = "Procedure"
sProcedureType = Switch(InStr(1, sProcedureDeclaration, "Function") > 0, "Function", _
InStr(1, sProcedureDeclaration, "Sub") > 0, "Sub")
Case vbext_pk_Let
sProcedureType = "Property Let"
Case vbext_pk_Set
sProcedureType = "Property Set"
Case vbext_pk_Get
sProcedureType = "Property Get"
Case Else
sProcedureType = "Unknown"
End Select
'Output Procedure Level Information
Debug.Print sModuleName, _
sCurrentProcedure, _
sProcedureType, _
sProcedureDeclaration, _
lProcedureNoLines, _
lProcedureStartLineNo, _
lModuleLineNo
sPreviousProcedure = sCurrentProcedure
End If
Next
Debug.Print
Next
Error_Handler_Exit:
On Error Resume Next
Set oVBC = Nothing
Exit Sub
Error_Handler:
MsgBox "The following error has occurred" & vbCrLf & vbCrLf & _
"Error Source: VBE_XLS_GetModuleFunctionInfo" & vbCrLf & _
"Error Number: " & Err.Number & vbCrLf & _
"Error Description: " & Err.Description & _
Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
, vbOKOnly + vbCritical, "An Error has Occurred!"
Resume Error_Handler_Exit
End Sub