In Microsoft Access, what are the relationships between CodeData, CodeProject, and DAO..? - ms-access

What are the differences between the following sets of Microsoft Access VBA collections..? Some of them seem to point to the same data & child objects, but through different conduits. I'm thinking the DAO objects are DBMS-specific, but Access.⃰ Code.⃰ and Access.⃰ Project.⃰ are not, and get connected through Access. There is ADODB, which is another beast entirely. Also, it seems some of the collections may not exist if there no child objects of that type.
Given the mentions of SQL features, there seem to be three scenarios to be concerned with, but it's difficult to grasp:
A standalone ACCDB file.
A split database with two or more ACCDB
files.
An Access frontend to an SQL server.
Object set #1:
Access.CodeData.AllTables
Access.CodeData.AllQueries
Access.CodeData.AllFunctions
Access.CodeData.AllDatabaseDiagrams
Access.CodeData.AllStoredProcedures
Access.CodeData.AllViews
Access.CodeProject.AllForms
Access.CodeProject.AllMacros
Access.CodeProject.AllModules
Access.CodeProject.AllReports
And of course CodeData vs. CurrentData and CodeProject vs. CurrentProject.
Object set #2:
Access.Application.CodeData.*
Access.Application.CodeProject.*
Object set #3:
Access.Application.CurrentDb.QueryDefs (DAO.QueryDefs)
Access.Application.CurrentDb.Relations (DAO.Relations)
Access.Application.CurrentDb.TableDefs (DAO.TableDefs)

The biggest difference is in the type of database you are working with. A standard Microsoft Access database (*.accdb) will primarily use the CurrentDB/CodeDB object to reference the database objects.
An ADP Project, on the other hand, is kind of a hybrid with some data on the SQL server (tables, queries, etc...) accessed through CurrentData/CodeData and other objects like forms and reports residing in the *.adp file and accessed through CurrentProject/CodeProject.
The difference between CurrentDB and CodeDB comes into play when you have multiple database files involved, such as other Access databases referenced as library databases. Those library databases might have functions to interact with the parent application, and others that reference the library.

As you should already know (since I told you here), you can store code in external databases, and refer to that code from another database.
The code in this external database might rely on certain tables, queries and forms to be present. But since it's ran on another database, they might not be.
That's where CodeDb, CodeProject and CodeData come in. They're fully equivalent to the variants starting with Current, only refer to the database where the code is stored, instead of the database which is currently open.
Differences between CurrentDb, CurrentProject and CurrentData is really asking for differences between apples, pears and oranges. They're all objects, but fulfill different functions. As ThunderFrame noted, CurrentDb is mostly DAO, and CurrentProject contains some ADO functionality (mostly just the connection), but also functions to influence the VBA part of the database, the database path, and many other functions. And CurrentData is something else entirely.

