Access 2003: run code on database close - ms-access

In Access 2003, there are ways of running code when a form or database is opened, but what about when the database is closed?
My motivation is the unavoidable use of a somewhat buggy third-party COM library. Releasing the COM reference (by setting the variable to Nothing) causes it (correctly) to disconnect from its server. The trouble is it can't then re-connect without exiting the process and starting a new one (which is a known bug). In this case, the process is the whole Access IDE :(
Ideally I'd like to store the COM reference somewhere that would be protected from the VBA "Reset" action which clears global variables (and is common during debugging, sometimes forced by a code edit). But then I would like to have the chance to clean up before the database is closed.

You could run code at database close if you have a form which you set to automatically open at database startup ... and leave the form open. Then you can use the form's On Close event to run your cleanup code:
Private Sub Form_Close()
'do your stuff here '
End Sub

don't release the reference. Let Access do that when it closes.

Related

Access Application.Quit not working, SharePoint Web Database

In my database (Access 2013, .accdw), I am checking the user's current version in the Form_Open event. If it is not up to date it triggers an external command to download a fresh copy and then is supposed to close itself to allow for the update. The problem is that after Application.Quit is triggered, access closes but instantly reopens, blocking the download. I've stripped out all the code I can to isolate the problem.
If 1 = 1 Then
Application.Quit
End If
Simplified pretty far, right? It should always just close as soon as the form is opened. With this as the only code in my Form_Open event which is the only code in the form, it still closes, reopens, and then closes again. Docmd.Quit has the same effect. I've tried too many variations to enumerate. In a button this code works fine, but I need it to run the check before it loads any data (the linked tables may be being altered while we change versions).
Any ideas how to make it stay closed the first time it closes?
I think it reopens because your external code (.bat file ?) reopens it. It's not an Access problem. Have you checked the numerous tools you can find for Access automated client deployment ? Here are the first 2 I found:
http://www.databasejournal.com/features/msaccess/article.php/3286111/Automatically-Deploy-a-New-Access-Client.htm
http://www.devhut.net/2015/06/30/ms-access-deploying-your-database-front-end-to-your-users/
Your code works for me in a Form_Open(), but then this is Access 2010 and no Sharepoint.
You may have better luck by Creating an AutoExec macro that calls an initialization function that does the version check, instead of having a form open automatically.
If the version check is ok, then open your start form from the function.
The easy work-around is to always fetch the current version and then launch it.
This way there's no fuzz and the user always run the latest version.

Running Microsoft Access as a Scheduled Task

