Searching through VBA accross multiple MDB's

cajulien

New member
Local time
Today, 17:14
Joined
Feb 3, 2020
Messages
25
Hi folks, been trying to figure this out for hours now...too many dead leads, maybe one of you can help.

We've a large number of old MDB files, each with a bunch of modules. We're trying to save all the VBA code across all MDB's modules to a single ACCDB table so we can search through it for strings e.g., references to specific folders or files.

From a new ACCDB I'm crawling a set of folders and their subfolders to find all the *.mdb's
For each mdb I can loop through the modules and get their names, but I can't seem to be able to retrieve their VBA.

To illustrate, assume A.accdb finds the following:
B.mdb: has modules W and X
C.mdb: has modules Y and Z

We're trying to put W, X, Y, and Z's VBA into a long Text field in a table in A.accdb so we can search using Instr(...) across all MDB's and their modules.
We know this won't be efficient text searching but it's certainly more efficient than opening and searching through each MDB one at a time: assume we're talking hundreds of MDB's.

Thanks for your help folks
 
Several possible approaches to include adding references to the MDB files or using SaveAsText to get a bunch of Text files you can combine and search through.
 
You can export all forms, reports, modules, macros to text. Then search the text files.

Here's a procedure you can use. It runs from the UnLoad event of my "first" form. You will need to modify the code to run from an external database. Otherwise, you would need to add the code to every database and then run the export procedure.
Code:
Option Compare Database
Option Explicit
    Public iCountForms      As Integer
    Public iCountReports    As Integer
    Public iCountQueries    As Integer
    Public iCountModules    As Integer
    Public iCountScripts    As Integer    'macros
    Public iCountTables     As Integer
    

Public Sub ExportOnClose()
    Dim strPath             As String
    Dim strFullPath         As String
    Dim objFSO              As Object
    Dim strMsg              As String
    Dim strDBName           As String
    Dim iStartName          As Integer
    Dim iStartExt           As Integer
    Dim iLength             As Integer
    Dim SeqNum              As Integer
    Dim EndLoop             As Boolean

On Error GoTo ErrProc
    
    '''' 'create new folder
    iStartName = InStrRev(CurrentDb.Name, "\") + 1
    iStartExt = InStrRev(CurrentDb.Name, ".")
    iLength = iStartExt - iStartName
    strDBName = Mid(CurrentDb.Name, iStartName, iLength)
    
    strPath = DLookup("NetworkPath", "tblBackupTheseUserNames", "UserName = " & QUOTE & Environ("UserName") & QUOTE) & ""
    If strPath = "" Then        'no back up for logged in user
        Exit Sub
    End If
    
    '''' 'Create path for database
    If Right(strPath, 1) = "\" Then
    Else
        strPath = strPath & "\"
    End If
    strPath = strPath & strDBName
    Debug.Print "strpath = " & strPath
    Set objFSO = CreateObject("Scripting.FileSystemObject")
    If Not objFSO.FolderExists(strPath) Then
        objFSO.CreateFolder (strPath)
    End If
    
    '''' 'create path for database for today --- Add suffix if necessary
    EndLoop = False
    SeqNum = 0
    
    strPath = strPath & "\"
    strFullPath = strPath & strDBName & "_" & Format(Date, "yyyymmdd")
    Debug.Print "strfullpath = " & strFullPath
    Set objFSO = CreateObject("Scripting.FileSystemObject")
    If Not objFSO.FolderExists(strFullPath) Then
        objFSO.CreateFolder (strFullPath)
    Else
        Do Until EndLoop = True
            SeqNum = SeqNum + 1
            strFullPath = strPath & strDBName & "_" & Format(Date, "yyyymmdd") & "_" & Format(SeqNum, "000")
            If Not objFSO.FolderExists(strFullPath) Then
                objFSO.CreateFolder (strFullPath)
                EndLoop = True
                Debug.Print strFullPath
            Else
            End If
        Loop
    End If
    '''' 'export all objects
    MsgBox "This may take a few minutes.  Please wait for the count message.", vbOKOnly
    Call ExportDatabaseObjects("forms", strFullPath, CurrentDb.Name)
    Call ExportDatabaseObjects("reports", strFullPath, CurrentDb.Name)
    Call ExportDatabaseObjects("modules", strFullPath, CurrentDb.Name)
    Call ExportDatabaseObjects("QueryDefs", strFullPath, CurrentDb.Name)
    Call ExportDatabaseObjects("scripts", strFullPath, CurrentDb.Name)
    strMsg = "Exported Forms = " & iCountForms & vbCrLf
    strMsg = strMsg & "         Reports = " & iCountReports & vbCrLf
    strMsg = strMsg & "         Modules = " & iCountModules & vbCrLf
    strMsg = strMsg & "         Queries = " & iCountQueries & vbCrLf
    strMsg = strMsg & "         Macros = " & iCountScripts & vbCrLf
    strMsg = strMsg & "         Tables = " & iCountTables & vbCrLf
    MsgBox "Selected objects have been exported as a text file to " & strPath, vbInformation
    Debug.Print strMsg
    MsgBox strMsg, vbOKOnly
    
