Issue when using a DAO handle when the database closes unexpectedly - ms-access

I am using a DAO handle (represented in the below code) to improve the speed and performance of my Access database which is found on a shared network and is quite slow. The below code was offered to me by an expert to help the database improve it's speed and performance. As you can see, the database upon opening opens the handle (OpenAllDatabases True), and then closes it upon closing the database (OpenAllDatabases False).
My issue arrives when the database unexpectedly closes. When this happens, I am then informed that i no longer can get into edit mode of the database because it is already opened by another user. I imagine that this is the case because the the 'OpenAllDatabases' was set to TRUE when the database unexpectedly closed. When this happens, i am forced to open the database in exclusive only deactive the code, close and re-open the database and then rebuild the code. This for me is quite risky especially since there are multiple users using the tool. Below is my code:
On the main form:
Form_Load()
OpenAllDatabases True
End Sub
On the command buttons to close the database:
Private Sub cmdCloseDatabase_Click()
OpenAllDatabases False
End Sub
Module
Sub OpenAllDatabases(pfInit As Boolean)
' Open a handle to all databases and keep it open during the entire time the application runs.
' Params : pfInit TRUE to initialize (call when application starts)
' FALSE to close (call when application ends)
' Source : Total Visual SourceBook
Dim x As Integer
Dim strName As String
Dim strMsg As String
' Maximum number of back end databases to link
Const cintMaxDatabases As Integer = 2
' List of databases kept in a static array so we can close them later
Static dbsOpen() As DAO.Database
If pfInit Then
ReDim dbsOpen(1 To cintMaxDatabases)
For x = 1 To cintMaxDatabases
' Specify your back end databases
Select Case x
Case 1:
strname="S:\Apps\PRESTO\BE.accdb"
End Select
strMsg = ""
On Error Resume Next
Set dbsOpen(x) = OpenDatabase(strName)
If Err.Number > 0 Then
strMsg = "Trouble opening database: " & strName & vbCrLf & _
"Make sure the drive is available." & vbCrLf & _
"Error: " & Err.Description & " (" & Err.Number & ")"
End If
On Error GoTo 0
If strMsg <> "" Then
MsgBox strMsg
Exit For
End If
Next x
Else
On Error Resume Next
For x = 1 To cintMaxDatabases
dbsOpen(x).Close
Next x
End If
End Sub

In Sub OpenAllDatabases, I see a problem with these two lines:
Const cintMaxDatabases As Integer = 2
' ...
For x = 1 To cintMaxDatabases
Select Case x
Case 1:
strname="S:\Apps\PRESTO\BE.accdb"
End Select
You are going through the loop twice, but are only setting the database path once. If you follow your code, you are making TWO connections to "S:\Apps\PRESTO\BE.accdb".
Fix this error so you are only making one connection, and see if your problem goes away.

OK, thanks for fixing that issue.
I use similar code, which works all the time. I have been comparing your code to mine, and trying to think what the difference could be.
The next thing I would like for you to try is change this line:
Set dbsOpen(x) = OpenDatabase(strName)
To:
Set dbsOpen(x) = OpenDatabase(strName, ReadOnly:=True)
In my quick tests, this will still improve the performance of the application, and your forms can still write to the backend data.
This way, OpenAllDatabases cannot get a write lock on your backend database. See if this solves the issue when your frontend closes unexpectedly.

Related

Currentdb.Execute with dbFailonError not throwing an error

In Access 2003-2016, I am using CurrentDb.Execute with dbFailonError to run an INSERT statement. The INSERT statement should fail (and it does) because one field has an incorrect value based on a related table with "Enforced Referential Integrity". However, it does not throw an error. I have tried recreating this issue in a new database, and the error works correctly. There is something wrong in the settings with my current database, and I don't want to recreate it from scratch. I have taken everything out of my database except for the problematic piece, and my minimal reproducible example database is at this link.
Here is my code, but the problem is that this code works fine and does throw errors when I create a new database from scratch. It just doesn't work in my current database.
Private Sub Command34_Click()
Dim testsql As String
testsql = "INSERT INTO tblObservations (Site,TotalDepth) VALUES ('SUD-096',5)"
With CurrentDb
On Error GoTo Err_Execute
.Execute testsql, dbFailOnError
On Error GoTo 0
MsgBox ("Upload completed. " & .RecordsAffected & " records added.")
End With
Exit Sub
Finish:
Exit Sub
Err_Execute:
If DBEngine.Errors.Count > 0 Then
For Each errLoop In DBEngine.Errors
MsgBox ("Error number: " & errLoop.Number & vbCr & errLoop.Description)
Next errLoop
End If
Resume Finish
End Sub
Use Option Explicit, like Hans said. Always use Option Explicit!
You're missing a reference to the Microsoft Office ##.# Access Database Engine object. This is where dbFailOnError is defined. Because you don't have that reference, dbFailOnError is not defined. This reference is added to all Access databases by default, and I strongly recommend adding it.
And because you're not using Option Explicit, VBA doesn't mind that it's undefined and just casts that undefined variable to a zero.
If, for some reason, you don't want to add the reference, use the corresponding value for dbFailOnError:
.Execute testsql, 128

