ADO Connection to Access leaves .ldb file behind with Screen.MousePointer = vbHourglass - ms-access

I'm writing an MS Outlook (2003) macro which uses an ADO Connection to an Access DB (2003). I am opening a connection, getting some records into a Recordset, which I use to populate a grid (but not bind to). I then close the Recordset and Connection and set both to Nothing.
The process creates an instance of MSACCESS.EXE, and a .ldb file for the Access DB, both of which remain after I have closed the Connection, Recordset, Macro and Outlook itself. One or both of these remnants is preventing opening the Access DB until the MSACCESS.EXE process is manually killed and the .ldb file is deleted.
Similar posts say "close the connection" but that is not solving the problem.
Here's the VBA code:
Screen.MousePointer = vbHourglass
Set db = New ADODB.Connection 'Declared at module level
Set rs = New ADODB.Recordset 'Declared at module level
Dim sSQL As String
sSQL = "SELECT Customers.ContactFirstName As Name, Customers.ContactLastName As Surname, Customers.EmailName AS Email, Customers.Address, Customers.Area, Customers.Town FROM qryCustomersWithEmail ORDER BY Customers.ContactLastName ASC"
db.Open "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & "C:\My Documents\Tables.mdb;Persist Security Info=False"
rs.Open sSQL, db, adOpenStatic, adLockReadOnly
If rs.RecordCount > 0 Then
'actions performed on recordset removed
End If
rs.Close
db.Close
Set rs = Nothing
Set db = Nothing
Screen.MousePointer = vbDefault
It's on Vista.
I'm doing this to provide my customer with an easy way to send bulk emails to everyone in his Access database. I tried by accessing Outlook from Access but the security "feature" of Outlook, which pops up a warning message for every email created, scuppered this approach.
UPDATE.
I removed PopulateFlexGrid and the problem still occurred just for opening and closing the recordset. What I previously omitted from this sample, as I considered it irrelevant is that I wrapped the code with Screen.MousePointer = vbHourglass at the start and Screen.MousePointer = vbDefault at the end. I removed this and the problem no longer occurs. Stepping through I see MSACCESS.EXE start up in TM when I call Screen.MousePointer = vbHourglass.
I also tried a version where I used DAO instead of ADO, no other difference, and it works without creating .ldb or starting up an MSACCESS.exe. This works with the Screen.Mousepointer code in there.
Can anybody explain this?

The answer to this is that the reference to
Screen.MousePointer
which i'd omitted as irrelevant, is actually a member of Access. Hence when i call it MSACCESS starts up.
I should have used
Me.MousePointer = fmMousePointerHourGlass
So it's my fault for copying some code from Access VBA to Outlook VBA and expecting it to work the same. Apologies to all of you who spent time looking at this!

What is the code of the function PopulateFlexGrid? Is it possible this is not closing the recordset passed to it?
Try removing this line and instead just looping through the code without doing anything and seeing if it still leaves the LDB. If it still leaves the LDB then the problem is not with the PopulateFlexGrid function

I agree with david saying there is something unclear here. Such a code cannot create an instance of Access or an .ldb file. This code could even run without Access being installed on the machine.
Could you open your recordset with a clientSide cursor? Using a serverSide cursor might cause the access file to be modified (not sure ...) in a way or another. To make sure that your next command does not interfeer in a way or another with access, you could even copy your record locally (to an xml file), close both records and connection, reopen the recordset with the xml file as datasource, and then populate your flexgrid.
By the way, and though it has nothing to do with your problem, it is usually preferable to split the object declaration to:
dim myObect as ADODB....
set myObject = New ADODB....

Related

use login to allow use of dsnless links

