MS Access - "phantom" process created by canceling report - vba

I have encountered a problem in Access 2010 for Windows:
Launch a report using doCmd.OpenReport
Set "Cancel = True" in an event associated with the report
Close Access
Access continues to run as a "phantom" process and must be killed in Task Manager. Access cannot open another database until this process is killed. The task initially shows some CPU utilization but quiets down to 0%.
I discovered this while using doCmd.OpenReport to launch a report. The report opens a form in the Report_Open event. The form prompts the user for report parameters and allows them to press "OK" to display the report or press "Cancel". The On Click event for "Cancel" sets "Cancel = True".
It appears that OpenReport() is not responding gracefully to the cancel. This seems like a frequently used technique so I hesitate to call it a bug and am wondering if I am doing something wrong. Actually... the more I explain this, it does sound like a bug. At this point, I am hoping somebody has a workaround or that I am missing something obvious.
This is just a simplified example I created to illustrate the problem:
form
Private Sub Form_Open(Cancel As Integer)
On Error GoTo errHandler
DoCmd.OpenReport "Test Report", acViewPreview, , , acDialog
Exit Sub
errHandler:
Select Case Err.Number
Case 2501 ' Cancelled by user, or by NoData event.
MsgBox "Report cancelled, or no matching data.", vbInformation, "Information"
Case Else
MsgBox "Error " & Err & ": " & Error$, vbInformation, "Form_Open()"
End Select
Resume Next
End Sub
report
Private Sub Report_Open(Cancel As Integer)
Cancel = True
End Sub

The reason for the problem in this case is acDialog:
DoCmd.OpenReport "Test Report", acViewPreview, , , acDialog
I think you will find:
DoCmd.OpenReport "Test Report", acViewPreview
Works without problems.
Edit re Comment
You should not need to cancel the report. For the most part, it is better to avoid errors than to trap them, so check for data and get parameters before opening the form. Move the form from the report open event to step of its own and use DLookUp or a query to check for data before launching the form.

I'm not in a position to test this problem, so I'll just offer a generic solution: Remove the data/recordset/recordsource from the report definition.
You can apply the recordsource to the report after you have determined the parameters. If the report is canceled, it is canceled without ever having a data connection.

Related

VBA conditional formatting not visible in Print Preview

I am trying to apply conditional formatting to a report using VBA. This works if I open the report in Report View, but does not work if I open the report in Print Preview (unless I first open in Report View).
This issue does not apply to conditional formatting added via the wizard. That displays correctly in Print Preview.
I am using Office365 with Access Version 2208
My setup uses a form to allow a user to enter a date. This date is then passed to the report as an OpenArg:
Private Sub cmd_Ok_Click()
'DoCmd.OpenReport "Component Value", acViewPreview, , , , Me.txt_Date <- Does not show conditional formatting
DoCmd.OpenReport "Component Value", acViewReport, , , , Me.txt_Date
DoCmd.Close acForm, Me.Name, acSaveNo
End Sub
Then the report adds a format condition in its OnLoad event
Dim strCondDate As String
Private Sub Report_Load()
If Not IsNull(Me.OpenArgs) Then
strCondDate = Me.OpenArgs
End If
If Not IsNull(strCondDate) Then
With Me.txt_Updated.FormatConditions
With .Add(acExpression, , "[Last Updated] < #" & strCondDate & "#")
.BackColor = vbYellow
End With
End With
End If
End Sub
I can breakpoint this code and see that it runs correctly when I open the report with acViewPreview, but the formatting is not visible.
Why does the format condition not appear in Print Preview (unless I first open the report in Report View)?
How can I get the formatting to be displayed?
Conclusion is Conditional Formatting rule cannot be programmatically added to report opened directly to Print or PrintPreview but can to ReportView. Fortunately, there is no need to use VBA. CF rule can grab OpenArgs content: Field Value Is less than CDate([OpenArgs]). Otherwise, have rule reference control on form that has user input.
Another alternative is to directly set Backcolor property in OnFormat or OnPrint event with conditional VBA structure. Drawback is these events do not execute in ReportView.

Access Crashes When Using VBA To Set Bound Combobox RowSource

