Reputation: 7019
The routine below in Access 2003 produces error 7961 -- My database 'can't find the module.'
But it only does it on some modules. It is consistent on which modules fail.
What's wrong?
Private Sub DoReplace()
Dim obj As AccessObject
For Each obj In CurrentProject.AllModules
Debug.Print ModuleType(obj.Name) & " " & obj.Name
Next obj
End Sub
Public Function ModuleType(ByVal ModuleName As String) As Variant
On Error GoTo errHandler
Dim mdl As Module
Set mdl = Modules(ModuleName)
ModuleType = mdl.Type
ModuleType = Switch(ModuleType = 0, "std ", ModuleType = 1, "class ")
Set mdl = Nothing
errExit: Exit Function
errHandler:
ModuleType = "Err " & Err.Number '7961
Resume errExit
End Function
Debug output:
Err 7961 vba_28_Part_Asterisk class cls_22_mas90_Item2 Err 7961 vba_44_Part_WhereUsed2 Err 7961 cls_22_JobOps_BOM_WhereUsed_method2 Err 7961 vba_26_Part_misc std vba_44_Part_MRP std vba_99_TurnOffSubDataSheets Err 7961 vba_99_MasteringArraysByScott Err 7961 vba_44_Part_WhereUsed Err 7961 cls_22_JobOps_BOM_WhereUsed Err 7961 cls_22_mas90_Item class cls_22_mas90_Order class cls_23_HOMER_Item class cls_44_mrp_record
Upvotes: 1
Views: 4200
Reputation: 23067
I know you've figured out the issue, but you don't appear to have grasped why it works this way, nor come up with the best approach to solve your problem.
In all versions of Access, there are several collections that you use all the time:
and others that aren't used so often:
The first two collections are accessible only as members of a database, so you can use the TableDefs or QueryDefs collection only via something like this:
CurrentDB.TableDefs.Count
This is because TableDefs and QueryDefs are pure Jet objects, rather than Access objects.
The other collections are collections of Access objects, and they include only the OPEN objects, as you can see if you do this:
?Forms.Count
...in the immediate window, you get 0 if there are no forms open, regardless of how many forms there are actually in your database.
Before Access 2000, you had to use the Documents container to get to the list of stored Access objects that weren't loaded. This was rather convoluted, and had to be approached differently for different object types. For modules, here's the code:
Dim db As DAO.Database
Dim cnt As Container
Dim doc As Document
Set db = CurrentDb
Set cnt = db.Containers!Modules
For Each doc In cnt.Documents
Debug.Print doc.Name
Next doc
Set doc = Nothing
Set cnt = Nothing
Set db = Nothing
And you also had to know that Macros were stored in a container called "Scripts." Pretty ugly.
In Access 2000, because of the change to the way the Access project was stored (as a single BLOB field in a single record in a system table, instead of being stored in multiple records, one per object), the CurrentProject.All**** collections were introduced. These were:
For your purposes, the best choice is clearly the AllModules collection instead of the Modules collection because that way you don't have to worry whether the modules are open or not. Of course, the Containers/Documents approach works, but AllModules requires significally less code.
EDIT:
The code for using AllModules is:
Dim i As Integer
For i = 0 To CurrentProject.AllModules.Count - 1
Debug.Print CurrentProject.AllModules(i).name
Next i
OR:
Dim obj As Object
For Each obj In CurrentProject.AllModules
Debug.Print obj.name
Next obj
Set obj = Nothing
I always prefer using strongly typed objects for my FOR loops, but only a generic object variable works here, so I would probably use the counter since it saves having to clean up the last implicit object pointer at the end.
Also, keep in mind that the Modules collection (i.e., open modules) includes form modules as well as standalone and class modules, while AllModules is limited to standalone and class modules.
Upvotes: 6
Reputation: 7019
The solution is to pass the AccessObject (instead of a string). So from this:
ModuleType(ByVal ModuleName As String) As Variant
... to this:
ModuleType(ByVal obj As AccessObject) As Variant
David's post helped me to re-examine the code and also provided good general knowledge, and I'm up-voting his answer accordingly. My error, however, was about objects and functions rather than how to use the All collections.
Upvotes: 1
Reputation: 7019
Set mdl = Modules(obj.Name)
To do the above, the module object has to be loaded. This is checked using
obj.IsLoaded
(obj is an AccessObject, mdl is a Module -- refer above.)
My problem was that only some modules were loaded. Loading a module can be accomplished by opening it in the VBA IDE. No more Error 7961.
I am very surprised how difficult it was to get to this answer. I had to hammer on Google and apply some grit of my own. Hope it will help somebody else.
Upvotes: 1