Access Run-time Error 2475 *occasionally* occuring

I have an error log that logs in the access table whenever a runtime error occurs for a user in the error trapper, and a particular error seems to occur for 10 random users, every hour at least.
This error appears to occur completely at random, on a random module with the Set ActiveForm code, with random users at random intervals. As far as I can see, there is no pattern between the users.
2475 - "You entered an expression that requires a form to be the active window".
This appears to occur in any of the modules that contain any of the setting of a form. I am using the following lines:
Dim af as Object
Set af = Screen.ActiveForm
I have tried using alternatives, such as declaring it as Form, and also tried the below:
Dim sstatus as String
Dim ps as String
If DLookup("[TM_UserType]", "[SD_Teams]", "[TM_username]= '" & usernm & "'") = "adj" Then
sstatus = "adj"
Else
sstatus = "tm"
End If
ps = "frmProdSubmit_" & sstatus
Then referencing the form this way:
Forms(ps).cmbTeam.Value = ""
But this still causes the same issue, even removing the ActiveForm part.
The last thing to mention (as I believe they could be factors) is that the front end is accessed through a shortcut, which minimises the Access window. Not sure if this could be the culprit, or if the user clicking another application can remove the focus.
The back-end of the database is also accessed by up to around 700 users each day.
As it stands, the error trapper pops up with the message, but the front end continues working correctly. It's just an annoying issue to resolve, but am slowly running out of ideas now, and any help would be hugely appreciated!
Error 2475 is thrown when a non-form object is the active screen object such as a table datasheet. I've encountered this error in an application that uses multiple instances of a form and needs to track whether the multiple form module is active or one of the other application module functions in which case all instances of the multiple forms (popups) need to have .visible set to false. I use the Screen.ActiveForm.Name call in the Form_Deactivate event.
You can trap the error in the procedure's error handler and take action knowing the screen's active object is not a form.
Example:
Private Sub Form_Deactivate()
On Error GoTo errHandler
If Screen.ActiveForm.Name <> "AnApplicationForm" Then 'throws 2475 if not a form object
sstatus = "status message"
End If
ExitSub:
Exit Sub
errHandler:
If Err.Number = 2475 Then Resume Next 'screen active object is not a form i.e. datasheet
MsgBox "Error in " & Me.Name & ".Form_Deactivate: " & Err.Number & " - " & Err.Description
Resume ExitSub
End Sub 'Form_Deactivate

How to eliminate period of unresponsiveness when checking for disconnected share drive in Access 2007

I have a database that has a lot of stored data in a shared drive (another database and stored pictures to look up). To make sure that this data is present before I try to access it, I run a small sub to check if it is connected and will allow the user to retry the connection again if it failed:
Public Sub CheckConnection()
Dim Ans As Long
On Error GoTo NotConnected
ReDo:
If Dir("<Filepath>", vbDirectory) <> "" Then Exit Sub
NotConnected:
Ans = MsgBox("Access can't find the share drive! Please check your connection to the Share Drive!" & vbNewLine & _
"If you have reconnected to the sharedirve, click ""Retry"". Otherwise click ""Cancel"" to close the database.", vbRetryCancel, "Connection Error")
Select Case Ans
Case 2 'Cancel
DoCmd.Quit acQuitSaveAll
Case Else
Resume ReDo
End Select
End Sub
The issue with this is that the first time (and only the first time) this code runs and fails, Access grays out and says that it is not responding for 10-20 seconds before the msgbox pops up. Once this happens, and subsequent failures are immediate causing the msgbox to pop up again as quickly as you can click retry. I want to know how to eliminate that initial time out of Access and immediately let the user see the msgbox.
I have also tried the following code and it yielded the same results:
Dim fso As Object
Set fso = CreateObject("Scripting.FileSystemObject")
Select Case fso.FolderExists(fso.GetParentFolderName(<Filepath>))
Case True
Exit sub
Case False
End Select
Ans = MsgBox("Access can't find the share drive! Please check your connection to the Share Drive!" & vbNewLine & _
"If you have reconnected to the sharedirve, click ""Retry"". Otherwise click ""Cancel"" to close the database.", vbRetryCancel, "Connection Error")
Select Case Ans
Case 2 'Cancel
DoCmd.Quit acQuitSaveAll
Case Else
Resume ReDo
End Select