ExitProc:
    Exit Sub
ErrProc:
    Select Case Err.Number
        Case Else
            MsgBox Err.Number & "--" & Err.Description
    End Select
    Resume ExitProc
    Resume
    

End Sub

Public Sub ExportDatabaseObjects(ExportType As String, sExportLocation As Variant, strDatabase As Variant)
On Error GoTo Err_ExportDatabaseObjects
    
    Dim db  As DAO.Database
    Dim td  As DAO.TableDef
    Dim d   As Document
    Dim c   As Container
    Dim i   As Integer
    
    If strDatabase & "" = "" Then
        Set db = CurrentDb()
    Else
        Set db = DBEngine.Workspaces(0).OpenDatabase(strDatabase)
    End If

    If Right(sExportLocation, 1) = "\" Then
    Else
        sExportLocation = sExportLocation & "\"
    End If
    Select Case ExportType
        Case "Tables"
            iCountTables = 0
            For Each td In db.TableDefs 'Tables
                If Left(td.Name, 4) <> "MSys" Then
                    DoCmd.TransferText acExportDelim, , td.Name, sExportLocation & "Table_" & td.Name & ".txt", True
                    iCountTables = iCountTables + 1
                End If
            Next td
        Case "Forms"
            Set c = db.Containers("Forms")
            iCountForms = 0
            For Each d In c.Documents
                Application.SaveAsText acForm, d.Name, sExportLocation & "Form_" & d.Name & ".txt"
                iCountForms = iCountForms + 1
            Next d
        Case "Reports"
            Set c = db.Containers("Reports")
            iCountReports = 0
            For Each d In c.Documents
                Application.SaveAsText acReport, d.Name, sExportLocation & "Report_" & d.Name & ".txt"
                iCountReports = iCountReports + 1
            Next d
        Case "Scripts"
            Set c = db.Containers("Scripts")
            iCountScripts = 0
            For Each d In c.Documents
                Application.SaveAsText acMacro, d.Name, sExportLocation & "Macro_" & d.Name & ".txt"
                iCountScripts = iCountScripts + 1
            Next d
        Case "Modules"
            Set c = db.Containers("Modules")
            iCountModules = 0
            For Each d In c.Documents
                Application.SaveAsText acModule, d.Name, sExportLocation & "Module_" & d.Name & ".txt"
                iCountModules = iCountModules + 1
            Next d
        Case "Querydefs"
            iCountQueries = 0
            For i = 0 To db.QueryDefs.Count - 1
                Application.SaveAsText acQuery, db.QueryDefs(i).Name, sExportLocation & "Query_" & db.QueryDefs(i).Name & ".txt"
                iCountQueries = iCountQueries + 1
            Next i
        Case Else
    End Select

    Set db = Nothing
    Set c = Nothing
    