I've asked this on UA but wanted to post here as well in the hope someone can help me figure this out. Also, I'm aware of the evils of using MVFs; this is just something that I want to figure out.
I have a form with a combobox that is bound to a multivalue field. I want to use VBA to set the RowSource of the combobox. (Specifically, I want to use the value of another combobox on the form to filter the available choices in the second combobox, but right now I'm just trying to get the RowSource update to work with a static SQL statment.)
If I set the RowSource in the form's Design Mode, then it works as expected. But as soon as I try to use the exact same value to set the RowSource in VBA, Access crashes. Here is the code on the After Update event of the 'parent' combobox:
Private Sub cboSelRegSet_AfterUpdate()
Dim strSQL As String
On Error GoTo Query_Error
strSQL = "SELECT tblRegulation.ID, tblRegulation.RegulationCode " _
& "FROM tblRegulation " _
& "ORDER BY tblRegulation.RegulationCode;"
MsgBox "Before Filter", vbOKOnly
Me.cboSelReg.RowSource = strSQL
MsgBox "After Filter", vbOKOnly
Me.cboSelReg.Requery
MsgBox "After Requery", vbOKOnly
ExitNow:
Exit Sub
Query_Error:
MsgBox Err.Description
Resume ExitNow
End Sub
If I make "cboSelReg" unbound, then the update works, but since it is no longer bound to an MVF, the user can only select one value which defeats the purpose.
I've also added msgbox prompts to step through the code and it seems to get through the entire subroutine since I get the prompt after the requery. But as soon as I click OK, Access crashes.
Is there a way to make this work?

Syntax Error (Missing operator) in query expression '[ProductName] = Product1' when opening a form from a search report

I am building a simple database and I want to have a report open when a result from a search report is clicked on.
The VBA code is as follows:
Private Sub Product_name_Click()
DoCmd.OpenReport "Product Table", acViewReport, , "[ProductName]= " & Me![ProductName]
DoCmd.Close acReport, "KeywordSearchReport", acSaveNo
End Sub
I get the error message in the title. It is correctly getting the value for Me![ProductName] and [ProductName] is a field in the underlying table. I have more or less this same code for opening a report after submitting a form and it works fine.

User level security?

I have a table that has usernames, passwords, and a yes/no column for isadmin.
How do I make it so if they login with an account that has a check mark under "isadmin" they get access to design view, the ribbon, etc? Though if they log in with an account that doesn't have a check mark under the isadmin box they only can view the forms, not edit them, and the ribbon is inaccessible?
I just don't know where to start, as I had assumed there was a way to save the database as a seperate copy that only users can view forms in, and if the admin runs his copy he gets all the changes to the tables (via the forms) the users made. So when the admin edits a form, and saves it it doesn't remove all the user's data as when it was saved, it was saved to the admin's copy too. I'm really confused.
I am using Access 2013
This is a simple solution for user level security being removed in newer releases of Access; using a lot of VBA.
STEP 1: Creating The Table
First, create a table. I will name mine LogininfoT. Now, for the columns inside of the table, name them EmployeeID, LoginID, LoginPassword, EmployeeName, and lastly IsAdmin. Make EmployeeID your key, and IsAdmin a YES/NO field.
For testing, add two users to this table. With this information:
EmployeeID LoginID LoginPassword EmployeeName IsAdmin
1 1111 1234 Bob [x]
2 2222 1234 Stewert [ ]
STEP 2: Creating The Forms
Now that we have the table made, let's design the form to use this set of data.
I will name my form LoginF. Go into design view, and slap down a text box, a combo box, and a button. For the combo box rename the text to say something like Login ID (you can change this to whatever fits your need) and for the text box, put the text as Password (once again, change this to whatever you want it doesn't effect the outcome). The text in the button can be whatever you want, I will be putting Login on it.
Click the combo box and rename it. I will be naming it LoginCmBx. Next, click the text box and rename it, I will be naming it PasswordTxt. Lastly, click the button and rename it, I will be naming it LoginBtn.
Click the combo box again and under the event tab, go into the After Update scripting. Use code and type this in:
Private Sub LoginCmBx_AfterUpdate()
Me.PasswordTxt.SetFocus
End Sub
This makes it so after you select a username, it automatically puts the focus onto the password text box so you can start typing right away without using TAB on your keyboard, or using your mouse.
Next, go to the button and under the event tab, go into the On Click scripting. Use code and type this in:
Private Sub LoginBtn_Click()
If IsNull(Me.LoginCmBx) Or Me.LoginCmBx = "" Then
MsgBox "You must enter a User Name.", vbOKOnly, "Required Data"
Me.LoginCmBx.SetFocus
Exit Sub
End If
If IsNull(Me.PasswordTxt) Or Me.PasswordTxt = "" Then
MsgBox "You must enter a Password.", vbOKOnly, "Required Data"
Me.PasswordTxt.SetFocus
Exit Sub
End If
If Me.PasswordTxt.Value = DLookup("LoginPassword", "LoginInfoT", _
"[EmployeeID]=" & Me.LoginCmBx.Value) Then
EmployeeID = Me.LoginCmBx.Value
On Error Resume Next
DoCmd.DeleteObject acQuery, "IsAdminQ"
On Error GoTo Err_LoginBtn_Click
Dim qdef As DAO.QueryDef
Set qdef = CurrentDb.CreateQueryDef("IsAdminQ", _
"SELECT IsAdmin " & _
"FROM LoginInfoT " & _
"WHERE EmployeeID = " & LoginCmBx.Value)
Exit_LoginBtn_Click:
DoCmd.Close acForm, "LoginF", acSaveNo
DoCmd.OpenForm "MenuF"
Exit Sub
Err_LoginBtn_Click:
MsgBox Err.Description
Resume Exit_LoginBtn_Click
Else
MsgBox "Password Invalid. Please Try Again", vbOKOnly, _
"Invalid Entry!"
Me.PasswordTxt.SetFocus
End If
End Sub
What this does is check if you selected a username, if not it spits out an error telling the user to select one. If you did, it checks if you entered a password. If they didn't, it spits out another error saying they didn't enter a password. If they selected both, and the password doesn't match the one in the table for the username you selected it spits out an error saying you got the password wrong. If you got the password right to the username you selected, it logs you in. It will then close the current form you are on, and open up a new one named "MenuF" it will also create a query with that little bit of information under the username you selected, either if it's an admin or not.. We haven't created MenuF yet, so lets quickly do that. We aren't done with LoginF just quite yet though, so be prepared to come back to that later!
Create the form, and put down a button. Here is your menu form, you can create as many buttons as you want going to other forms or even just put a subform on here and have your entire database. Taht button you put down, you can name the text to whatever you want. I put mine as Log out. Name the button MenuLogOutBtn. Go into the event tab, and under the On Click scripting click code and type this in:
Private Sub MenuLogOutBtn_Click()
DoCmd.DeleteObject acQuery, "IsAdminQ"
DoCmd.OpenForm "LoginF"
DoCmd.Close acForm, "MenuF", acSaveNo
End Sub
What this does is delete the query the login button created, opens the login form again, and closes the menu. Simple!
Now I need you to throw down a checkbox, and name it MyCheckbox. This box requires no special coding, or control sources. Though I do suggest changing visible as no, and deleting the text that comes along with it.
Now, go to the form's event properties and under the Open scripting go to code and type this in:
Private Sub Form_Open(Cancel As Integer)
Me.MyCheckbox.Value = GetLoginStateIsAdmin()
If GetLoginStateIsAdmin = True Then
Me.ShortcutMenu = True
DoCmd.ShowToolbar "Ribbon", acToolbarYes
DoCmd.ShowToolbar "Menu Bar", acToolbarYes
Application.SetOption "ShowWindowsinTaskbar", True
DoCmd.SelectObject acTable, , True
Else
Me.ShortcutMenu = False
DoCmd.ShowToolbar "Ribbon", acToolbarNo
DoCmd.ShowToolbar "Menu Bar", acToolbarNo
Application.SetOption "ShowWindowsinTaskbar", False
DoCmd.NavigateTo "acNavigationCategoryObjectType"
DoCmd.RunCommand acCmdWindowHide
End If
End Sub
What this does is checkbox's information which is attached to query's IsAdmin column and give GetLoginStateIsAdmin that boolean variable. After it does that, it starts a simple If statement that turns off menu bars and disabled right click if you aren't an admin; if you are, it allows you do right click and all menu bars are visible.
Though if you didn't notice yet, our checkbox doesn't get the information from the query yet! Oh no!
STEP 3: Creating The Public Modules
If you were on your toes, you would notice even the login code wouldn't work at this point. First, we need some public modules. Go to the Create tab in the ribbon, and create a module. Type this in:
Public EmployeeID As Long
Save this module as LoginModule.
Create another module, and type this in:
Function GetLoginStateIsAdmin()
'
Dim rst As DAO.Recordset
Set rst = CurrentDb.OpenRecordset("IsAdminQ")
GetLoginStateIsAdmin = Nz(rst(0), False)
Set rst = Nothing
'
End Function
Save this one as GetAdmin.
Lets create one more module; so the user opening the database can't by bass stuff by using the shift key to launch it.
Type this in it:
Function ap_DisableShift()
'This function disable the shift at startup. This action causes
'the Autoexec macro and Startup properties to always be executed.
On Error GoTo errDisableShift
Dim db As DAO.Database
Dim prop As DAO.Property
Const conPropNotFound = 3270
Set db = CurrentDb()
'This next line disables the shift key on startup.
db.Properties("AllowByPassKey") = False
'The function is successful.
Exit Function
errDisableShift:
'The first part of this error routine creates the "AllowByPassKey
'property if it does not exist.
If Err = conPropNotFound Then
Set prop = db.CreateProperty("AllowByPassKey", _
dbBoolean, False)
db.Properties.Append prop
Resume Next
Else
MsgBox "Function 'ap_DisableShift' did not complete successfully."
Exit Function
End If
End Function
Save that as ShiftModule.
We are done the modules! Lets go back to the LoginF now.
STEP 4: Finishing Up LoginF
Go to the form's event tab, and click the on load scripting. Click code, then type this in:
Private Sub Form_Load()
On Error Resume Next
DoCmd.DeleteObject acQuery, "CustomerMoreInfoQ"
End Sub
What this does is make sure that the query the login button creates is deleted when this form starts up, just in case the user closes the database without logging out. So if you click login, it won't cause errors because the query isn't still there.
STEP 5: Testing It Out.
Run the form LoginF in form view, and select Bob as the username. Type in the password 1234 into the password text box, and click login. It should open up the MenuF and you see all menus and you can right click. Good. Now, log out and login with Stewert, using the same password. Now you see all the menus remove themselves, and you can't right click! Yay!
For extra security, in the LoginF's Other tab, make sure Shortcut Menu is set to No. This will set right click to be disabled always; as you aren't logged in as a user at this point. It doesn't know if you are an admin or not.
STEP 6: Disabling The Toolbars On Start Up & launching LoginF On Start Up.
Go to File > Options > Current Database.
Under Display Form, select FormF.
Under the Navigation section, unclick Display Navigation Pane.
Click okay, then go back to LoginF; go into the On Load code and add this just before the End Sub:
DoCmd.ShowToolbar "Ribbon", acToolbarNo
You are done! Save your database, then close it and open it again. It should load the LoginF form where you can't right click, there are no menus etc. The only way to get the menus to edit things is to log into an admin account!
Step 7: Expanding
This doesn't automatically expand the more you add forms though. You need to add that checkbox named MyCheckbox (I suggest copy + pasting it) to each form you add, and add this code to each form you add:
Private Sub Form_Open(Cancel As Integer)
Me.MyCheckbox.Value = GetLoginStateIsAdmin()
If GetLoginStateIsAdmin = True Then
Me.ShortcutMenu = True
Else
Me.ShortcutMenu = False
End If
End Sub
Though once you do that to every form, the security works and you need to log in to an admin account to change anything. If you are just a user, you can use the form normally (click buttons, edit data on subforms, etc) You can't edit the form it self though.

Microsoft Access 2007 Lags after Print Job

I am new to Microsoft Access.
I added some queries to an old and existing database (.accdb) using Microsoft Access 2007.
I created two forms in Access, and two buttons in the Switchboard.
Whenever the buttons are clicked, I load the form which was created using the SQL query through the Form Wizard.
However, now whenever I print, the particular form which has the print button using the following old existing code.
Private Sub CmdPrintInv_Click()
On Error GoTo Err_CmdPrintInv_Click
Dim stDocName As String
Dim sqlstr As String
sqlstr = "InvType='" & InvType.Value & "' AND InvNo='" & InvNo & "'"
stDocName = "R-Invoice"
DoCmd.OpenReport stDocName, acNormal, , sqlstr
Exit_CmdPrintInv_Click:
Exit Sub
Err_CmdPrintInv_Click:
MsgBox Err.Description
Resume Exit_CmdPrintInv_Click
End Sub
Previously, the database was working fine after clicking print. The other buttons were responsive.
Now, the database lags, particularly the window attached. Whenever I try to click other buttons, they are not responsive.
Any advise would help me out.