We have a large number of access databases that contain a number of common functions. We need to make a small change to one of the functions, and are looking for a way to automate this via scripting, etc. Is there any way to get at the VBA code inside an Access database, and make a change to it?
I have used ADOX before from PS or VBS to be able to list tables, views and queries, but it does not expose the VBA code in its API.
~bp
If the code changes are always in a module with a consistent name then you can simply remove the existing module and reimport the new version in all the projects you need to apply the change.
http://www.cpearson.com/excel/VBE.aspx
The above link posts information on how to import modules into a VBA project. So simple case of looping through your multiple databases and removing the module and importing the latest version.
If the code changes are located in different module names, you can open each database and loop through the modules to search for the string. The functions you'll need are listed here (depending on the style of change, it'll most likely be some combination of Find and ReplaceLine)
http://msdn.microsoft.com/en-us/library/office/aa223124%28v=office.11%29.aspx
Related
I have reviewed the previous Questions and haven't found the answer to the following question,
Is there a Database Tool available in MS Access to run and identify the Queries that are NOT Bring used as a part of my database. We have lots of Queries that are no longer used and I need to clean the database and get rid of these Queries.
Access does have a built in “dependency” feature. The result is a VERY nice tree-view of those dependencies, and you can even launch such objects using that treeview of your application to “navigate” the application so to speak.
The option is found under database tools and is appropriately called Object Dependencies.
The result looks like this:
While you don't want to use auto correct, this feature will force on track changes. If this is a large application, then on first run a significant delay will occur. After that, the results can be viewed instantly. So, most developers still turn off track name autocorrect (often referred to track auto destroy). However, the track auto correct is required for this feature.
And, unfortunately, you have to go query by query, but at least it will display dependences for each query - (forms, or reports). However, VBA code that creates SQL on the fly and uses such queries? Well, it will not catch that case. So, at the end of the day, deleting a query may well still be used in code, and if that code creates SQL on the fly (as at LOT of VBA code does, then you can never really be sure that the query is not not used some place in the application.
So, the dependency checker can easy determine if another query, another form/sub form, or report uses that query. So dependency checker does a rather nice job.
However, VBA code is a different matter, and how VBA code runs and does things cannot be determined until such time code is actually run. In effect, a dependency checker would have to actually run the VBA code, and even then, sometimes code will make several choices as to which query to run, or use - and that is determined by code. I suppose that you could do a quick "search", since a search is global for VBA (all code in modules, reports and forms can be searched). This would find most uses of the query, but not in all cases since as noted VBA code often can and does create sql on the fly.
I have a vague recollection part of Access Analyzer from FMS Inc has this functionality built in.
Failing that, I can see 2 options that may work.
Firstly, you could use the inbuilt Database Documenter. This creates a report that you can export to Excel. You would then need to import this into the database, and write some code that loops the queries to see if they appear in this table;
Alternatively, you could use the undocumented "SaveAsText" feature to loop all Forms/Reports/Macros/Modules in your database, as well as looping the Querydefs and saving their SQL into a text file. You would then write some VBA to loop the queries, open each of the text files and check for the existence of the query.
Either way, rather than just deleting any unused queries, rename then to something like "old_Query", and leave them for a month or so in the database just in case!!
Regards,
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.
I have a rather odd thing happening to me when I import a table into access.
I have tried doing this in three ways...
Firstly, I import the table (from a text file) using the built in functionality and having defined (and saved) the Import Specification everything works tickety-boo.
Next I re-run the upload by creating a macro but with reference to the Import Specification file that I saved above.
The problem is, some of the Import Specifications are not holding when I do it this way, in particular, I have requested that one field has indexing (No Duplicates), however when it loads with the macro it sets the fields indexing back to the default position back to "No"
I also tried to load the table using VBA (with reference to the same Import Specification File), but encounter the same problem.
Does anyone know how to maintain the integrity of the indexing when doing automated loads based of spec files?
One of the easiest way to control upload(if you have to change upload scenario dynamically) is to use custom VBA code to create and fill table with date from the source. The reason for that, in my opinion, the specifications and export/import system in Access is working, but weak and have pretty low level tools to use it via VBA, because it was mainly created for hand import/export with Access interface.
PS by custom VBA code, i mean the code that absolutely avoid built-in import features. Again this only make sense if your import/export scenario is dynamic and need to make choices in a process. If it's static and can be created using standard features, then just do it by hand with a step by step wizard and then call it with your VBA code without any changes.
I wrote a function in visual basic which I can use in any query or form of given access database by adding the module. What I want is to able to use the function in all the databases in my machine.
A simple solution is to copy the module everytime to the database, but is there any simple solution?
In excel you can do this by adding the function to a .xla file and then copying the file to XLSTART folder. But MSACCESS doesn't have any such equivalent folder.
Can someone suggest something?
Here is a complete tutorial: Using a centralized VBA module in multiple Access databases
Make your "helper" database. Put a few procedures in there.
In your 'client' databases, open the VBA Editor
Go to Tools->References. Browse and select your helper database.
Done! You can now use all the functions in your helper database
throughout your client databases.
[EDIT]
There's no way (and it's really unnecessary) to create database which automatically starts with MS Access application.
I think that you're looking for a way to create template database which contains the set of custom objects, such as tables, vba module(s), forms, queries, etc.
See:
Save and reuse database design elements
Easy Access with templates I: Create a database
Hey guys I've written a little module/macro that helps our inventory department, they will need to run the module/macro a few times every month. They receive the databases from out in the field these are exported from some 3rd party inventory tracking system we have.
My question, is there any way to install this module/macro outside of the db file but still within access so that the inventory management team does not have to open vb editor, import the module, create a new macro, name it, set it up properly to execute the function?
The module/macro does not need to be changed for any new database that comes in.
Basically they need to be able to open any database received by the field and have this functionality in the module/macro available to them without having to set this. Is this possible?
You could look at my article for vb123.com:
Using Database Library Files in Your Access Application
The thinking there is that you can put access objects in a library file (still an mdb or mde file, or any of the new access file types) and then by just adding a reference to the file in your VBE project, you get that functionality made available to you.
It works with forms, reports, queries, classes, etc. To be honest, I haven't tried it with macros, but don't see a reason why it wouldn't work.
The beauty is that reusable functionality is packaged into one file, that is still just a plain old access file.
If you had the inclination, you could also write an access add in that basically calls your macro in the library.
We did this with a product for making dealing with SQL much easier in Access, and it has worked for years with Access installations all over the world. You can install the add in using a professional installation package such as wise installation, making it a pretty seamless experience for the end user of your macro. However, there is a considerable overhead in writing this kind of setup in a professional way. Depends on what your users need, I suppose.
Perhaps you are thinking of VBScript or a back-end, front-end set up?
You can use VBScript to perform actions on an Access database, through the Access object, with ADO and so on. The script can either accept command line input, request information, or run against any database in the current directory.
With back-end front-end, the new database becomes the back-end and the Access file with your macro is the front-end. Your macro should ask the user for the name of the back-end file and either use that with the Access object or link the tables, according to what is needed to be done.