Great Access Tools – Find and Replace
Export\Salvage Your AWA Data
Setting Up an MS Access Database
Access x32 vs x64 Compatibility
Access Best Practices and Troubleshooting Steps
I thought I’d make a quick post to go over some basic Access development best practices and Troubleshooting steps that any developer should be aware of.
MS Access Development Best Practices
So you want to start developing an MS Access database! The following is based on my personal experience developing databases for over 15 years now. That said, a few (2 or 3) of the items below are debatable, but most are steadfast rules.
General
- Implement everything I covered in my article entitled Setting Up an MS Access Database
- Split your database (I’d suggest you do this early on in development)
- Add a Persistent Connection between your front-end and back-end at the startup of your application
- Always develop using the oldest version of Access that will be used to run your database
- etc…
- Make sure to always develop on a local copy of the database. Never develop on a network copy
- Save regularly
- Backup, backup … did I mention backup!
- Deploy an ACCDE version of your database to your end-users rather than the source ACCDB
- I recommend setting up a table(s) in both the FE and BE to keep track of your database version history and the changes that occurred in each version
Access Settings and Properties
- You should not use the Compact on Close property
- Always disable the Track name AutoCorrect info property
Tables
- Set all your tables’ Subdatasheet Name to [None]
- Never use Lookups
- Never use the Attachments data type
- Never use the Hyperlink data type
- Never use special characters when naming fields (this includes avoiding the use of spaces). You are best to use CamelCase or another well known technique
- Never use any Reserved Words when naming fields
- Do not use Input Masks
- Take the time to properly index your table fields
- Ensure to set the Required property for your table fields where required
- Take the time to setup Relationships between your tables
Forms/Reports
- Never use special characters when naming forms/reports and their controls (this includes avoiding the use of spaces). You are best to use CamelCase or another well known technique
- Never use any Reserved Words when naming forms/reports and controls
- Give your controls meaningful names, do not use the default names that Access creates (ie: command1, text1, …)!
- Do not use Input Masks
- Avoid the use of any and all ActiveX Controls
- If you’re looking to embed images within, please take the time to resize the image files prior to using them. There is no point using a full resolution image 3442x2559px if you’re just going to use it as a 32x32px icon on a button! This leads to database bloat and impacts database/object load times …
- When reusing images within multiple objects, say a logo, remember to either set the Picture Type to Shared, or my preferred method, is to create a form/report and insert the image and then insert the form/report as a subform/subreport wherever the image is required. This also mean I only have one object to edit to update all my dependent objects and this technique is backwards compatible with mdb format database unlike the Picture Type approach which is only available in accdb database! (and yes people still use mdb databases!)
VBA
- Always include the Option Explicit declaration at the top of all of your VBA modules
- Compile regularly and save
- Do not make VBA changes to forms/Reports unless in Design mode
- Never use any Reserved Words when naming variables
MS Access Troubleshooting Steps
Below are some basic troubleshooting steps that you can try when attempting figure out issues with a database.
- Perform a Compact and Repair
- Restart Access
- Shutdown and restart your PC, not hibernate or sleep, but rather a proper shutdown.
- If you use VBA code
- Ensure there are no MISSING references (Tools -> References…)
- Decompile, Compact and Repair, Compile, Compact and Repair
- Create a new blank db and then import everything into it
- Update Access/Office to ensure you have all the latest fixes/patched/…
- Do not rely on Windows Update as it does not do this!
- Perform an Office Repair through the Windows Control Panel
- (This is only for extreme cases) Perform a complete uninstall of office using Option 2 from Uninstall Office 2016, Office 2013, or Office 365 from a PC and then reinstall it.
- Backup everything first
dBASE Support Restored to Access 2016 MSI
The MS Access Development Team was proud to announce that dBASE support has been restored to Access 2016 MSI installations. DBase is finally back!
What Do You Need To Do
Simple, download and install both of the following updates:
- May 2, 2017, update for Access 2016 (KB3178700)
- This one updates Access
- May 2, 2017, update for Office 2016 (KB3115501)
- This one updates ACE
and obviously, do not forget to do the same on every computer!
Access Database File Compatibility Table
Access has always maintained an exceptional backwards compatibility between version, but as we move forward and new versions are released some of the older versions are being left behind. Now I’m not going defend why this is happening, but I did want to try and create a simple list to easily know what version is compatible with which version. This is what I’ve put together thus far.
Version | Compatible Up To |
Access 2.0 | Access 2003 |
Access 95 | Access 2007 |
Access 97 | Access 2010 |
Access 2000 | Access 2016 |
Access XP (2002) | Access 2016 |
Access 2003 | Access 2016 |
So for example, if you had an Access 2.0 mdb and wanted to upgrade it to be able to run it on Access 2016 you’d need to use Access 2003 to upgrade the file format to Access 2002-2003 mdb format and then you could use Access 2016 to open the mdb and/or convert it.
Some Useful References:
Access 2.0 -> Import an Access 2.0 database into an Access 2007 file
Access 95 -> Import an Access 95 database into an Access 2007 file
Access 97 -> Convert a database to the .accdb file format
Access 2000, 2002 & 2003 -> Convert a database to the .accdb file format
MS Access – Find Macros Using a Search Term
To continue my original post, entitled MS Access – Find Embedded Macros, regarding identifying where Embedded Macro were being used within an Access database, I also develop the following procedure that enable one to search through all Embedded Macros and Standard Macros for a search term. The search term can be anything, object names, commands, segments of words, …
I originally was using this to identify where Forms were being called since I needed to rename them.
'---------------------------------------------------------------------------------------
' Procedure : FindTermInMacros
' Author : Daniel Pineault, CARDA Consultants Inc.
' Website : http://www.cardaconsultants.com
' Purpose : Search through Form and Report Embedded Macros and standard Macros for
' a given term
' The search results are printed to the immediate window
' Copyright : The following may be altered and reused as you wish so long as the
' copyright notice is left unchanged (including Author, Website and
' Copyright). It may not be sold/resold or reposted on other sites (links
' back to this site are allowed).
' Req'd Refs: Uses Late Binding, so none required
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sSearchTerm The term to look form
'
' Usage:
' ~~~~~~
' Call FindTermInMacros("Form1")
' Call FindTermInMacros("SetTempVar")
'
' Revision History:
' Rev Date(yyyy/mm/dd) Description
' **************************************************************************************
' 1 2017-01-07 Initial Release
' 2 2017-05-22 Added search of standard Macros
'---------------------------------------------------------------------------------------
Public Function FindTermInMacros(sSearchTerm As String)
On Error GoTo Error_Handler
Dim oFrm As Object
Dim frm As Access.Form
Dim oRpt As Object
Dim rpt As Access.Report
Dim ctl As Access.Control
Dim oMcr As Object
Dim prp As DAO.Property
Dim sFile As String
Dim sMcr As String
Dim intChannel As Integer
Dim sLine As String
Access.Application.Echo False
Debug.Print "Search Results for the Term '" & sSearchTerm & "'"
Debug.Print "Object Type", "Object Name", "Control Name", "Event Name"
Debug.Print String(80, "-")
'Search Forms
For Each oFrm In Application.CurrentProject.AllForms
DoCmd.OpenForm oFrm.Name, acDesign
Set frm = Forms(oFrm.Name).Form
With frm
'Form Properties
For Each prp In .Properties
If InStr(prp.Name, "EmMacro") > 0 Then
If Len(prp.value) > 0 Then
'Search for the Search Term we are looking for
If InStr(prp.value, sSearchTerm) > 0 Then
Debug.Print "Form:", frm.Name, , Replace(prp.Name, "EmMacro", "") ', prp.Value
End If
End If
End If
Next prp
'Form Control Properties
For Each ctl In frm.Controls
For Each prp In ctl.Properties
If InStr(prp.Name, "EMMacro") > 0 Then
If Len(prp.value) > 0 Then
If InStr(prp.value, sSearchTerm) > 0 Then
Debug.Print "Form", frm.Name, ctl.Name, Replace(prp.Name, "EmMacro", "")
End If
End If
End If
Next prp
Next ctl
End With
DoCmd.Close acForm, oFrm.Name, acSaveNo
Next oFrm
'Search Reports
For Each oRpt In Application.CurrentProject.AllReports
DoCmd.OpenReport oRpt.Name, acDesign
Set rpt = Reports(oRpt.Name).Report
With rpt
'Report Properties
For Each prp In .Properties
If InStr(prp.Name, "EmMacro") > 0 Then
If Len(prp.value) > 0 Then
'Search for the Search Term we are looking for
If InStr(prp.value, sSearchTerm) > 0 Then
Debug.Print "Report:", rpt.Name, , Replace(prp.Name, "EmMacro", "") ', prp.Value
End If
End If
End If
Next prp
'Report Control Properties
For Each ctl In rpt.Controls
For Each prp In ctl.Properties
If InStr(prp.Name, "EMMacro") > 0 Then
If Len(prp.value) > 0 Then
If InStr(prp.value, sSearchTerm) > 0 Then
Debug.Print "Form", frm.Name, ctl.Name, Replace(prp.Name, "EmMacro", "")
End If
End If
End If
Next prp
Next ctl
End With
DoCmd.Close acReport, oRpt.Name, acSaveNo
Next oRpt
'Search Standard Macros
'There appears to be no way to simply read/access a macro's commands through VBA, so
'we have to export the object to a text file and then read and search the resulting
'file. It's just the way it is, thank MS for not giving us any mean to interact with
'macros!
For Each oMcr In Application.CurrentProject.AllMacros
sFile = Access.Application.CurrentProject.path & "\Macro_" & oMcr.Name & ".txt"
'Export the Macro to a Text file so we can review it
Access.Application.SaveAsText acMacro, oMcr.Name, sFile
'Read the text file
sMcr = ""
intChannel = FreeFile
Open sFile For Input Access Read As #intChannel
Do Until EOF(intChannel)
Line Input #intChannel, sLine
If Trim(sLine) Like "Comment =""_AXL: 0 Then
Debug.Print "Macro:", oMcr.Name
End If
Next oMcr
Debug.Print String(80, "-")
Debug.Print "Search Completed"
Error_Handler_Exit:
On Error Resume Next
Access.Application.Echo True
If Not oMcr Is Nothing Then Set oMcr = Nothing
If Not prp Is Nothing Then Set prp = Nothing
If Not ctl Is Nothing Then Set ctl = Nothing
If Not rpt Is Nothing Then Set rpt = Nothing
If Not oRpt Is Nothing Then Set oRpt = Nothing
If Not frm Is Nothing Then Set frm = Nothing
If Not oFrm Is Nothing Then Set oFrm = Nothing
Exit Function
Error_Handler:
MsgBox "The following error has occured" & vbCrLf & vbCrLf & _
"Error Number: " & Err.Number & vbCrLf & _
"Error Source: FindTermInMacros" & vbCrLf & _
"Error Description: " & Err.Description & _
Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
, vbOKOnly + vbCritical, "An Error has Occured!"
Resume Error_Handler_Exit
End Function
MS Access Reserved Word Checker
If you aren’t already aware of it, there are a set of words which should never be used to name
- Database objects (Tables, Queries, Forms, …)
- Table/Query Fields
- Form/Report Controls
- VBA procedure names, variable, …
these are referred to as Reserved Words and thus Reserved Words need to be avoided at all cost as they can generate strange behaviors or outright failure of your database.
The issue with Reserved Words is that there is simply no way to remember them all. Furthermore, when taking over another developer’s work, it is next to impossible to review every object, control, … for such terms.
As such, I decided to try and automate the verification process to speed up and guarantee the results. Hence was born the MS Access Reserved Word Checker. A very simple tool that
searches through:
- Tables and their Fields
- Queries and their Fields
- Forms and their Controls
- Reports and their Controls
- Macros
looking for the use of:
- Reserved Words
- Special Characters
in their names.
The results are then tabulated in a form for review
Disclaimer/Notes:
If you do not have Microsoft Access, simply download and install the freely available runtime version (this permits running MS Access databases, but not modifying their design):
MS Access 2007 Runtime
MS Access 2010 Runtime
Ms Access 2013 Runtime
Ms Access 2016 Runtime
Although we have taken steps to ensure that the software on this site is free of viruses, we provide the software 'AS IS' and we recommend you take the time to scan it again.
In no event will Devhut.net or CARDA Consultants Inc. be liable to the client/end-user or any third party for any damages, including any lost profits, lost savings or other incidental, consequential or special damages arising out of the operation of or inability to operate the software which CARDA Consultants Inc. has provided, even if CARDA Consultants Inc. has been advised of the possibility of such damages.
Download a Demo Database
Feel free to download a demo copy by using the link provided below:
Version History
V1.001 (2017-06-22)
- Added verification of Queries and Query Field Names
V1.000 (2017-06-21)
- Initial release
MS Access Security Demo Database
Security and Access, where to start? What to do?
Security can be simple and complicated, it all depends on your needs and abilities. It can be superficial, as it can be very granular. As such, I thought I’d present a couple options to help people get going in the right direction.
What does this demo illustrate?
In the demo, I demonstrate 3 different techniques:
The Static Password Approach
- How to password protect a single command button using a hard-code VBA password
The Dynamic Automatic Authentication
This use a set of security tables that you setup once and then the database can automatically authenticate and apply security as required. Although this example demonstrates restricting access to a command button, it can be used to control access to forms, reports, …
This is by far the preferred approach and although requires a little more initial setup time, in the long run it will drastically simplify your work.
- How to restrict executing a command button’s code
- How to restrict access (visibility) of a command button
Disclaimer/Notes:
If you do not have Microsoft Access, simply download and install the freely available runtime version (this permits running MS Access databases, but not modifying their design):MS Access 2007 Runtime
MS Access 2010 Runtime
Ms Access 2013 Runtime
Ms Access 2016 Runtime
In no event will Devhut.net or CARDA Consultants Inc. be liable to the client/end-user or any third party for any damages, including any lost profits, lost savings or other incidental, consequential or special damages arising out of the operation of or inability to operate the software which CARDA Consultants Inc. has provided, even if CARDA Consultants Inc. has been advised of the possibility of such damages.
Download a Demo Database
Feel free to download a demo copy by using the link provided below:
Version History
V1.000 (2017-07-10)
Initial release
Access Calculated Control Blank/Empty/Null
Now the following is for a very niche market, but I thought I’d share none the less.
Access 2010 (although I seen reports in other versions such as Access 2013) had a major issue in which calculated control would not display their content. Yet, if you clicked on the control, it would then display the proper value. Now Microsoft eventually remedied the issue by issuing an update (I believe https://support.microsoft.com/en-us/help/2827138/access-encounters-multiple-issues-if-the-pc-has-been-running-more-than). So there is a real solution available, which is to update your installation.
However, I had a client, whose IT Dept. would not, will not, install the updates and I left me with no choice but to come up with a workaround of my own. As such, I created a routine that simply goes through a form and sets the focus on each control, one by one. It’s not ideal, but it works.
'---------------------------------------------------------------------------------------
' Procedure : Frm_TouchMe
' Author : Daniel Pineault, CARDA Consultants Inc.
' Website : http://www.cardaconsultants.com
' Purpose : Goes through a form and sets the focus on each control, one by one
' Copyright : The following may be altered and reused as you wish so long as the
' copyright notice is left unchanged (including Author, Website and
' Copyright). It may not be sold/resold or reposted on other sites (links
' back to this site are allowed).
' Req'd Refs: Uses Late Binding, so none required
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' frm : Form object to go through
'
' Usage:
' ~~~~~~
' Call Frm_TouchMe(Me)
' Call Frm_TouchMe(Me.frm_Orders.Form)
'
' Revision History:
' Rev Date(yyyy/mm/dd) Description
' **************************************************************************************
' 1 2017-08-18 Initial Release
'---------------------------------------------------------------------------------------
Public Sub Frm_TouchMe(frm As Access.Form)
10 On Error GoTo Error_Handler
Dim ctl As Access.Control
20 With frm.Recordset
30 If .RecordCount <> 0 Then
40 .MoveFirst
50 Do While Not .EOF
60 For Each ctl In frm.Controls
70 If ctl.ControlType = acCheckBox Or ctl.ControlType = acComboBox Or ctl.ControlType = acTextBox Then
80 If ctl.Enabled = True And ctl.Visible = True Then
90 If frm.DefaultView = 2 Then 'Datasheet
100 If ctl.ColumnHidden = False Then
110 ctl.SetFocus
120 End If
130 Else
140 ctl.SetFocus
150 End If
160 End If
170 End If
180 DoEvents 'This is necessary otherwise the results were not always consistent
190 Next ctl
200 .MoveNext
210 Loop
220 .MoveFirst
230 End If
240 End With
Error_Handler_Exit:
250 On Error Resume Next
260 If Not ctl Is Nothing Then Set ctl = Nothing
270 If Not frm Is Nothing Then Set frm = Nothing
280 Exit Sub
Error_Handler:
290 MsgBox "The following error has occured" & vbCrLf & vbCrLf & _
"Error Number: " & Err.Number & vbCrLf & _
"Error Source: Frm_TouchMe" & vbCrLf & _
"Error Description: " & Err.Description & _
Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
, vbOKOnly + vbCritical, "An Error has Occured!"
300 Resume Error_Handler_Exit
End Sub
In my case, the forms that were impacted by this issue were subforms, so I created a Load event in the parent forms and simply called the function by doing something like:
Call Frm_TouchMe(Me.frm_Orders.Form)
Et voilà! The problem was no more.
The proper resolution remains to update Office!
Special Characters and Internationalization of an Application
The Problem
If ever you get into developing applications, especially Access databases, that are deployed on various countries and/or where users change the default Regional settings you will quickly ask yourself why, oh why, did Microsoft make things so complex.
For example creating a value list through VBA, some languages use a comma (,), others the semicolon (;) and so on, and so on it goes for decimal separators, date separators, …
So hard coding the separator is a no-no unless you are in a very controlled environment!
Let’s look at a simple example, such as getting a listing of all the tables within a database to be used as a Value list of a combo box. Normally, our code would look something like:
'---------------------------------------------------------------------------------------
' Procedure : listTables
' Author : Daniel Pineault, CARDA Consultants Inc.
' Website : http://www.cardaconsultants.com
' Purpose : Generate a separated listing of all the database tables to be used
' as a value list for instance
' Copyright : The following may be altered and reused as you wish so long as the
' copyright notice is left unchanged (including Author, Website and
' Copyright). It may not be sold/resold or reposted on other sites (links
' back to this site are allowed).
' Req'd Refs: Uses Late Binding, so none required
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' bShowSys : True/False -> Include system tables or not
'
' Usage:
' ~~~~~~
' sTbls = listTables(True)
' sTbls = listTables(False)
' Me.cboName.RowSource = listTables(True)
'
' Revision History:
' Rev Date(yyyy/mm/dd) Description
' **************************************************************************************
' 1 2010-06-10 Initial Release
'---------------------------------------------------------------------------------------
Function listTables(bShowSys As Boolean) As String
On Error GoTo Error_Handler
Dim db As DAO.Database
Dim td As DAO.TableDefs
Set db = CurrentDb()
Set td = db.TableDefs
For Each t In td 'loop through all the fields of the tables
If Left(t.Name, 4) = "MSys" And bShowSys = False Then GoTo NextTable
listTables = listTables & t.Name & ","
NextTable:
Next
'Truncate the trailing comma on the last entry
If Len(listTables) > 0 Then listTables = Left(listTables, Len(listTables) - 1)
Error_Handler_Exit:
On Error Resume Next
If Not td Is Nothing Then Set td = Nothing
If Not db Is Nothing Then Set db = Nothing
Exit Function
Error_Handler:
MsgBox "The following error has occured" & vbCrLf & vbCrLf & _
"Error Number: " & Err.Number & vbCrLf & _
"Error Source: listTables" & vbCrLf & _
"Error Description: " & Err.Description & _
Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
, vbOKOnly + vbCritical, "An Error has Occured!"
Resume Error_Handler_Exit
End Function
The issue with the above is the comma in the following line of code because that may not be the list separator defined in the user’s computer settings.
listTables = listTables & t.Name & ","
Don’t get me wrong, the VBA code will run just fine. It is when you go to assign the string to a RowSource (…) that you will face problems.
So what are we to do? What’s the Solution
In Excel, they have a very simple command to determine the values of such separators
Application.International(xlListSeparator)
but there is nothing like that in Access! (banging my head against the wall. Microsoft’s standardization between applications is mind blowing!!!!)
Well, the fact is there are a couple approaches we could take: API, Registry, …
API
Declare Function GetLocaleInfo Lib "kernel32" Alias _
"GetLocaleInfoA" (ByVal Locale As Long, ByVal LCType As Long, _
ByVal lpLCData As String, ByVal cchData As Long) As Long
Declare Function GetUserDefaultLCID% Lib "kernel32" ()
Public Const LOCALE_SLIST = &HC
Public Function GetListSeparator() As String
On Error GoTo Error_Handler
Dim ListSeparator As String
Dim iRetVal1 As Long
Dim iRetVal2 As Long
Dim lpLCDataVar As String
Dim Position As Integer
Dim Locale As Long
Locale = GetUserDefaultLCID()
iRetVal1 = GetLocaleInfo(Locale, LOCALE_SLIST, lpLCDataVar, 0)
ListSeparator = String$(iRetVal1, 0)
iRetVal2 = GetLocaleInfo(Locale, LOCALE_SLIST, ListSeparator, iRetVal1)
Position = InStr(ListSeparator, Chr$(0))
If Position > 0 Then
ListSeparator = Left$(ListSeparator, Position - 1)
GetListSeparator = ListSeparator
End If
Error_Handler_Exit:
On Error Resume Next
Exit Function
Error_Handler:
MsgBox "The following error has occured" & vbCrLf & vbCrLf & _
"Error Number: " & Err.Number & vbCrLf & _
"Error Source: GetListSeparator" & vbCrLf & _
"Error Description: " & Err.Description & _
Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
, vbOKOnly + vbCritical, "An Error has Occured!"
Resume Error_Handler_Exit
End Function
Registry
All of these ‘mysterious’ settings are actually neatly stored in the registry and are easily accessible in the HKEY_CURRENT_USER\Control Panel\International key.
And with the following single line of code we can easily read any one of them by simply changing the key to read
CreateObject("WScript.Shell").RegRead("HKEY_CURRENT_USER\Control Panel\International\sList")
As always though, we should turn into a properly reusable function like
'---------------------------------------------------------------------------------------
' Procedure : GetInternationalSetting
' Author : Daniel Pineault, CARDA Consultants Inc.
' Website : http://www.cardaconsultants.com
' Purpose : Retrieve the system's regional setting values for internaltional chrs
' Copyright : The following may be altered and reused as you wish so long as the
' copyright notice is left unchanged (including Author, Website and
' Copyright). It may not be sold/resold or reposted on other sites (links
' back to this site are allowed).
' Req'd Refs: Uses Late Binding, so none required
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sKey : The registry key to read (sList, sDecimal, sDate, sThousand, ...)
'
' Usage:
' ~~~~~~
' GetInternationalSetting("sDecimal")
' GetInternationalSetting("sList")
'
' Revision History:
' Rev Date(yyyy/mm/dd) Description
' **************************************************************************************
' 1 2017-09-01 Initial Release
'---------------------------------------------------------------------------------------
Function GetInternationalSetting(ByVal sKey As String) As String
On Error GoTo Error_Handler
GetInternationalSetting = CreateObject("WScript.Shell").RegRead("HKEY_CURRENT_USER\Control Panel\International\" & sKey)
Error_Handler_Exit:
On Error Resume Next
Exit Function
Error_Handler:
MsgBox "The following error has occured" & vbCrLf & vbCrLf & _
"Error Number: " & Err.Number & vbCrLf & _
"Error Source: GetInternationalSetting" & vbCrLf & _
"Error Description: " & Err.Description & _
Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
, vbOKOnly + vbCritical, "An Error has Occured!"
Resume Error_Handler_Exit
End Function
With this new information in hand, we can then adapt our original table function so it become flexible for all PC’s and regions of the world by doing
Using the API
'---------------------------------------------------------------------------------------
' Procedure : listTables
' Author : Daniel Pineault, CARDA Consultants Inc.
' Website : http://www.cardaconsultants.com
' Purpose : Generate a separated listing of all the database tables to be used
' as a value list for instance
' Copyright : The following may be altered and reused as you wish so long as the
' copyright notice is left unchanged (including Author, Website and
' Copyright). It may not be sold/resold or reposted on other sites (links
' back to this site are allowed).
' Req'd Refs: Uses Late Binding, so none required
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' bShowSys : True/False -> Include system tables or not
'
' Usage:
' ~~~~~~
' sTbls = listTables(True)
' sTbls = listTables(False)
' Me.cboName.RowSource = listTables(True)
'
' Revision History:
' Rev Date(yyyy/mm/dd) Description
' **************************************************************************************
' 1 2010-06-10 Initial Release
' 2 2017-09-01 Internationalized version
'---------------------------------------------------------------------------------------
Function listTables(bShowSys As Boolean) As String
On Error GoTo Error_Handler
Dim db As DAO.Database
Dim td As DAO.TableDefs
Dim sDelim As String
sDelim = GetListSeparator
Set db = CurrentDb()
Set td = db.TableDefs
For Each t In td 'loop through all the fields of the tables
If Left(t.Name, 4) = "MSys" And bShowSys = False Then GoTo NextTable
listTables = listTables & t.Name & sDelim
NextTable:
Next
'Truncate the trailing comma on the last entry
If Len(listTables) > 0 Then listTables = Left(listTables, Len(listTables) - Len(sDelim))
Error_Handler_Exit:
On Error Resume Next
If Not td Is Nothing Then Set td = Nothing
If Not db Is Nothing Then Set db = Nothing
Exit Function
Error_Handler:
MsgBox "The following error has occured" & vbCrLf & vbCrLf & _
"Error Number: " & Err.Number & vbCrLf & _
"Error Source: listTables" & vbCrLf & _
"Error Description: " & Err.Description & _
Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
, vbOKOnly + vbCritical, "An Error has Occured!"
Resume Error_Handler_Exit
End Function
Using the function
'---------------------------------------------------------------------------------------
' Procedure : listTables
' Author : Daniel Pineault, CARDA Consultants Inc.
' Website : http://www.cardaconsultants.com
' Purpose : Generate a separated listing of all the database tables to be used
' as a value list for instance
' Copyright : The following may be altered and reused as you wish so long as the
' copyright notice is left unchanged (including Author, Website and
' Copyright). It may not be sold/resold or reposted on other sites (links
' back to this site are allowed).
' Req'd Refs: Uses Late Binding, so none required
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' bShowSys : True/False -> Include system tables or not
'
' Usage:
' ~~~~~~
' sTbls = listTables(True)
' sTbls = listTables(False)
' Me.cboName.RowSource = listTables(True)
'
' Revision History:
' Rev Date(yyyy/mm/dd) Description
' **************************************************************************************
' 1 2010-06-10 Initial Release
' 2 2017-09-01 Internationalized version
'---------------------------------------------------------------------------------------
Function listTables(bShowSys As Boolean) As String
On Error GoTo Error_Handler
Dim db As DAO.Database
Dim td As DAO.TableDefs
Dim sDelim As String
sDelim = GetInternationalSetting("sList")
Set db = CurrentDb()
Set td = db.TableDefs
For Each t In td 'loop through all the fields of the tables
If Left(t.Name, 4) = "MSys" And bShowSys = False Then GoTo NextTable
listTables = listTables & t.Name & sDelim
NextTable:
Next
'Truncate the trailing comma on the last entry
If Len(listTables) > 0 Then listTables = Left(listTables, Len(listTables) - Len(sDelim))
Error_Handler_Exit:
On Error Resume Next
If Not td Is Nothing Then Set td = Nothing
If Not db Is Nothing Then Set db = Nothing
Exit Function
Error_Handler:
MsgBox "The following error has occured" & vbCrLf & vbCrLf & _
"Error Number: " & Err.Number & vbCrLf & _
"Error Source: listTables" & vbCrLf & _
"Error Description: " & Err.Description & _
Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
, vbOKOnly + vbCritical, "An Error has Occured!"
Resume Error_Handler_Exit
End Function
Of course, then we’d need to perform the same operation through our code that builds lists, equations, … everywhere regional setting characters are used! This is when defining these regional settings as TempVars at the startup of your database makes a lot of sense and modifying function to accept the sDelim as an input variable can greatly simplify your coding life.
Another Approach
Instead of embedding the internationalization code within each function, another approach would be to leave your tried and tested functions alone, and simply use the Replace function when calling them. So you’d take something like
Me.cboName.RowSource = Replace(listTables(True), ",", GetInternationalSetting("sList"))
This way you don’t need to change any of your existing procedures. On the other hand, you need to always remember to add the Replace function to every call. There can also be issues with this approach should the string you are performing a replace on include any comma beyond those used as a list separator.
The choice is yours depending on your needs.
What’s also great with the above API or Function is that it will work in every VBA application! No more need to know in Excel you use this command, … now with one solution you can rule them all!
Useful Links
Retrieve Locale Info
Handle currency, date, and time formats for other locales
Book Internationalization with Visual Basic
Naming Conventions
What is a Naming Convention?
A Naming Convention is simply a standard rule (could be through the use of prefixes, suffixes, typographical convention (syntax styling) such as CamelCase, …) that you apply when creating names for :
- Objects
- Tables
- Queries
- Forms
- Reports
- Macros
- Modules (See VBA Components)
- Table Fields
- Controls (Forms & Reports)
- VBA Components (Modules, Class Modules) & Variables…
Why Use a Naming Convention?
Simple, to standardize your work and make it easier to follow.
For instance, take a simple example of
Public Sub Demo()
For cars = 0 To 10
Accident = DateAdd("d", cars, Date)
Debug.Print Accident
Next cars
End Sub
what is Accident and cars? Strings, Integer, Long, …?
Now, if you implement a basic naming convention and prefix all your VBA date variables with dt for Dates and i for Interger numbers you get
Public Sub Demo()
For icars = 0 To 10
dtAccident = DateAdd("d", icars, Date)
Debug.Print dtAccident
Next icars
End Sub
and the code becomes much more legible. You don’t even need to look at the procedure’s variable declarations to understand that dtAccident is a Date and icars is a Integer number.
So by using naming conventions you
- Standardize your work
- Simplify reading your code (imagine long procedures where you need to scroll up and down to see variable declarations, by using a naming convention, you no longer need to look at the declaration to know exactly what type of variable you are working with)
- Simplify troubleshooting
- Aid in finding things when they are listed together (imaging a list of database Objects with object prefixes, it becomes very easy to distinguish forms from reports and tables form queries, …)
What are Some of the Existing Naming Conventions?
If you Google the subject of Naming Conventions you will be inundated with all sorts of conventions. In Access, or VBA in general, you will commonly hear of the following ones:
- theAccessWeb’s http://access.mvps.org/access/general/gen0012.htm
- Hungarian notation: https://support.microsoft.com/en-us/help/17…ventions-for-vb
- Reddick VBA (RVBA) Naming Convention: http://www.xoc.net/downloads/rvbanc.pdf
- Leszynski naming convention: https://en.wikipedia.org/wiki/Leszynski_naming_convention
You’ll notice that Hungarian, RVBA and Leszynski are all very similar as they are derivative of the Hungarian.
Let’s Not Get Hung Up on Specifics!
Now, I know some people will be sticklers for one notation or another, but the truth of the matter is that it truly doesn’t make any difference whatsoever! The name of the game here is consistency!
What is important is that you have some standard and you stick with it throughout any given project.
- You can use your own notation. There’s nothing wrong with that.
- You can change notations from project to project (just don’t change notations within a single project). Once you start with one, stick with it, or if you decide to change notations, update it throughout your project.
More resources on the subject
MS Access – Automatic Backup of an Access Database
Another common question in Access forums is
I would like to know if there is a way I can automatically backup my database
There are various approaches that can be implemented depending on your needs and/or setup.
IT Department
The easiest solution in a corporate environment is to simply ask the IT department to include your database’s folder as part of their automated backups.
PROs
- One less thing for you to setup and administer
- Is done using enterprise level backup software
CONs
- Makes you reliant on the IT Department.
- If you have a responsive IT department this isn’t an issue, but if your IT department is unresponsive, this can be very problematic when you need to test the backup system, or god forbid, when you actually need to perform a recovery.
Windows Scheduled Task
You yourself could create a scheduled task in Windows to run a VBScript that would perform a backup.
PROs
- You don’t need to rely on another department (You’re in control)
- You don’t need to get any permissions/approvals …
CONs
- One more thing to setup and administer (more work for you)
Database Startup
You could setup your database, at startup, to check if a backup was done, and if not do so before continuing to load up.
PROs
- You don’t need to rely on another department (You’re in control)
- You don’t need to get any permissions/approvals …
- You don’t need to even learn about setting up schedule tasks
- You can easily adding logging and e-mail notifications as part of the backup process and create a graphical interface to reviewing the backups
CONs
- One more thing to setup and administer (more work for you)
What Should be Backed Up
Obviously, everything should be backed up initially, at least once, but after the initial backup, there is no need to backup the database Front-End, and/or other static files. Furthermore, as you release updates to your front-end or other files, you are going to have them backed up again so you always have a copy of the every version.
What is critical to backup on a regular basis in the database Back-End (the data)
Backup Validation
It is critical to regularly validate that the backup process executed, but also, to actually validate the backup files themselves. I mean you need to actually take a backup, perform a recovery and open the database files to ensure the file/data is sound.
Don’t laugh, I was brought into a company who hard years of backups, but they turned out to all be corrupted, so in fact they had no valid backups and no one had ever checked prior to actually needing them. At that point it was too late and they lost their entire database!
Why Back Up Your Database
I’ve seen people over the years say they don’t need to worry about performing backups. This is simply ludicrous! There are a multitude of reasons why backing up your files, little alone your database (your data) is critical. Things like needing to recover from
- Accidental development errors
- Accidental file deletion
- Corruption
- Computer/hard drive crashes
In 2018, backups are as critical as the development itself. After sending typically hundreds of hours, thousands of dollars, developing a database, does it not make sense to protect it!
Premade Solution
If you’re looking for a solution that you can simply drop into place, you may like to take a look at MS Access Auto Backup
MS Access – Hide the Ribbon
One more of these common questions in the forums is how to hide the Access application ribbon completely. As per the usual with Access, this can be accomplished a few different ways:
- Using VBA
- Using Ribbon XML
Using VBA
The simplest and easiest way to hide the ribbon completely from the user is to use VBA. In a single line of code (as shown below) it can be removed.
DoCmd.ShowToolbar "Ribbon", acToolbarNo
and display the ribbon again you simply do
DoCmd.ShowToolbar "Ribbon", acToolbarYes
Using Ribbon XML
Sometimes however, it is nice to have much more control over the ribbon and this is where using custom ribbon XML can become very helpful. Now Ribbon XML can be setup in various ways (USysRibbons table, User-defined table, XML File, straight from code, …).
Personally, I find using the USysRibbons table the easiest and now include it in all my databases. Then I include a simple ribbon who’s startFromScratch is set to true and has no content (so a blank, non-existent ribbon).
So I normally just import the table from a previous project as quite often the ribbons are useful, but to start a new USysRibbons table from scratch you can simply run the following query
CREATE TABLE USysRibbons (
ID COUNTER,
RibbonName TEXT(255),
RibbonXML MEMO
);
Then, you can make a new entry in the table. Provide a RibbonName, I keep it simple and use Blank_Ribbon and then use the following RibbonXML
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui"> <ribbon startFromScratch="true"> </ribbon> </customUI>
Then it is simply a question of applying it globally to the database File -> Options -> Current Database -> Ribbon and Toolbar Options -> Ribbon Name and selecting the name of the Ribbon we just created.
The next time you open your database, the ribbon will be gone. The only way to get it back is to use the shift bypass to regain access to the File menu to be able to change the property.
Now your probably saying to yourself “that a lot of work when I could achieve the same thing with a single line of VBA code!?” Yes, it is, but the difference is that I can then easily add other custom ribbons and apply them to individual database objects (printing ribbon for reports, data export ribbon for certain forms, …) now that I have the framework setup.
So it comes down to your needs. Are you simply hiding the ribbon and that’s it or are you looking to get into ribbon customization and give your users a custom experience.
MS Access – Determine Build Number
With the never ending updates for Office 365/2016 it has become necessary to be able to determine what build number you, your users, are running to be able to determine if you have all the latest updates installed, and/or determine if you have a known bugged update installed… so you can determine if you need to update/revert your installation.
As per most things, it isn’t really hard to locate this information once you’ve done so once.
How to Retrieve the Version and Build Information of Your Access 365/2016 Installation
- Click on the File tab
- Click on Account on the right-hand side menu. Then look for the version and build information under the About Access heading on the right-side.
As I said, it’s not a question of being hard, it’s a question of being aware where Microsoft moved such information to. Now you know!
MS Access – High DPI issues
With the higher and higher screen resolutions we are starting to see issues with Office application and High DPI displays. For instance, if you look at the following Answers forum thread
Has anybody at Microsoft ever edited an Access Form Chart control on a zoomed high DPI display?
you can see a few screenshots of just how useable Access can become in a High DPI environment. Now, I mention Access, but this can be an issue with pretty much any application (Excel, Word, Outlook, …) and the fix elaborated below is applicable to any of them as well.
The Fix
There is a little known fix to this issue, but as per the usual it isn’t well known and is left to the user to implement!
- Close any open instances of Access (or whatever program you are trying to fix)
- Open Windows Explorer and navigate to the program exe (so for Access you need to locate the msaccess.exe file) which is typically found in
- C:\Program Files (x86)\Microsoft Office\Office15 (replace the 15 with whatever version you are running)
- Then right-click on the file itself and select Properties.
- Click on the Compatibility tab
- Enable the Disable display scaling on high DPI settings property by clicking on the checkbox.
- Click OK, close Windows Explorer and start Access and all should be good.
Now, in the case of Access, since it incorporates other programs within it, you also need to apply the same change to them. So be sure to repeat the above steps to GRAPH.EXE so any charts/graphs display properly for you!
We can only hope that Microsoft will make such manual tweaks automated in the near future so that an application automatically adjusts and display in an optimal manner depending on its environment.
Access – Unlocking an Access VBA Project
I recently needed to unlock an Access VBA Project for which my client had lost the password. I was surprised to find that there was an incredibly simple hack that can unlock any Access password protected VBA project and I figured I’d share in case it could help others.
Make a copy of the database in question and only work with the copy. Never work with the original database file just in case anything goes wrong.
Open the database file in any HEX Editor of your choosing.
Find any and all occurrences of the term DPB= and change the B to another value (many tutorials suggest x, but it doesn’t seem to truly make any difference)
Once you have replaced all the occurrences, perform a Save As to save these modification under a new file.
Open the newly saved file in Access (like you would normally) and then go into the VBA editor. You will receive several error messages, simply dismiss them all (and yes, there can be quite a few!).
Open the Database Properties which will now have no password specified.
Enter a new password and confirm it to resecure the VBA Project with a known password.
Save the VBA Project
Close the database
That’s it, everything should now be in order. Use your database as you normally do.
I’m also hoping that by shedding some light on this issue perhaps Microsoft will make the necessary changes to start protecting people’s information properly. This is completely unacceptable in 2018 IMHO.
Access – Working with DBase dbf Files With Names That Are More Than 8 Characters
Have you ever needed to Import or Link a DBase DBF file in an Access? If your DBF filename is longer than 8 characters you’ll be in for a nice treat!
and once you click OK, you’ll get a nice error!!!
So, it would seem that Access can’t handle dbf files with filenames that are more than 8 characters long. We’re living back in the days of DOS.
Funny enough, it’s actually that very thought, that gave me the solution to this issue!
Back in the days of DOS, you couldn’t have these super long file names like we do today and the format is now known a ShortFileName and there’s a simple API that enables us to convert a LongFileName into the old ShortFileName format. I wondered if by simply converting formats Access might be happy once again and perform the Import/Link.
Public Declare Function GetShortPathName Lib "kernel32" _
Alias "GetShortPathNameA" _
(ByVal lpszLongPath As String, _
ByVal lpszShortPath As String, _
ByVal cchBuffer As Long) As Long
'---------------------------------------------------------------------------------------
' Procedure : GetShortName
' Author : Original author is unknown
' Modified Daniel Pineault, CARDA Consultants Inc.
' Website : http://www.cardaconsultants.com
' Purpose : Convert a LongFileName to a ShortFileName (think DOS!)
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
' (CC BY-SA 4.0) - https://creativecommons.org/licenses/by-sa/4.0/
' Req'd Refs: None required, Req GetShortPathName Declaration
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sFile : Path and filename including extension to convert format
'
' Usage:
' ~~~~~~
' GetShortName("C:\Users\Daniel\Desktop\XBase - dBase sample data.dbf")
' Returns -> C:\Users\Daniel\Desktop\XBASE-~1.DBF
'
' Revision History:
' Rev Date(yyyy/mm/dd) Description
' **************************************************************************************
' 1 Unknown Initial Release
' 2 2018-05-17 Error handling, ...
'---------------------------------------------------------------------------------------
Public Function GetShortName(sFile As String) As String
On Error GoTo Error_Handler
Dim sShortFile As String * 67
Dim lResult As Long
'Make a call to the GetShortPathName API
lResult = GetShortPathName(sFile, sShortFile, _
Len(sShortFile))
'Trim out unused characters from the string.
GetShortName = Left$(sShortFile, lResult)
Error_Handler_Exit:
On Error Resume Next
Exit Function
Error_Handler:
MsgBox "The following error has occured" & vbCrLf & vbCrLf & _
"Error Number: " & Err.Number & vbCrLf & _
"Error Source: GetShortName" & vbCrLf & _
"Error Description: " & Err.Description & _
Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
, vbOKOnly + vbCritical, "An Error has Occured!"
Resume Error_Handler_Exit
End Function
So after converting the original path
C:\Users\Daniel\Desktop\XBase – dBase sample data.dbf
We get
C:\Users\Daniel\Desktop\XBASE-~1.DBF
Now, we use this new file path/name as our File name (as shown below)
and surprise, surprise the Link/Import works as it should. So it would seem Access was simply feeling nostalgic!
I know this is far from ideal, but at least it works.
I have notified the Access Dev Team requesting this be added to their ‘To-Do’ list. There are no guarantees, but at least they were made aware and to me this seems like a very simple thing to address, so no real reason they shouldn’t be able to fix this.
After coming up with this workaround, I did some searching and discovered that this approach had been suggested by others (I guess great minds think alike). Moreover, there is a Uservoice suggestion entitles Allow Access to connect to a dBASE (.dbf) file with a name over 8 characters related to this very issue, so feel free to add your votes to show interest in resolving this bug (this isn’t a feature request as much as resolving a bug in the fundamental processing of the file).
Managing User-Level Security in Access 2007+
Here’s another question I see pop up from time to time in the Forums.
Since Microsoft did away with User-Level Security (ULS) in Access 2007+, people often wonder how they can manage ULS; add/remove users, change password, …?
The reality of the situation is that although the commands are not front and center, ie they aren’t listed in the Ribbon anywhere, they are still actually there. Just hidden!
So the question becomes, how can we access them?
Well, there are 2 ways to do this:
- Issue the VBA commands directly
- Customizing the Ribbon
Issue the VBA commands directly
You can access each of the 3 ULS dialogs by using the following 3 VBA commands.
User and Group Account
DoCmd.RunCommand acCmdUserAndGroupAccounts
User and Group Permissions
DoCmd.RunCommand acCmdUserAndGroupPermissions
User-Level Security Wizard
DoCmd.RunCommand acCmdUserLevelSecurityWizard
Customizing the Ribbon
Another options is to simply customize the ribbon.
- Right-Click on the Ribbon and Select Customize the Ribbon
- In the Choose commands from combo box, select All Commands
- In the command listbox, scroll down to the User and … commands
- Use the Add >> button to add the select command(s) to the Tab Group of your choosing or, as in my example, create a new Security Tab Group and add them there.
- You may also like to add the Set Logon Password command to your custom ribbon tab group for the sake of completeness.