In Access, the CodeDB and CurrentDb functions return DAO.Database objects.
CodeDb is the database or add-in, within which the code is running (which might not be the same as the CurrentDb.
CurrentDb is the currently active database (not an add-in).
Access 2002/03 added ADODB, and so they added CurrentProject and CodeProject functions for returning the ADO equivalents of CurrentDb and CodeDb, but being ADO, they have very different methods and properties.

Related

Can I create "linked" access objects (queries, macros etc.) (similar to "linked Tables")?

I am aware of MS Access - link to query in another Access database from a number to years back. My need is not so simple.
We have a number of MS Access ACCDBs which use linked tables to perform certain activities on linked tables (in both SQL Server DBs and local MDBs/ACCDBs). They consist of a very large number of queries and macros etc.
Each ACCDB references the main database using the linked tables and we have a small number of such databases with different content but identical structure. In the past, we have used some VBA code to re-link the main database. But the content is now significantly different in the main databases that we'd like to create a separate ACCDB for each content type. Naturally, because they are dealing with identical structures, a significant (>75%) portion of the objects (queries, macros, tables etc.) are notionally identical. Is there any way to create a "container" for the common stuff - similar to an ACCDE to hold the common stuff and reduce the maintenance burden as the common stuff "evolves" in one ACCDB (and needs to be distributed to the other ACCDB)?
We use the full range of Access objects - including all "action" query types, macros, linked tables etc.
The referenced link only allows selection queries. It doesn't seem as though "action" queries and macros can use the same kind of syntax to access the items in the external DB.
TIA,
Paolo
[Edit: To be clear, I need to run the stuff in the "library" DB in the context of the main ACCDB. Thus if I have a query "Select * from myTable" stored in the Library DB when I run it, it needs to reference the myTable in the ACCDB not the one in the library (if I even need one there)]
You can add a reference to a "library" database, and that will give you direct access to the code in that database.
To access queries, you can open the external database, but queries will run in their external context, not in the current (local) database. However, the tables you have linked in the current database can be linked as well in the external database. This way it doesn't matter where the query runs.

Access 2010: linked database, reference, or add-in?

Looked around and found a variety of answers, but nothing recent that really compares these options pro and con. So I thought I'd ask the community to weigh in on which route you prefer and why.
Background
This is what we have:
Common set of Access modules & classes used in numerous protocol databases (Access 2010 *.accdb split front/back-ends)
Front-ends link to back-end database tables & code (linked dbs)
Back-ends contain protocol specific data & code
Common module/class database shouldn't be directly edited by users
Knowns
Add-ins & db reference databases:
Require re-distribution each time they are changed (even if no code is changed within them).
Must be edited within their IDE vs. the IDE of the protocol database (or you'll lose your edits since that db isn't the common code's)
Questions
How should the common module/class database be connected to the protocol databases?
linked database just like back-ends are
attach it as a reference in the IDE (Tools > Reference)
create an add-in and add it as a reference
How would you do it and why?
What are the pros/cons?
Which option would maximize performance?
I have several variations of the following code lying around, I think this one is most applicable to your situation (it pulls modules and forms from a database, overwriting existing ones, as soon as the database is started).
Public Sub ImportModules()
Dim ImportDbLocation As String: ImportDbLocation = CurrentProject.path & "\ModuleDb.accdb"
Dim ObjectsToImport As Recordset
Set ObjectsToImport = CurrentDb.OpenRecordset("SELECT * FROM Objects IN """ & ImportDbLocation & """")
Do While Not ObjectsToImport.EOF
On Error Resume Next
DoCmd.DeleteObject ObjectsToImport!ObjectType, ObjectsToImport!ObjectName
On Error GoTo 0
DoCmd.TransferDatabase acImport, "Microsoft Access", ImportDbLocation, ObjectsToImport!ObjectType, ObjectsToImport!ObjectName, ObjectsToImport!ObjectName
ObjectsToImport.MoveNext
Loop
End Sub
This code is triggered from the AutoExec macro in the front-end databases.
The database referred to as ImportDbLocation is the database containing all modules and forms I want to import. It contains a single table named Objects. This table has two columns, one named ObjectName containing the names of all objects that should be pulled, and one named ObjectType, which is a lookup field that corresponds with the acObjectType enum (some irrelevant objects removed).
Advantages:
You get a fresh copy of all modules in the database, making sure any overwrites are irrelevant.
You can add a third column to the Objects table to filter out objects for a specific database, thus selectively pushing some objects to some front-ends, and others to others (and a fourth one to specify the name of the objects in the ModulesDb file, to use multiple variants of the same form for different front-ends).
You have all code in the front-end, so no weirdness with external files and references
You can also use this code to pull any other type of objects you want (in my case mainly queries and forms)
Disadvantages:
Load time increases (normally by a tiny bit, but dependent on how many objects are imported, it might be long)
Modules are visible and readable for end-users (technically also modifiable, but any changes are undone on database load)
You can't modify the module doing the import this way
You NEED proper security settings, else users will get spammed with security popups on every database open
I also have a variant lying around that asynchronously pushes the modules from a separate thread to the database when opened, and I have another implementation that allows me to selectively move modules to front-ends after doing modifications.
I can share them if needed, but haven't yet implemented the asynchronous one in a production environment (still a work in progress, it should save on load time and can push all modules)
First a big "thank you" to #Erik and #Gustav for your inputs.
Solution
The solution opted for in my use case was to use a common reference library database that houses modules and classes.
While I considered #Erik's solution, in the end copying over the modules and classes into each protocol database was a bit too much like sending out copies of them which could then devolve from the master modules/classes. This in part was one reason to shift to the single reference database - to avoid propagation into the other databases so there would be a single code source for them.
Caveat: If there are performance issues with the reference library solution, classes & modules may have to be pulled from the common library database ala #Erik's solution.
Database as Reference Library
The common modules and classes are contained within a database (*.accdb). Whenever the code is desired in another database, a reference is added like referencing any other code library (VB IDE - Tools > References). The only difference is that you Browse... to the library and make sure you select Microsoft Access Databases (*.accdb) in the search filter.
If the reference library database is kept in a common location, there shouldn't be issues with re-referencing, although re-connecting the reference is easily done (same as connecting in the first place).
I've also separated out common version control and development modules into similar separate databases that can be referenced when desired during development and de-referenced when a given database goes to production.
Development "Gotchas"
Actually these are more considerations/things to remember than true "gotchas":
- Edit library classes & modules in the library database
If you edit a class or module from the database that references the library database you will lose the edits as soon as you close the database you were working in.
You can try out edits from the database you're working in - the code will run, but it doesn't save.
I often have Notepad++ or OneNote open to copy & paste over code I'm testing to the library database. That way I can continue working on other areas and have a set of changes to update the library database.
- Compile the library database before using its components in the referencing database
This is more a workflow issue - make sure you save & compile in the library database, then open your referencing database after you've completed your changes and closed the library database first.
Resources
Here are some links which proved helpful:
Using a centralized vba module in multiple access databases
Using VBA Code Libraries in Access Database Applications
Referencing VBA Projects as Libraries
Demonstration Applications & VBA Code Libraries
Classes in VBA
Using Database Library Files in Your Access Application
Object Oriented VBA: Design Patterns: Simple Factory
Object Oriented VBA: Static Classes
The 6th reference (available only via the internet archive) has been particularly helpful in sorting out how to properly instantiate classes. In the end I opted to create a single Factory class (static class) with multiple functions (one per class - e.g. NewClassABC()) that instantiates the class within the common reference library database.
So far, so good.
The reference library database has the code in one place and other protocol databases can be updated to it when desired. No dealing with multiple copies and versions of the same module or class.
Hope this helps others looking for a "common" library type solution.

Documenting a Database (Specifically Microsoft Access 2003)

Coming from the world of MySQL, Postgres, MongoDB, Oracle, etc... I've just been given an MS-Access 2003 database with an application inside it; I need to document it.
I'm not worried about the Tables and relationships -- all standard stuff (except for the linked tables to go to outside data sources via ODBC); I'm noting Queries, Macros, Forms, Modules, Reports, and Pages. I'm not necessarily concerned about the data, and although the schema would be nice, it's all the other Microsoft "extras" that MS-Access provides.
At the moment my strategy is to manually build a list of each item and copy'n'paste each query and chunk of VB Code. (ugh) That's
Then it struck me, perhaps there's an MS-Access wizard who knows a faster way to get MS-Access to dump its guts in a more human readable format. At the moment, I can't even select more than one item, nor paste it into notepad.
See this SO post with a customized VBA routine to export all Access objects as raw texts.
It uses the VBA method Application.SaveAsText (an undocumented option).
Conversely, you can use Application.LoadFromText to re-create objects from text file exported using SaveAsText.

Communication between two MS Access application?

I am looking for a solution to effectively communicate between two running MS Access applications.
The approaches I tried so far is to use a common linked table and to use MSMQ service for communication. Both approaches work, but there is no way to "push" the data or command from one application to another and since MS Access doesn't support multi-threaded execution of VBA code, it is very difficult to implement polling without performance disadvantages.
Same time, VBA does support the addressof operator (from version 2000) that means we can also theoretically implement call-back functions in VBA and MS Access. But I have never seen any example how this can be used for inter-process communication and would appreciate any minimal example how I can send a string from one MS Access application to another without monitoring a shared table all the time.
You can use GetObject() to return the Access.Application object from another running db. With the application object you have access to just about everything you might need. Here's a contrived example of opening a form (but you can do a myriad of other things with the Application object):
Sub TestInterop()
Const mdbPath As String = "C:\OtherApp.mdb"
Dim OtherApp As Access.Application
Set OtherApp = GetObject(mdbPath)
OtherApp.Visible = True
OtherApp.DoCmd.OpenForm "Accounts"
End Sub
If the program is not already running, the GetObject() call will start the application (you would need to be careful if you have multiple versions of Access installed as it's difficult to know at runtime which version would actually open the .mdb). However, if GetObject() needs to start the app, it will do so with the visibility set to False, so we explicitly set it to True. If the app is already running, setting its Visibility to True will have no effect.
Consider it a wild idea, but may be put all your tables into sql express and/or sql ce and make look like a frontend to those tables?

Calling function in one MDB from another MDB

We have developed a consolidation function that will be used by other processes and want to position the function in its own MDB (call it "remote") so that it can be referenced and called from "caller.mdb" when its needed. The function is designed to return an array and works great when executed called directly from within "remote." However, with "remote" properly referenced in the "caller" VBA project, when "caller" makes the call the function returns errors. We get a variety of errors such as
3078: Jet cannot find the input table or query
QUESTION. Within "remote", how does one properly set references to the db and its local objects (e.g. one table and several queries including INSERT and UPDATE queries)? CurrentDB is apparently not the answer; we have also experimented with the AccessObject and CodeData objects. "Remote" and "caller" currently reside on the same drive, so that wouldn't seem to be the problem.
Instead of CurrentDb you could use with CodeDb wich points to the mdb currently executing the code.
Set db = CodeDb
The way Access itself does this (with all the wizards, which are all programmed in Access), is to use Application.Run. It does mean the code you're calling has to be a function, though it doesn't matter what it returns. Application.Run requires no references, just a path:
Application.Run("MyCodeDatabase.MyFunction()")
Obviously, if the code database is not in the path that Access uses (which includes its own app folders (including the app-specific folders in the user's profile) and the folder where your main application front end is stored), you'll need to specify the full path.
Application.Run() is a function that returns a value, but it is typed as variant. This may or may not work with your array. It's not clear from the object browser whether or not the arguments are passed ByVal or ByRef, but if they are ByRef (which is what I'd expect), you might just pass the array in and let the function work on it and then use it after the code in the remote database has completed.
On the other hand, the arguments are probably variants, so there's not much difference between that approach and just using the structure returned by Application.Run().
Marcand gave you the answer to your immediate question. There are other problems and irritations when it comes to using add-ins or referenced Access databases. See my Add-in Tips, Hints and Gotchas page.
There are a number of differences and nuances to calling forms and functions through a reference in a another MDB or ADP. I have run into issues in both situations, and what you are referring to as the "remote" database, I refer to as a central library.
At my Tips and Tricks page at http://www.mooresw.com/tips.php, I have pages devoted to programatically changing references, getting Access to search for the referenced file instead of having a broken reference, and calling forms through a reference.
Programatically changing references is needed when you publish the database from the development environment to the user or production environment. When working in the development folder, it's fine for the program to have a reference to the central library directly, but we wouldn't want code that 20 users are running tying up the central library in our development area. (An MDB file opened through a reference gets locked just as though your users were opening it directly)
The situation of running a form in a central library (or "remote" database) where there are no links or tables can be tricky. In that situation I've chosen to open a connection to the "caller.mdb" using ADO code with a Jet connection string in the open event of the forms. Doing so provides the ability for the code in the form (or functions in the library) to gain access to the tables and queries in the calling mdb.
For further information, see my pages at the tips link above, and in particular, see:
http://www.mooresw.com/call_a_form_in_another_MDB_through_a_reference.php
which I believe is most relevant to your situation.