''''    MsgBox "Selected objects have been exported as a text file to " & sExportLocation, vbInformation
    
Exit_ExportDatabaseObjects:
    Exit Sub
    
Err_ExportDatabaseObjects:
    MsgBox Err.Number & " - " & Err.Description
    Resume Exit_ExportDatabaseObjects
 
"modify the code to run from an external database"

That's the problem guys: Application.SaveAsText acModule...is looking for module name in A.accdb, but the module name is found in B.mdb that is opened programmatically from A.

I'd need to be able to do something like the following from A.accdb:

Set db = OpenDatabase("B.mdb") 'not the current db
db.Application.SaveAsText acModule, ModuleNameInB, TargetFilePath

Am I missing something obvious or have you guys ever actually done this from an external database?
 
"modify the code to run from an external database"

That's the problem guys: Application.SaveAsText acModule...is looking for module name in A.accdb, but the module name is found in B.mdb that is opened programmatically from A.

I'd need to be able to do something like the following from A.accdb:

Set db = OpenDatabase("B.mdb") 'not the current db
db.Application.SaveAsText acModule, ModuleNameInB, TargetFilePath

Am I missing something obvious or have you guys ever actually done this from an external database?
You could try using VBS. Check this out.
 
You could try using VBS. Check this out.
Gosh, thanks DBguy, I was hoping for a pure Access VBA solution but I'm starting to think this just isn't possible...
 
Code:
Dim db  As DAO.Database
With db, a DAO reference to the database file is to be created in a visible manner.

For methods on the access object (application) such as DoCmd, SaveAsText, etc., you must also create a reference to the application object of the DB file.
Code:
Dim oAcc As Access.Application
Set oAcc = CreateObject("Access.Application")
With oAcc
    .OpenCurrentDatabase "Z:\...\Second.accdb"

   ' do something

    .CloseCurrentDatabase
End With
oAcc.Quit
Set oAcc = Nothing
 
Oh noooo!!!
.OpenCurrentDatabase actually opens the second.MDB as if a user did, which then triggers all the autostart logic...we definitely DON'T want to do that.

Is there a way to OpenCurrentDatabase with a "Press SHIFT" option so nothing runs on its own?

On the plus side the Module's code did get exported to TXT as intended though...guess that's a forward movement...
 
Last edited:
Am reading 20+ year old posts about this issue...seems there's no way around preventing the autorun from launching on OpenCurrentDatabase except using Windows API to pretend like "user" is pressing SHIFT.


That's so ugly I'll have to think about how badly we need this...thanks for your help folks!
 
Am reading 20+ year old posts about this issue...seems there's no way around preventing the autorun from launching on OpenCurrentDatabase except using Windows API to pretend like "user" is pressing SHIFT.


That's so ugly I'll have to think about how badly we need this...thanks for your help folks!
Besides OpenCurrentDatabase, there's also OpenDatabase, but I'm not sure if it does what you want.
 
@theDBguy Looks like the procedure takes three arguments. I can't figure out what the third one is:

' Usage:
' CScript decompose.vbs <input file> <path> <stubname>

"stubname" does not have any meaning to me. I tried just a string but that didn't work. I also don't see any place in the code where "stubname" is referenced. Do you know what is supposed to be passed here?
 
@theDBguy Looks like the procedure takes three arguments. I can't figure out what the third one is:

' Usage:
' CScript decompose.vbs <input file> <path> <stubname>

"stubname" does not have any meaning to me. I tried just a string but that didn't work. I also don't see any place in the code where "stubname" is referenced. Do you know what is supposed to be passed here?
@Pat Hartman We'll probably have to ask Mike about that. I believe it's a leftover from his earlier/original code on the topic. He may have forgotten to remove it. Just my guess...
 
To have two databases open, you need two objects.

Set ThisDB = CurrentDb()
Set ThatDB = DBEngine.Workspaces(0).OpenDatabase(strDatabase) ''''get strDatabase from form control or prompt.
 
To have two databases open, you need two objects.