I am seeking comments on how to schedule auto updates of a database (.accdb) since I am not very comfortable with the process I have set up.
Currently, it works as follow:
Task Scheduler calls a .bat
.bat calls a .vbs
.vbs opens the database and calls a macro
The macro calls a function (VBA Level)
The function calls the update Subroutine
I consider there are too many steps and the fact that it requires 2 external files (.Bat and .vbs) related to the database and stored on the system increase the risk that the procedure would break.
Apparently (but please tell me that I am wrong and how I can change it) .vbs cannot call a subroutine but only a macro. Identically, an access macro cannot call a subroutine but only a function if the user is expecting to enter the VB environment of the database. This is the reason why I called a function (VBA Level) that then calls the subroutine.
Hope some of you know how to shorten the steps and eventually get ride of the .bat and .vbs
To the best of my knowledge the shortest path for a Windows Scheduled Task to "do something useful in Access VBA" is:
Create a Public Function (not Sub) in the database. For example:
Option Compare Database
Option Explicit
Public Function WriteToTable1()
Dim cdb As DAO.Database
Set cdb = CurrentDb
cdb.Execute "INSERT INTO Table1 (textCol) VALUES ('sched test')", dbFailOnError
Set cdb = Nothing
Application.Quit
End Function
Create a Macro in the database to invoke the function:
Create a Windows Scheduled Task to invoke MSACCESS.EXE with the appropriate parameters
In the above dialog box the values are:
Program/script:
"C:\Program Files\Microsoft Office\Office14\MSACCESS.EXE"
Add arguments (optional):
C:\Users\Public\schedTest.accdb /x DoSomething
A VBS script can call any standard VBA SUBROUTINE with the following:
dim accessApp
set accessApp = createObject("Access.Application")
accessApp.OpenCurrentDataBase("C:\MyApp\MultiSelect.mdb")
accessApp.Run "TimeUpDate"
accessApp.Quit
set accessApp = nothing
Note that the sub TimeUpDate is a standard VBA subroutine. This means no Autoexec macros, and no macros at all - only pure VBA sub calls + this VBS script.
There is a little known trick dating back to the earliest years of access to allow it to run as a process which still works. Access will always on startup look for a macro called "Autoexec". If it finds it it will immediately start executing this macro. I find this is extremely useful if I need to initialise the program before opening forms or, as in the case of the original questioner, run access as a scheduled process with no user I/O.
After beating my head against the wall for about four hours, I finally got this to work:
1) Create a DOS batch file with one line it. The line is composed of three parts a) the full path to Microsoft Access (msaccess.exe), b) the full path of the Microsoft Access database with the code in it, and c) the Access command line argument "/x MacroName". The first two items should be surrounded with quotes. Mine looks like this:
"C:\Program Files (x86)\Microsoft Office\Office14\MSACCESS.EXE" "C:\MyPrograms\ProdDB Reports\ProdDB Reports.accdb" /X DailyTestReportsRun
2) Create a macro inside of Access with the name you used in your batch file. It has one command, RunCode, with an argument of the name of a VBA function you want to call. This should be followed by open/close parenthesis "()". I didn't try passing any parameters to the function; I think this would be problematic.
4) Make sure the VBA function you call has a Docmd.Quit command at the end, or that you add this as a second line to your macro. These will make sure that Access doesn't stay open after your process runs.
5) In Windows Task Scheduler, select "create a basic task" (which invokes a wizard). Set the program name to the name of your DOS batch file. There's a helpful check box labeled something like "Open the properties window when I'm finished." Check that so you that go to the properties window.
6) Set the task to run regardless of whether the user is logged on or not. Also check on the "Run with highest privileges" box, which one friend on here suggested.
You can now test everything by right-clicking the scheduled task and selecting the Run command.
I liked Albert Kallal's script and tried it. Everything worked great until I tried to schedule it. Then, for some mysterious reason the scheduler would not kick it off.
none of the above work
ms access DB with task scheduler will not work as to open the db run code and quit the application
the solution I found is to avoid task scheduler and have the ms access db open all time and have a timer in msaccess do the job

What is this Access bug? You do not have exclusive access to the database at this time

Tested on Access 2003 Pro (build 11.8321.8324) SP3.
Steps to reproduce:
create a new database.
create a new form.
put a button on the form.
paste the following code in the button's Click event procedure:
Debug.Print Workspaces.Count
Debug.Print CurrentDb.Name
close the code editor and form, saving changes.
do not skip this step: close Access.
re-open Access and your database.
open the form
click the button
click the toolbar button to switch the form to design mode.
You should see the following error dialog:
You do not have exclusive access to the database at this time. If you proceed to make changes, you may not be able to save them later.
Does anyone know what is going on here?
The simple workaround is to call CurrentDb prior to calling Workspaces for the first time:
Debug.Print CurrentDb.Name
Debug.Print Workspaces.Count
Debug.Print CurrentDb.Name
I'll take a shot at demystifying what's going on, but this is just my theory.
The relevant bits of the Access help file are as follows (for CurrentDb):
Note In previous versions of
Microsoft Access, you may have used
the syntax
DBEngine.Workspaces(0).Databases(0) or
DBEngine(0)(0) to return a pointer to
the current database. In Microsoft
Access 2000, you should use the
CurrentDb method instead. The
CurrentDb method creates another
instance of the current database,
while the DBEngine(0)(0) syntax refers
to the open copy of the current
database. The CurrentDb method enables
you to create more than one variable
of type Database that refers to the
current database. Microsoft Access
still supports the DBEngine(0)(0)
syntax, but you should consider making
this modification to your code in
order to avoid possible conflicts in a
multiuser database.
And for the Workspaces collection:
When you first refer to or use a
Workspace object, you automatically
create the default workspace,
DBEngine.Workspaces(0).
It would seem that by creating the default workspace prior to the first call to CurrentDb, you are somehow causing CurrentDb to forget how it should work. It seems that instead of creating a new instance of the current database it just uses the one that's already lying around in the default Workspace.
Of course, this is all conjecture and I'm as curious as you to know the "real answer".