disable shift key on startup in ms-access

Problem: In MS Access you can hold the shift key when opening a database in order to bypass Startup options and the AutoExec Script. I want to disable this permanently.
First of all I know this has been answered on numerous other sites, however I could not find a question about it here, but I have a slightly different need.The solutions I found were focused on placing invisible buttons to re-enable the shift-key shortcut with passwords etc.
I want a very simple solution. I want a script I can add to my AutoExec script to disable the shift-key shortcut or something like that.
I DO NOT need a way to re-enable the shift-key shortcut.
The simplest, most secure, and easiest way to do this is preferred.
Thanks!
I have always used this bit of code
Function SetBypass(rbFlag As Boolean, File_name As String) As Integer
DoCmd.Hourglass True
On Error GoTo SetBypass_Error
Dim db As Database
Set db = DBEngine(0).OpenDatabase(File_name)
db.Properties!AllowBypassKey = rbFlag
setByPass_Exit:
MsgBox "Changed the bypass key to " & rbFlag & " for database " & File_name, vbInformation, "Skyline Shared"
db.Close
Set db = Nothing
DoCmd.Hourglass False
Exit Function
SetBypass_Error:
DoCmd.Hourglass False
If Err = 3270 Then
' allowbypasskey property does not exist
db.Properties.Append db.CreateProperty("AllowBypassKey", dbBoolean, rbFlag)
Resume Next
Else
' some other error message
MsgBox "Unexpected error: " & Error$ & " (" & Err & ")"
Resume setByPass_Exit
End If
End Function
You pass it a filename and then say if you want the bypass key to be enabled or not.
The problem is that anyone else with this code can use it to “Unlock” your database and enable the bypass key.
The only way I can think to get around this would be to only give the users the runtime version of access

Corrupt Form - Rescue or Remake?