i'm using dsnless links in access 2019 to a mysql instance on aws. I have an Access login form, which passes uid & password to a connection string in VBA, and connects and executes simple "select 1".
All along, i assumed the credentials from my access input form were being used to check against my MYSQL users, BUT...it looks to maybe use a cached user/password instead? When i enter a wrong password into my access login form, it still successfully connects and executes the qdf / sql query.
(i can 'watch' the connection string in vba does have the wrong password)
how can i force odbc to authenticate using the actual id & password actually being sent in connection string??
This simple login form has worked, but just realizing now it will pass a wrong password from my access form, but yet still connect and execute the sql...
Function TestLogin(uid As String, pwd As String) As Boolean
On Error GoTo testerror
Dim dbs As DAO.Database
Dim qdf As DAO.QueryDef
Dim RST As DAO.Recordset
Dim strcon As String
Dim errX As DAO.Error
Dim strsql As String
Dim strRW
strcon = "ODBC; Driver=MySQL ODBC 8.0 Unicode Driver;" & _
"SERVER={xxx};DATABASE=xxx;PORT=3306;" & _
"UID=" & uid & "; PWD=" & pwd & ";COLUMN_SIZE_S32=1;DFLT_BIGINT_BIND_STR=1;OPTION=3"
Set dbs = CurrentDb()
dbs.QueryTimeout = 5
Set qdf = dbs.CreateQueryDef("")
qdf.Connect = strcon
qdf.sql = "SELECT current_user"
Set RST = qdf.OpenRecordset(dbOpenSnapshot, dbSQLPassThrough)
TestLogin = True
mysqlUser = uid
floor = DLookup("floor", "tblxx", "user ='" & mysqlUser & "'")
Set qdf = Nothing
Set RST = Nothing
DoCmd.Close
DoCmd.OpenForm "Switchboard"
exit_errorTrap:
Set qdf = Nothing
Set RST = Nothing
Exit Function
Ok, the WAY this works can be confusing.
On application start up we asume you do not by accient (or intention) let any form, any VBA code that runs to touch, or logon or use IN ANY POSSBILE way on startup.
Ok, with above? If you linked the tables correct without UID/Password, if you touch, or even try to open a table from the nav pane? you SHOULD get a ODBC prompt. So, test the above - hold down shift key during startup - NO CODE or NO touch of a linked table is assumed here.
Now, click on a linked table - if the table opens, then you screwed up your table linking, and you DO NOT need the logon, then, right????
Next issue - and READ VERY careful.
If you execute a VALID logon (with your logon code), then ONCE you done this, the correct connection is cached - you can NOT IN ANY POSSBILE way blow out, remove, or re-set that cache.
If AFTER having executed ANY valid logon?
Then ANY ADTIONAL attempted logons (say with your logon code) WILL ALWAYS return true and valid. INCLUDING WRONG logons!!!! (they STILL return true!!!!).
The above information thus means some very significant issue's.
to clear hte logon, you will have to (must) exit Access. You thus can't crete a logout form - you must EXIT applcation.
Worse yet, you BETTER ALWAYS BUT ALWAYS BUT ALWAYS exit access first to clear that cache when developing and testing re-linking of tables. If you at any point in time during development by intention (or accident) open a linked table, and enter uid/password (or do so via code, or do so by JUST clicking on a linked table that DOES and DID save the uid/password?.
Then if you decide to re-link tables - EVEN WITH a incorrect UID/password, they WILL REPORT they linked ok!!!! - but they did not!!!! Even during re-link, if you use the wrong UID/Password, you are screwed, since access will in fact use the cached one (if your uid/password are wrong!!!!).
So, you first have to 100%, if not 200% do the first test above - check, double check/ triple check that linked tables do NOT WORK when clicked on.
Now, run your VBA logon.
Now, cliking on any linked table should work. But as noted, ONCE you touched, or logged on (by any possbile means), the UID/PW is cached, and will remain so for that session until such time you exit access.
What this means is you get ONCE chance to logon. They can fail, but the instant you achieve ONE successful logon, then ALL FURTHER attempts at logons WILL WORK - even if incorrect.
In summary:
You can prompt for a logon.
you can do this without having to re-link tables.
All linked tables, and EVEN pass-though queries will use that cached logon.
You MUST exit access to clear this cache, and thus it is IMPOSSILE to now launch your custom logon form and logon with a different user, since the previous user remains cached.
This also as noted means during deveopment, to link tables without uid/pw, you need to exit Access. launch access (without ANY POSSBILE touch of linked tables/data).
You then execute your logon. You THEN re-link tables without UID/Password. Then you have to exit access, re-launch (shift startup by-pass). Now click on a table - it should fail - prompt for odbc.
But, if you exit, shift- by pass startup. Execute your logon. Now click on a linked table - it should work. If it does not, or the first test (without a logon DID work, then as noted, you have royal screwed up something here.
so, for wrong/incorrect logons? You can continue trying to log on.
But ONCE you have logged on - that's quite much the end of this process. Any further attempts at logons will ALWAYS work - even for totaly wrong logons - you have to exit to clear the PW cache.
you also as a result can thus not use two cached passwords - say some tables linked as read only, and some tables linked as read/write (based on different uid/pw). and the reason is you can't control which logon will be used - and access will thus choose the read/write logon.
So, I suppose the most simple explain?
Once a uid/pw is cached, then you can and will be able to use all linked tables regardless of what uid/logon you attempt to use. And EVEN if at that point in time you did decide to run your re-link code? it will re-link JUST fine, even if you supply a incorrect uid/password. But, be carful!!! - you can in some cases thus re-link, check that the tables worked, but when you exit, and re-enter - you find those linked tables now don't work!!!
So, be VERY careful, if not outright SUPER careful when you re-link, especially if you been messing around with connections - you have to exit access, shift by-pass startup. and THEN re-link your tables. One stray cached uid/password, and you are in heaps of trouble - since things will work, and not make any sense at all.
keeping the above simple concept in mind - all will and should now make sense.
I been using this code:
Function TestLogin(strCon As String) As Boolean
On Error GoTo TestError
Dim dbs As DAO.Database
Dim qdf As DAO.QueryDef
Set dbs = CurrentDb()
Set qdf = dbs.CreateQueryDef("")
qdf.connect = strCon
qdf.ReturnsRecords = False
'Any VALID SQL statement that runs on server will work below.
' this does assume user has enough rights to query built in
' system tables
qdf.sql = "SELECT 1 "
qdf.Execute
TestLogin = True
Exit Function
TestError:
TestLogin = False
Exit Function
End Function
now you used a pass-though query - and I never done/use that before - and don't think it will work. You have to use above format.

Reattaching [Event Procedure] in Access

Due to requirements to not share data between clients, I have an MS Access 2010 data base that I use to extract data from our SQL Server into a small .accdb which we then send to a client, they modify, then I load the data back to the Server.
In my 'Master' Access database (EDIT2: I use Access as a front end to SQL Server), I have a button that will create a client specific .accdb (EDIT2: This has a local table that contains the client's specific data for them to moidify). This code simply copies a template .accdb with forms, code, etc & names it appropriately for the client.
Unfortunately when this copy is made, all the event procedure connections are lost from the properties box. The code still exists in the module, though. This is a fairly well-known issue and is well documented on Google. The general solution is to go through each form & reset the properties for every form & control that needs an event, then Access will reconnect it with the existing code. That's ok, once. I'll have this copy/loss issue dozens or hundreds of times.
I found one reference from ~2003 March, 2004 to dynamically identify missing code (EDIT2: [Event Procedure] references in the properties box) & set the property to [Event Procedure] to fix this. However, when trying to identify if an object should have an event handler, the code relies on this statement
DLookup("EventProcedureSuffix", "EventProcedures", "EventName = '" & prpCurr.Name & "'")
and that generates an error 3078 saying it cannot find a table or query named 'EventProcedures' (EDIT2: which seems to have been a system table in the older version of Access that the code was based on). Does anyone know what happened to the 'EventProcedures' table in Access 2010? Has it been renamed, is it no longer accessible, is there a replacement?
This also begs the question of how do I fire this code in the first place. I have it on the OnOpen event of the main form that is opened when the DB is opened, but if the event handler is disconnected, that won't fire, either...
EDIT: Found the link to the source of the code I'm using: http://www.accessmvp.com/djsteele/Access/AA200403.zip
Instead of trying to re-attach the Event Procedures after the fact you might try to find a method that creates a new user database in a way that preserves the Event Procedure links.
The following Access 2010 code seems to work fine for me. It creates an Access 2003 format .mdb file and then exports a Table and a Form. The form has a button with code behind it, and the button works fine when I open the form within the .mdb file.
Option Compare Database
Option Explicit
Public Function CreateUserDatabase()
Dim fd As Object ' Office.FileDialog
Dim db As DAO.Database
Dim newDbPath As String
Set fd = Application.FileDialog(2) ' msoFileDialogSaveAs
fd.Title = "Save User Database As..."
fd.InitialFileName = "UserDB.mdb"
fd.Show
If fd.SelectedItems.Count <> 0 Then
newDbPath = fd.SelectedItems(1)
If UCase(Right(newDbPath, 4)) <> ".MDB" Then
newDbPath = newDbPath & ".mdb"
End If
On Error Resume Next
Kill newDbPath
On Error GoTo 0
Set db = DBEngine(0).CreateDatabase(newDbPath, dbLangGeneral, dbVersion40)
db.Close
Set db = Nothing
DoCmd.TransferDatabase acExport, "Microsoft Access", newDbPath, acTable, "UserData", "UserData", False
DoCmd.TransferDatabase acExport, "Microsoft Access", newDbPath, acForm, "UserForm", "UserForm", False
MsgBox "The user database has been created.", vbInformation
End If
Set fd = Nothing
End Function

VB6- Open Access DB or one of it's forms with ADO object

My application written with visual basic 6 and it has an Access database. I want to add a VB form and open the database in this form to make db edits the DB in that. I have this code for oppenning:
Dim db As DAO.Database
Set db = DBEngine.workspaces(0).opendatabase("c:\ss.mdb")
I have a form inside that database. This form makes the data insertion process faster. I want to open this access form with my application.
How i should do this??
Note: I have this code that uses Microsoft access 14 object library.
Dim appaccess As Access.Application, dbstr As String
On Error Resume Next
Set appaccess = New Access.Application
Set appaccess = CreateObject("Access.Application")
dbstr = "c:\ss.mdb"
'Or dbstr="c:\my documents\yourfile.mdb
'put the correct path here.
appaccess.OpenCurrentDatabase dbstr
appaccess.DoCmd.OpenForm "aa", acLayout
appaccess.Visible = True
But when i run this code the form appears and after a while it gets disapear. besides using access object library creates some access version conflicts. So, Although it is not necessary but i prefer to do that with ADO object. Anyway, i' looking for e method to solve my problem.
Thank you for your help
ADO provides methods to interact with various data sources. Although you can use it for Access db files, it doesn't provide methods to utilize Access forms.
With your current approach, you use CreateObject to set the object variable appaccess to a new Access application instance, then open your form within that instance. However, when the variable goes out of scope, the Access instance closes and the form disappears.
You might revise your VB6 code to keep the variable in scope until you're finished with the Access instance. Unfortunately, I don't know how to fit that change into the rest of your code, and I've never used VB6.
Alternatively, you could use the Shell Function to start the Access instance, then use GetObject() to set your object variable to that instance.
With the Shell() approach, you would need to supply the full path to MSACCESS.EXE. You can find the folder where it's located by reading the registry. Here's a VBScript sample which does that, and I'm hoping you can adapt it easily for VB6.
Option Explicit
Dim MSAccFolder
Dim RegKey
Dim WSHShell
RegKey = "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\" _
& "CurrentVersion\App Paths\MSACCESS.EXE\Path"
Set WSHShell = WScript.CreateObject("WScript.Shell")
MSAccFolder= WSHShell.RegRead(RegKey)
WScript.Echo "MS Access Folder: " & MSAccFolder
Set WSHShell = Nothing
One thing is that you're using DAO in that first chunk of code rather than ADO.
Maybe that doesn't matter though because there's no reference to db in your second chunk of code.
So the first chunk of code looks to be unecessary.
I don't know why you have 2 set statements for appaccess, you don't need the second one.
Also I'd suggest that you comment out the 'on error resume next' statement so you can see which line is causing the error while debugging.
If you're going to use VB, you should abandon the Access Automation stuff and create your form in VB. Then use ADO to open and work with the Access file. This is much more efficient than using VB to automatically open an Access form.

Block committing changes to Microsoft Access database

Is there a way to write a hook that will run before changes are committed to an Access DB? I'm looking for a way to block changes if a specific process is currently querying the DB.
You didn't give us much information to work with. Can you adapt something like Create and Use Flexible AutoNumber Fields so specific process first opens a table exclusively? Then any other operations which might change data would have to wait until they can lock that same table.
What is specific process? Do you have a method to determine when/if it is reading data from your database?
If specific process is external to the database, like web server ASP code which uses ADO to fetch data, you could see whether the ADO connection Mode and IsolationLevel properties can help.
Update: I used a form to experiment with the adModeShareDenyWrite Mode property for an ADO connection. While the form is open, other users can open the database, but not make any changes. However, if another user already has the db open when the form attempts to open the connection, it triggers an error.
Perhaps your deployment script could attempt to open a adModeShareDenyWrite connection, and bail out on error.
Option Compare Database
Option Explicit
Dim cn As ADODB.Connection
Private Sub Form_Close()
If Not (cn Is Nothing) Then
cn.Close
Set cn = Nothing
End If
End Sub
Private Sub Form_Load()
Set cn = New ADODB.Connection
cn.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=\\Cmpq\export\Access\backend\links2003.mdb;" & _
"User Id=admin;Password=;"
cn.Mode = adModeShareDenyWrite
cn.Open
End Sub

MS Access ADP Disconnected Recordset Reverts

I have an Microsoft Access 2003 ADP, which uses a bound "main" form with several bound continuous-style subforms. They all utilize disconnected Recordsets via a helper class.
Several users have noticed the same strange behavior: they add/edit a record in the continuous subform, they leave the record (committing the edit to the Recordset), they lock the computer (Ctrl+Alt+Del), they unlock the computer, they go back to the form, after about 5 seconds it flickers and revert to the original unedited state.
I've been able to reproduce this following the steps above, further, after making a change in my form, bound to a disconnected Recordset, I went to SQL Server and changed a value. After the lock/unlock computer routine, the form flickers and refreshes, and the NEW value I just entered in SQL Server appears.
It's as if after about 5 seconds, my disconnected Recordset is reconnecting (on it's own) and requerying the Recordset.
I realize I'm not giving a lot of information here, but has anyone encountered an issue with disconnected Recordsets reconnecting and requerying? Or at least have an ideas of where I could start debugging?
I have the ADP and SQL script to create the database if someone would like to recreate this in their environment.
Here's how I'm creating the disconnected Recordset:
Dim cnn As ADODB.Connection
Dim stmTest As ADODB.Stream
Set cnn = New ADODB.Connection
cnn.Open Application.CurrentProject.AccessConnection.ConnectionString
' Create recordset and disconnect it.
Set mrsTest = New ADODB.Recordset
mrsTest.CursorLocation = adUseClient
mrsTest.Open "SELECT * FROM [tblChild] WHERE ParentID = 1" _
, cnn _
, adOpenStatic, adLockBatchOptimistic
Set mrsTest.ActiveConnection = Nothing
cnn.Close
Set cnn = Nothing
' Copy recordset to stream in memory.
Set stmTest = New ADODB.Stream
stmTest.Open
mrsTest.Save stmTest, adPersistADTG
' Bind form to disconnected recordset.
Set Me.Recordset = mrsTest
' Open a copy of the recordset from the stream.
Set mrsTest = New ADODB.Recordset
stmTest.Position = 0
mrsTest.Open stmTest
I wanted transaction like processing (Save and Cancel buttons) without
the tables being locked, in my multiple user system. For continuous
forms in Access to work, they must be bound to a Recordset.
You can use forms/subforms bound to temporary tables to achieve just this effect.
http://www.access-programmers.co.uk/forums/showthread.php?t=206862
Steve
Probably a little bit offtopic, but this is interesting code. I have never thought about working with datasets like this. Is there a particular reason why you use these 'disconnected datasets'?