ms-access: doing something on database open

is it possible to run a sub or function as soon as the user opens an access database file? if so, how?
Create your function:
Public Function DoSomething()
' do stuff '
End Function
Then create a macro with the run code action which calls your DoSomething function. Name the macro autoexec. Then, every time the data base starts up, it will run your autoexec macro.
Another thing you can do is set a form to open whenever the database starts. You could then call your DoSomething function from a form event (on open, or on load).
Choose one of those approaches. Either way, if you ever want to start the database without DoSomething running, hold down the shift key as the database opens to bypass your automatic startup routine.
You could open a hidden form on start up like in Access 2007 Startup.
This is also possible in older Access version.
You may use this hidden form for logging or other 'system'-related tasks.

VBA File Open is slow

I'm trying to open a series of Excel spreadsheets using an instance of Excel created inside of a module in an Access database. I can get the files to open properly; however, the actual call to make Excel start takes quite a while, and to open the files takes even longer. The location of the files doesn't matter (same time to open on a local HDD as a network drive).
In an attempt to figure out what was taking so long, I added a timer to the logging module. Opening the files takes approximately 2m30s, during which the host application (Access) is entirely unresponsive to user input); the rest of the script executes in less than 10 seconds.
I'm using the standard Excel.Workbooks.Open call as follows
Set OpenSpreadsheet = Excel.Workbooks.Open(Name, 2, False)
Using Debug.Print methods around this line says it can take up to 2 1/2 minutes for this one line to execute.
Is there anything I can do to make the Excel files open quicker?
EDIT: When opening, UpdateLinks is False and ReadOnly is True; all other options are left to their defaults.
First idea: Can you use a jet driver with an ODBC connection to Excel, instead of opening it in an Excel object? Might be much faster.
Second idea: Make sure to create and instantiate the Excel application object just once at the beginning of the routine, then use the Excel.Workbooks.Open() and Excel.ActiveWorkbook.Close() for each spreadsheet. That way you're not "re-launching" the MS Excel application each time.
To draw out the second of #BradC's well-advised recommendations, if you need to use Excel in more than one procedure, create a self-initializing global function. I always use late binding for automating Office apps.
Public Function Excel(Optional bolCleanup As Boolean = False) As Object
Static objExcel As Object
If bolCleanup Then
If Not objExcel Is Nothing Then
Set objExcel = Nothing
Exit Function
End If
End If
If objExcel Is Nothing Then
Set objExcel = CreateObject("Excel.Application")
End If
Set Excel = objExcel
End Function
Then you can use it in code without needing to initialize it, and the single instance will remain available to any code that needs to use Excel.
And when you shut down your app, you'd call Excel(True) to clean up.
I do this with Outlook and Word all the time. However, there are some COM apps that it works poorly with, such as PDF Creator, which doesn't take kindly to this kind of treatment (you end up in an endless loop with it shutting down and re-initializing itself if you try to destroy the instance this way).
Another approach depends on your setup and process.
In my case I need read-only access to a set of Excel documents stored on SharePoint. My code is currently like:
For each path in paths
set wb = Workbooks.open(path,false)
next
In this case, each workbook is individually downloaded each time a workbook is opened. It would be significantly more efficient if the files were downloaded asyncronously and after the download is complete, the rest of the process executes on the local disk.
My idea is to use CopyFileEx() and pass a callback function. Excel will then download the Excel documents to disk asynchronously and call a VBA function regarding progress. When all files are completed, we can launch the next part of the process, which opens the local workbooks, scans them and then removes them from the local drive.
I'll post code later if I manage to implement it.