Access Run-time Error 2475 *occasionally* occuring - ms-access

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

Related

MS Access Library Template - Reference Category Value in VBA

Completely new to MS Access - I'm trying to do a simple modification to the MS Access Lending Library template using VBA.
Going into the Asset List form and clicking New Asset pops up the Asset Details form, where I can click the Save and New button. This calls the VBA code cmdSaveandNew_Click().
My question is, within this VBA method, how do I reference the values of the fields in the Asset Details form - For example getting the value that the user entered in the Category field and storing it in a variable.
I've tried several strings of code to try to get the value back, but I keep getting various errors, saying it can't find the referenced form, or Object Required, or this method is supported. I think it's likely just that I don't fully understand the structure of what I'm trying to reference or the syntax for specifying how to reference it.
Right now (after I've been trying many different combinations of things, I have:
text = Forms![Asset List].[Asset Details].Form.Category
which is giving me the "can't find the field '|1' referred to in your expression" error.
Edit - code currently looks like this:
Private Sub cmdSaveandNew_Click()
'On Error GoTo cmdSaveandNew_Click_Err
Dim someVar As String
Dim num As Integer
Dim objtf
'objtf = Forms![Asset List].[Asset Details].Form.Category
objtf = Me.Category
'num = Form.Controls.Count
Msg.Box (TypeName(objtf))
someVar = InputBox("How many?")
On Error Resume Next
If (Form.Dirty) Then
DoCmd.RunCommand acCmdSaveRecord
End If
If (MacroError.Number <> 0) Then
Beep
MsgBox MacroError.Description, vbOKOnly, ""
Exit Sub
End If
On Error GoTo 0
DoCmd.GoToRecord , "", acNewRec
DoCmd.GoToControl "Item"
MsgBox someVar
cmdSaveandNew_Click_Exit:
Exit Sub
cmdSaveandNew_Click_Err:
MsgBox Error$
Resume cmdSaveandNew_Click_Exit
End Sub
The variable must be declared global in a general module if you want to reference from various modules. Then setting it by code behind the Asset Details form would be simply: SomeVariableName = Me.Category.
Don't use reserved words as names - text is a reserved word.

Getting a more useful/accurate error message

We have an issue with an Access front-end that seemingly randomly but constantly times out users with no explanation. This has been a long-standing issue, each time the disconnect happens we're provided with the sub that's causing the issue and a generic error message.
I'm starting to think the error message is misleading, it gives a generic "Connection Failure" with an Error Code of 0. After Googling the code that apparently means no error occurred?
I use C# not VBA so I'm not familiar with it but the error handling is as follows:
Private Sub Form_Timer()
Dim blnSystemMaintenance As Boolean
On Error GoTo ErrHand
' Check for System Maintenance.
blnSystemMaintenance = DLookup("SystemMaintenance", "ConfigItems")
If blnSystemMaintenance Then
//Do stuff
Else
//Do stuff
End If
//Do stuff
End If
ErrExit:
Exit Sub
Resume
ErrHand:
' If Err.Number = 1 Then
' Else
MsgBox "Error number: " & Err.Number & vbCrLf & _
"Error description: " & Err.Description, vbCritical, _
"Unexpected Error Occurred in Sub: Form_Timer"
Resume ErrExit
' End If
End Sub
Is there a way to get a more helpful error message?
Domain aggregates often don't give descriptive error messages. I'm assuming your database runs into problems connecting to the backend when running the domain aggregate.
You can easily replace your domain aggregate with a recordset call:
blnSystemMaintenance = CurrentDb.OpenRecordset("SELECT SystemMaintenance FROM ConfigItems")(0).Value
Often, those give a bit more descriptive errors, if the error occurs at all when using a recordset.
Sometimes error messages may be misleading for some type of functions like DLookup, this is a system problem, but sometimes error messages are misleading because not all procedures have correct error handlers and it's difficult to localize the problem especially if the developer has no direct access to the application. So, correct error handlers may significantly simplify troubleshooting.
Normally I use two types of error handlers, which give me quite clear diagnostic messages. Most common - usual error handler for procedures, which may interact with the user and normally called by the system - events handlers:
Private Sub Frame2_Click()
On Error GoTo ErrorHandler
'code here
ExitHere:
On Error Resume Next
Exit Sub
Resume '>> remove in release
ErrorHandler:
MsgBox "Error " & err.Number & " (" & err.Description & "), Line " & Erl & " in procedure Frame2_Click of Form_Form1", vbExclamation + vbMsgBoxHelpButton, "Error " & Now()
Resume ExitHere
End Sub
The second type of handlers - for procedures, which may be called only by other VBA procedures, "bubble" handler, which adds information about the error to error description and passes the error up to caller procedure:
Sub MySub()
On Error GoTo ErrorHandler
'code here
ExitHere:
Exit Sub
Resume '>> remove in release
ErrorHandler:
Debug.Assert Not (STOP_AT_ERROR And IS_DEV) '>> remove in release
err.Raise err.Number, "MySub of Form_Form1", err.Description & vbCrLf & "in MySub of Form_Form1 at " & Erl
End Sub
It allows to show full call stack with error lines and procedure names. Line numbers should be added to each line of code though.
For debug simplifying I use two constants: if STOP_AT_ERROR is True and IS_DEV is True, code execution stops right after the error without bubbling and allows to inspect variables. For inspecting, I move the code execution pointer to the line Resume using Ctrl-F9, then press F8 and code pointer moves to the row, which caused the error.
In the production or QA versions just change IS_DEV to False and code won't stop at Debug.Assert line.
For automatic generating error handlers and adding line numbers, I use third party software, for instance, MZ-Tools. It can be done manually, but it's quite a time consuming

How to reduce repeated VBA code

I have an identical AfterUpdate event for 11 fields in my form. Is there any way to put the AfterUpdate code in a function or module and then call the function or module in each AfterUpdate event?
If a certain field is changed on the main form, I prompt the user with a message box saying they need to add a note in the subform describing the change. If the user clicks OK on the message box, it brings the focus to the note subform. If the user clicks Cancel, the change is undone in the main form.
Below is the code that is repeated for 11 fields:
Private Sub ID_AfterUpdate()
On Error GoTo StandardErrorHandling
Dim NoteReq As Integer
NoteReq = MsgBox("A change has been made to a protected field! " & _
"Please provide a note with a brief description of the change." _
, vbOKCancel, "A Note is Required!")
If Form.Dirty = True Then
If NoteReq = vbCancel Then
Me.Undo
ElseIf NoteReq = vbOK Then
'You may have to set focus to the subform then the control
[Forms]![MainForm].[SubFormNotes].SetFocus
DoCmd.GoToRecord , , acNewRec 'Go to new note record, not existing one
End If
End If
ExitStandardErrorHandling:
Exit Sub
StandardErrorHandling:
MsgBox "The following error has occured:" & vbCrLf & vbCrLf & _
"Error Number: " & Err.Number & vbCrLf & vbCrLf & _
"Error Description: " & Err.Description & vbCrLf & vbCrLf _
, vbCritical, "An Error has Occurred!"
Resume ExitStandardErrorHandling
End Sub
I know a function returns a value so if I put the above code in a function, and called the function within the AfterUpdate event of each field, how would I code it so that it returns a value? Or is there another way to cut down on the redundant code? Thanks!
Functions don't seem to need to return a value in my experience. I've made functions to do exactly this sort of thing - highlight a textbox by changing the BackColor when selected, make several buttons do a set process to another control that shares a tag, swap values between Active and Previous controls etc.
You should be able to create a function directly from that coding simply by replacing the first line with something along the lines of Function UpdateNote() and the last line with End Function, and then do a find and replace "sub" with "function"
Then you can call that function simply by putting exactly =UpdateNote() (or whatever you call the funtion) in the After Update event property (directly in the property list, not vba or macro).
You can even apply it to all the different field's properties at once, by selecting all 11 of them and pasting that =UpdateNote() in the After Update event property.
It definitely works, and is pretty neat and clean in my opinion. If there's a reason it's not ideal, I'm not aware of it...

Issue when using a DAO handle when the database closes unexpectedly

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.

Multi-Page vs Multi-PDF Loop problems

I have a form that contains multiple partners that share a "pool". The partners are listed on a subform. After I'm done entering the information, I want a button to run a report for each of the partners with their specific information. But since it's multiple partners and I need one report for each (which I then want to email), I want to use a Loop to go through each of the partners.
EDIT1: Added entire code for review. I do have Option Explicit in and I have compiled it as well.
Private Sub btn_Run_Click()
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim strSQL As String
strSQL = "Select * FROM Cobind_qryReport WHERE PartPoolName = """ & Me.TopLvlPoolName & """"
Debug.Print "strSQL: " & strSQL
Set db = CurrentDb
Set rs = db.OpenRecordset(strSQL)
On Error GoTo Err_PO_Click
If MsgBox("Do you wish to issue the cobind invites?", vbYesNo + vbQuestion, "Confirmation Required") = vbYes Then
rs.MoveFirst
Do While rs.EOF = False
DoCmd.OutputTo acOutputReport, "Cobind_rptMain", acFormatPDF,_
"K:\OB MS Admin\Postage\CoBind Opportunities\Sent Invites\" _
& rs!CatCode & "_" & rs!PartPoolName "Cobind Invite_" & _
Format(Now(), "mmddyy") & ".pdf"
DoCmd.SendObject acSendReport, "Cobind_rptMain", acFormatPDF, ,_
, , " Cobind Invite", "Please find the cobind invite attached._
Response is needed by " & [RSVP] & ". Thank you.", True
rs.MoveNext
Loop
End If
Exit_PO_Click:
MsgBox ("It didn't work")
rs.Close
Set rs = Nothing
Set db = Nothing
Exit Sub
Err_PO_Click:
MsgBox Err.Description
Resume Exit_PO_Click
End Sub
This should allow me to create a report for each record in my query, save it to my server, then open an email to send it out. Right now, it appears that the [PartPoolName] is hanging up the code because I'm getting a "Microsoft Office Access can't find the field "|" referred to in your expression." If I take out the [PartPoolName], it'll create a PDF with four pages (each page showing a partner), where I want to end up with four separate PDFs.
The first thing you should do is add Option Explicit to the Declarations section of your module.
Then, from the Visual Basic editor's main menu, select Debug->Compile [your project name here]
Fix all the problems the compiler complains about. I suspect one of the compiler's first complaints may be triggered by this section of your code:
rs.MoveFirst
Do While Recordset.EOF = False
Do you have two recordset objects open, or one?
After you fix everything the compiler complains about, try your revised code.
If you get runtime errors, show us the exact error message and which code line is highlighted.
If the part of your code you haven't shown us includes an error hander, you can disable that error handler like so:
'On Error GoTo Err_PO_Click
Error handlers are great for production to shield users from errors. However, during development you really need to be able to identify which code line causes the error.
Alternatively, you can leave your error handler active and select Tools->Options from the editor's main menu. In the Options dialog, select the General tab, then select the "Break on All Errors" radio button and click OK. You can switch that option back to "Break on Unhandled Errors" after you finish your testing.
Update: You wrote: Right now, it appears that the [PartPoolName] is hanging up the code because I'm getting a "Microsoft Office Access can't find the field "|" referred to in your expression."
What is [PartPoolName]? If it's a field in the recordset, you can reference its value as rs!PartPoolName If it's something else, perhaps a global variable, give us more information about it.
Update2: Whenever your current code completes without error, you will hit this:
Exit_PO_Click:
MsgBox ("It didn't work")
Can that be right?
Update3: This OutputTo statement is your issue now, right?
DoCmd.OutputTo acOutputReport, "Cobind_rptMain", acFormatPDF,_
"K:\OB MS Admin\Postage\CoBind Opportunities\Sent Invites\" & _
"Cobind Invite_" & Format(Now(), "mmddyy") & ".pdf"
Cobind_rptMain is a report. It has a RowSource for its data. You're calling OutputTo with that report 4 times (once for each of the 4 rows in the recordset). Yet you expect 4 different versions of that report ... a separate report for each PartPoolName value?
To finish off the fine work by HansUp visit a page on how to print a report for a single record and how to generate reports to attach to emails. See the Emailing reports as attachments from Microsoft Access page.