Set ThisDB = CurrentDb()
Set ThatDB = DBEngine.Workspaces(0).OpenDatabase(strDatabase) ''''get strDatabase from form control or prompt.
@Pat Hartman : not sure where we are on this one...are you adding something to my original question or is this something different now?
From CurrentDb I'm already opening ThatDb in the default (0) workspace, problem is that I can't access ThatDb.mdb's module's VBA code, and I can't SaveToText without OpenCurrentDatabase
 
@cajulien I think I was replying to theDBGuy.

I agree. I have not been able to get the export to text using a reference to a database other than the one where the code is running. I tried this numerous times in the past and gave it another go today. Just doesn't work. I wouldn't mind using the Script link posted by theDBGuy but I can't get that to work either. The script seems to need three arguments but I can't figure out how the third is used or what value I need to supply.

I use the code I posted earlier that uses the transferToText method to document databases. In my apps, I load a form that checks links and if they are valid, opens the switchboard and hides itself. That means that this form is the LAST form to close when the app is closed. So, it checks to see if I am the logged in user and if I am and if any design changes were made, it asks me if I want to make a backup. This ensures I make multiple backups each day when I am developing but the code doesn't bother the users. I originally built the code because I was teaching a group of Excel analysts how to use Access to reduce their Excel workload since they were importing multi-million row recordsets. I just couldn't get these folks to understand the importance of creating backups so I needed to do something to try to save them from themselves after the dozenth time I had to help them to recover work they had lost. I ended up adding it to all my apps also because it doesn't hurt to be reminded to create backups. All I needed to do was to remember to close the database every few hours and my code would back it up for me. Both full file and the export to text.

I also have a documentation tool and I was hoping to utilize the export to text code but for this use, the code needs to be run from outside the database. This is the exact problem you are fighting with so I thought I'd give it another try with the script. Did you ever get the script to work?
 
Well, the code won't work without it.
I could have sworn it worked for me when I gave it a try before. In the VBS code, there is this section.
Code:
    Dim sStubMDBFilename
    If (WScript.Arguments.Count < 3) then
        sStubMDBFilename = sExportpath & myName & "_stub." & myType
    Else
        sStubMDBFileName = WScript.Arguments(2)
    End If
I believe that's where <stubname> is used.
 
The script seems to need three arguments but I can't figure out how the third is used or what value I need to supply.
Hi @Pat Hartman

I just gave it a try and all I did was pass one argument (the name of the ACCDB file), and it was able to extract all the modules from it.

It created a "stub" file which was basically the same ACCDB file with all the objects removed after the export.
 
@cajulien I think I was replying to theDBGuy.

I agree. I have not been able to get the export to text using a reference to a database other than the one where the code is running. I tried this numerous times in the past and gave it another go today. Just doesn't work.
It certainly does work. Here is one approach which I use regularly...

1. Link to the MSysObjects table in the external database (MSysObjectsEXT) entering the password if necessary

Rich (BB code):
  'link to external MSysObjects table
     DoCmd.TransferDatabase acLink, "Microsoft Access", strFilePath, acTable, "MSysObjects", "MSysObjectsEXT"

2. Grab the connect string from the local MSysObjects table and from that the password where appropriate so you don't need to enter it again when you open the external database

Rich (BB code):
        'get connection string from record in local MSysObjects
GetConnectPwd:
            strConnect = Nz(DLookup("Connect", "MSysObjects", "Database = '" & strFilePath & "'"), "")
                      
             If strConnect <> "" Then
               Set ExtDb = DBEngine.OpenDatabase(strFilePath, False, False, strConnect)
                    If Nz(strPwd, "") = "" Then
                            'get PWD from Connect string 
                       strPwd = Mid(strConnect, InStr(strConnect, "=") + 1)
                       strPwd = Left(strPwd, Len(strPwd) - 1)

                     End If
             Else
                 Set ExtDb = DBEngine.OpenDatabase(strFilePath, False)
                 Me.txtPwd = "No"
             End If

3. Set a reference to the external database

Rich (BB code):
       Set appAcc = New Access.Application

      If strConnect <> "" Then
          appAcc.OpenCurrentDatabase strFilePath, False, strPwd
       Else
          appAcc.OpenCurrentDatabase strFilePath, False
        End If

4. Save any external database object to a text file

Code:
 appAcc.SaveAsText ObjectTypeID, ObjName, strTargetFile

For example:
Code:
 appAcc.SaveAsText acModule, "modVBE", "G:\MyFiles\ExampleDatabases\DatabaseAnalyzerPro\SavedObjects\Module_modVBE.txt"
 

Users who are viewing this thread

Back
Top Bottom