During my work on this database application, I've apparently managed to corrupt a form in the application - attempting to save any edit to any field on the form will cause Access to crash, and for the database file to report corrupted when Access attempts to re-open it.
I've tried exporting the entire form + controls as text, then re-importing them using VB code (from Allen Browne's website) but it will not re-import without either crashing Access or telling me the form isn't import-able due to an error (no error number or description given).
The form is rather complex, hence I am hesitant to just remake it from scratch, so is there a way to save it? If I do manage to recover it, does this mean I should transfer everything to a new MDB file (in case it's a cascading failure effect)?
To be honest, I've never managed to corrupt an Access database object before, so I don't know if this is something that signals the end of that MDB file, or just something I can correct and continue as before.
Decompile is a good thing to try once you've made a copy of the database. Have you tried saving the form under a different name using File >> Save As? Also try copying and pasting the form with a different name from the database window.
Also it's been my experience that one corrupt form/report does not spread to the rest of the database. That said it doesn't hurt to clean things up. Compact and repair only fixes up tables and related data such as indexes and relationships. To clean up corrupted other objects such as forms and reports you must import them into a new MDB/ACCDB. Tip: Close the database container window if you have a lot of objects. Access wastes a lot of time during the import refreshing the database container window.
What I ended up having to do was re-create the form, and copy element by element until I discovered that the strSupplierID combo box itself was the cause of the crashing. I re-created it from scratch, manually giving it the same properties, and replacing the VB from stored copies I cut and pasted to the clipboard. The form now works, and I removed the corrupted form, and compacted the database. Thanks for the help, everyone! :)
Others have supplied you with various approaches to possibly recover your corrupted form. Sometimes an code-bearing Access object will become irretrievably corrupt and none of these methods will work. In that case, you'll have to look= at backups to find a non-corrupt version as a starting point and import that and then revise it back to the current state of the object.
I'm posting an answer to suggest that you probably need to change your coding practices if you're encountering corruption in code-bearing objects.
First, you need to make sure you keep regular backups and do not overwrite them. Rolling back to an earlier version is always a last resort.
Always turn off COMPILE ON DEMAND in the VBE options. Read Michael Kaplan's article on The Real Deal on the Decompile Switch for the explanation of why.
In the VBE, add the compile button (and the call stack button) to your regular VBE toolbar, and hit that compile button after every few lines of code, and save your code.
Decide on a reasonable interval to backup and decompile your app. If you're doing heavy-duty code pounding, you might want to do this every day. If you've experienced an Access crash during coding, you likely want to make your backup and decompile/recompile. Certainly before distributing to users, you should decompile and recompile your app.
If you follow these practices, the causes of corruption in code-bearing Access objects will be minimized as much as possible, while you will also have plenty of backups (multiple levels of redundant backups are a must, because when backup failures happen, they almost always cascade through multiple levels -- have several types of backup and don't depend on an automatic backup).
But the key point:
Compile often, decompile reasonably often and icky stuff will never have a chance to accumulate in the p-code of your application.
Have you looked at the full set of methods for dealing with corruption from Allen Browne: http://allenbrowne.com/ser-47.html ? In particular, decompile.
It may be worth try a copy and paste of the controls into a new form and gradually add back in the code.
I have had that happen to me many times. Here are a couple things that have saved my bacon. I am assuming you are using Access 2003 or higher. Try converting the database to Access 2002 or 2000 format. Then convert that database back to your current version.
Here is some code that I created to combat bloat in previous versions. It also solved this issue for me 95% of the time.
Option Compare Database
Option Explicit
Private Sub cmdCreateDuplicate_Click()
'********************************************************
' Author Daniel Tweddell
' Revision Date 10/27/05
'
' To Combat bloat, we are recreating the a new database
'********************************************************
On Error GoTo Err_Function
Dim strNewdb As String
Dim AppNewDb As New Access.Application 'the new database we're creating to manage the updates
strNewdb = CurrentProject.Path & "\db1.mdb"
SysCmd acSysCmdSetStatus, "Creating Database. . ."
With AppNewDb
DeleteFile strNewdb 'make sure it's not already there
.Visible = False 'hear no database see no database
.NewCurrentDatabase strNewdb 'open it
ChangeRemoteProperty "StartupShowDbWindow", AppNewDb, , dbBoolean, False
ChangeRemoteProperty "Auto compact", AppNewDb, , dbBoolean, True
ImportReferences AppNewDb, Application
.CloseCurrentDatabase
End With
Set AppNewDb = Nothing
Dim ao As AccessObject
For Each ao In CurrentData.AllTables
If Left(ao.Name, 4) <> "msys" Then
DoCmd.TransferDatabase acExport, "Microsoft Access", strNewdb, acTable, ao.Name, ao.Name
SysCmd acSysCmdSetStatus, "Exporting " & ao.Name & ". . ."
End If
Next
For Each ao In CurrentData.AllQueries
DoCmd.TransferDatabase acExport, "Microsoft Access", strNewdb, acQuery, ao.Name, ao.Name
SysCmd acSysCmdSetStatus, "Exporting " & ao.Name & ". . ."
Next
For Each ao In CurrentProject.AllForms
DoCmd.TransferDatabase acExport, "Microsoft Access", strNewdb, acForm, ao.Name, ao.Name
SysCmd acSysCmdSetStatus, "Exporting " & ao.Name & ". . ."
Next
For Each ao In CurrentProject.AllReports
DoCmd.TransferDatabase acExport, "Microsoft Access", strNewdb, acReport, ao.Name, ao.Name
SysCmd acSysCmdSetStatus, "Exporting " & ao.Name & ". . ."
Next
For Each ao In CurrentProject.AllMacros
DoCmd.TransferDatabase acExport, "Microsoft Access", strNewdb, acMacro, ao.Name, ao.Name
SysCmd acSysCmdSetStatus, "Exporting " & ao.Name & ". . ."
Next
For Each ao In CurrentProject.AllModules
DoCmd.TransferDatabase acExport, "Microsoft Access", strNewdb, acModule, ao.Name, ao.Name
SysCmd acSysCmdSetStatus, "Exporting " & ao.Name & ". . ."
Next
MsgBox "Creation Complete!" & vbCrLf & "Reset Password", vbExclamation, "New Database"
Exit Sub
Err_Function:
ErrHandler Err.Number, Err.Description, Me.Name & " cmdCreateDuplicate_Click()"
End Sub
Function DeleteFile(ByVal strPathAndFile As String) As Boolean
'***********************************************************************************
' Author Daniel Tweddell
' Revision Date 04/14/03
'
' Deletes a file
'***********************************************************************************
On Error GoTo Err_Function
DeleteFile = True 'default to true
If UncDir(strPathAndFile) <> "" Then 'make sure the file is there
Kill strPathAndFile 'delete a file
End If
Exit Function
Err_Function:
ErrHandler Err.Number, Err.Description, "DeleteFile()", bSilent
DeleteFile = False 'if there is a problem, false
End Function
Public Sub ChangeRemoteProperty(strPropName As String, _
appToDB As Access.Application, Optional appFromDB As Access.Application, _
Optional vPropType As Variant, Optional vPropValue As Variant)
'********************************************************************************
' Author Daniel Tweddell
' Revision Date 01/13/04
'
' Changes/adds a database property in one db to match another
'********************************************************************************
On Error GoTo Err_Function
Dim ToDB As DAO.Database
Dim FromDB As DAO.Database
Dim prpTest As DAO.Property
Dim bPropertyExists As Boolean
Set ToDB = appToDB.CurrentDb
If Not appFromDB Is Nothing Then Set FromDB = appFromDB.CurrentDb
bPropertyExists = False 'flag to see if we found the property
For Each prpTest In ToDB.Properties 'first see if the property exists so we don't error
If prpTest.Name = strPropName Then
If IsMissing(vPropValue) Then vPropValue = FromDB.Properties(strPropName) 'in case we want to assign it a specific value
ToDB.Properties(strPropName) = vPropValue 'if it does set it and get out or the loop
bPropertyExists = True
Exit For
End If
Next
If Not bPropertyExists Then ' Property not found.
Dim prpChange As DAO.Property
If IsMissing(vPropValue) Then
With FromDB.Properties(strPropName)
vPropValue = .Value 'in case we want to assign it a specific value
vPropType = .Type
End With
End If
Set prpChange = ToDB.CreateProperty(strPropName, vPropType, vPropValue) 'add it
ToDB.Properties.Append prpChange
End If
Exit Sub
Err_Function:
ErrHandler Err.Number, Err.Description, "ChangeRemoteProperty()", bSilent
End Sub
Public Sub ImportReferences(AppNewDb As Access.Application, appUpdateDB As Access.Application, Optional iStatus As Integer)
'********************************************************************************
' Author Daniel Tweddell
' Revision Date 01/13/04
'
' Copies the current references from the one database to another we're building
'********************************************************************************
On Error GoTo Err_Function
Dim rNewRef As Reference
Dim rUpdateRef As Reference
Dim bReferenceExists As Boolean
Dim rToAdd As Reference
Dim sReference As String
If iStatus <> 0 Then ProgressBarUpdate iStatus, "Referencing Visual Basic Libraries. . ."
For Each rUpdateRef In appUpdateDB.References
bReferenceExists = False
For Each rNewRef In AppNewDb.References
sReference = rNewRef.Name
If rUpdateRef.Name = sReference Then
bReferenceExists = True
Exit For
End If
Next
If Not bReferenceExists Then
With rUpdateRef
Set rToAdd = AppNewDb.References.AddFromGuid(.Guid, .Major, .Minor)
End With
End If
Next
Exit Sub
Err_Function:
ErrHandler Err.Number, Err.Description, "ImportReferences(" & sReference & ")", bSilent
Resume Next
End Sub
I have found that combo boxes with 10 or more columns can cause an Access form to corrupt. Try reducing the number of the columns or removing that combo box to see if the form saves properly. This problem is was related to working in Win 7 64 bit operating system with Access 2003 databases. There was no problem when developing in XP in other words the forms save fine with large column counts in combo boxes. Hope this information helps since it caused a lot of wasted time thinking the database was corrupted.